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 
trueFalse(bool x)44 static QString trueFalse(bool x){
45 	if(!x)
46 		return QString("false");
47 	else
48 		return QString("true");
49 }
50 
yesNoTranslated(bool x)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 Matrix3D<int> subgroupsMatrix;
62 static Matrix3D<int> teachersMatrix;
63 
64 static int teachers_conflicts=-1;
65 static int subgroups_conflicts=-1;
66 
67 extern Matrix2D<bool> breakDayHour;
68 
69 extern Matrix3D<bool> teacherNotAvailableDayHour;
70 
71 extern Matrix3D<bool> subgroupNotAvailableDayHour;
72 
73 extern Matrix1D<int> activityTagN1N2N3;
74 
75 /////////////////////////////////////////////////////////////////////////////////////////////
76 /////////////////////////////////////////////////////////////////////////////////////////////
77 
getActivityDetailedDescription(Rules & r,int id)78 QString getActivityDetailedDescription(Rules& r, int id)
79 {
80 	QString s="";
81 
82 	Activity* act=r.activitiesPointerHash.value(id, nullptr);
83 	if(act==nullptr){
84 		s+=QCoreApplication::translate("Activity", "Invalid (inexistent) id for activity");
85 		return s;
86 	}
87 
88 	/*if(act->activityTagsNames.count()>0){
89 		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)."
90 		 "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(","));
91 	}
92 	else{
93 		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)."
94 		 "The abbreviations are: Teachers, Subject, Students. There are no activity tags here").arg(act->teachersNames.join(",")).arg(act->subjectName).arg(act->studentsNames.join(","));
95 	}
96 	return s;*/
97 
98 	const int INDENT=4;
99 
100 	bool _indent;
101 	if(act->isSplit() && act->id!=act->activityGroupId)
102 		_indent=true;
103 	else
104 		_indent=false;
105 
106 	bool indentRepr;
107 	if(act->isSplit() && act->id==act->activityGroupId)
108 		indentRepr=true;
109 	else
110 		indentRepr=false;
111 
112 	QString _teachers="";
113 	if(act->teachersNames.count()==0)
114 		_teachers=QCoreApplication::translate("Activity", "no teachers");
115 	else
116 		_teachers=act->teachersNames.join(",");
117 
118 	QString _subject=act->subjectName;
119 
120 	QString _activityTags=act->activityTagsNames.join(",");
121 
122 	QString _students="";
123 	if(act->studentsNames.count()==0)
124 		_students=QCoreApplication::translate("Activity", "no students");
125 	else
126 		_students=act->studentsNames.join(",");
127 
128 	/*QString _id;
129 	_id = CustomFETString::number(id);
130 
131 	QString _agid="";
132 	if(act->isSplit())
133 		_agid = CustomFETString::number(act->activityGroupId);*/
134 
135 	QString _duration=CustomFETString::number(act->duration);
136 
137 	QString _totalDuration="";
138 	if(act->isSplit())
139 		_totalDuration = CustomFETString::number(act->totalDuration);
140 
141 	QString _active;
142 	if(act->active==true)
143 		_active="";
144 	else
145 		_active="X";
146 
147 	QString _nstudents="";
148 	if(act->computeNTotalStudents==false)
149 		_nstudents=CustomFETString::number(act->nTotalStudents);
150 
151 	/////////
152 	//QString s="";
153 	if(_indent)
154 		s+=QString(INDENT, ' ');
155 
156 	/*s+=_id;
157 	s+=" - ";*/
158 
159 	if(_active!=""){
160 		s+=_active;
161 		s+=" - ";
162 	}
163 
164 	s+=_duration;
165 	if(act->isSplit()){
166 		s+="/";
167 		s+=_totalDuration;
168 	}
169 	s+=" - ";
170 
171 	if(indentRepr)
172 		s+=QString(INDENT, ' ');
173 
174 	s+=_teachers;
175 	s+=" - ";
176 	s+=_subject;
177 	s+=" - ";
178 	if(_activityTags!=""){
179 		s+=_activityTags;
180 		s+=" - ";
181 	}
182 	s+=_students;
183 
184 	if(_nstudents!=""){
185 		s+=" - ";
186 		s+=_nstudents;
187 	}
188 
189 	if(!act->comments.isEmpty()){
190 		s+=" - ";
191 		s+=act->comments;
192 	}
193 
194 	return s;
195 
196 }
197 
populateInternalSubgroupsList(const Rules & r,const StudentsSet * ss,QList<int> & iSubgroupsList)198 void populateInternalSubgroupsList(const Rules& r, const StudentsSet* ss, QList<int>& iSubgroupsList){
199 	iSubgroupsList.clear();
200 
201 	QSet<int> tmpSet;
202 
203 	if(ss->type==STUDENTS_SUBGROUP){
204 		int tmp;
205 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
206 		assert(tmp>=0);
207 		assert(tmp<r.nInternalSubgroups);
208 		if(!tmpSet.contains(tmp)){
209 			tmpSet.insert(tmp);
210 			iSubgroupsList.append(tmp);
211 		}
212 	}
213 	else if(ss->type==STUDENTS_GROUP){
214 		StudentsGroup* stg=(StudentsGroup*)ss;
215 		for(int i=0; i<stg->subgroupsList.size(); i++){
216 			StudentsSubgroup* sts=stg->subgroupsList[i];
217 			int tmp;
218 			tmp=sts->indexInInternalSubgroupsList;
219 			assert(tmp>=0);
220 			assert(tmp<r.nInternalSubgroups);
221 			if(!tmpSet.contains(tmp)){
222 				tmpSet.insert(tmp);
223 				iSubgroupsList.append(tmp);
224 			}
225 		}
226 	}
227 	else if(ss->type==STUDENTS_YEAR){
228 		StudentsYear* sty=(StudentsYear*)ss;
229 		for(int i=0; i<sty->groupsList.size(); i++){
230 			StudentsGroup* stg=sty->groupsList[i];
231 			for(int j=0; j<stg->subgroupsList.size(); j++){
232 				StudentsSubgroup* sts=stg->subgroupsList[j];
233 				int tmp;
234 				tmp=sts->indexInInternalSubgroupsList;
235 				assert(tmp>=0);
236 				assert(tmp<r.nInternalSubgroups);
237 				if(!tmpSet.contains(tmp)){
238 					tmpSet.insert(tmp);
239 					iSubgroupsList.append(tmp);
240 				}
241 			}
242 		}
243 	}
244 	else
245 		assert(0);
246 }
247 
TimeConstraint()248 TimeConstraint::TimeConstraint()
249 {
250 	type=CONSTRAINT_GENERIC_TIME;
251 
252 	active=true;
253 	comments=QString("");
254 }
255 
~TimeConstraint()256 TimeConstraint::~TimeConstraint()
257 {
258 }
259 
TimeConstraint(double wp)260 TimeConstraint::TimeConstraint(double wp)
261 {
262 	type=CONSTRAINT_GENERIC_TIME;
263 
264 	weightPercentage=wp;
265 	assert(wp<=100 && wp>=0);
266 
267 	active=true;
268 	comments=QString("");
269 }
270 
canBeUsedInOfficialMode()271 bool TimeConstraint::canBeUsedInOfficialMode()
272 {
273 	assert(type!=CONSTRAINT_GENERIC_TIME);
274 
275 	if(type==CONSTRAINT_BASIC_COMPULSORY_TIME ||
276 	 type==CONSTRAINT_BREAK_TIMES ||
277 	 type==CONSTRAINT_TEACHER_NOT_AVAILABLE_TIMES ||
278 	 type==CONSTRAINT_TEACHERS_MAX_HOURS_DAILY ||
279 	 type==CONSTRAINT_TEACHER_MAX_DAYS_PER_WEEK ||
280 	 type==CONSTRAINT_TEACHERS_MAX_GAPS_PER_WEEK ||
281 	 type==CONSTRAINT_TEACHER_MAX_GAPS_PER_WEEK ||
282 	 type==CONSTRAINT_TEACHER_MAX_HOURS_DAILY ||
283 	 type==CONSTRAINT_TEACHERS_MAX_HOURS_CONTINUOUSLY ||
284 	 type==CONSTRAINT_TEACHER_MAX_HOURS_CONTINUOUSLY ||
285 
286 	 type==CONSTRAINT_TEACHERS_MIN_HOURS_DAILY ||
287 	 type==CONSTRAINT_TEACHER_MIN_HOURS_DAILY ||
288 	 type==CONSTRAINT_TEACHERS_MAX_GAPS_PER_DAY ||
289 	 type==CONSTRAINT_TEACHER_MAX_GAPS_PER_DAY ||
290 
291 	 type==CONSTRAINT_STUDENTS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
292 	 type==CONSTRAINT_STUDENTS_SET_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
293 	 type==CONSTRAINT_STUDENTS_SET_NOT_AVAILABLE_TIMES ||
294 	 type==CONSTRAINT_STUDENTS_MAX_GAPS_PER_WEEK ||
295 	 type==CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_WEEK ||
296 
297 	 type==CONSTRAINT_STUDENTS_MAX_HOURS_DAILY ||
298 	 type==CONSTRAINT_STUDENTS_SET_MAX_HOURS_DAILY ||
299 	 type==CONSTRAINT_STUDENTS_MAX_HOURS_CONTINUOUSLY ||
300 	 type==CONSTRAINT_STUDENTS_SET_MAX_HOURS_CONTINUOUSLY ||
301 
302 	 type==CONSTRAINT_STUDENTS_MIN_HOURS_DAILY ||
303 	 type==CONSTRAINT_STUDENTS_SET_MIN_HOURS_DAILY ||
304 
305 	 type==CONSTRAINT_ACTIVITY_ENDS_STUDENTS_DAY ||
306 	 type==CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIME ||
307 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME ||
308 	 type==CONSTRAINT_ACTIVITIES_NOT_OVERLAPPING ||
309 	 type==CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES ||
310 	 type==CONSTRAINT_ACTIVITY_PREFERRED_TIME_SLOTS ||
311 	 type==CONSTRAINT_ACTIVITIES_PREFERRED_TIME_SLOTS ||
312 	 type==CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIMES ||
313 	 type==CONSTRAINT_ACTIVITIES_PREFERRED_STARTING_TIMES ||
314 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_HOUR ||
315 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_DAY ||
316 	 type==CONSTRAINT_TWO_ACTIVITIES_CONSECUTIVE ||
317 	 type==CONSTRAINT_TWO_ACTIVITIES_ORDERED ||
318 	 type==CONSTRAINT_MIN_GAPS_BETWEEN_ACTIVITIES ||
319 	 type==CONSTRAINT_SUBACTIVITIES_PREFERRED_TIME_SLOTS ||
320 	 type==CONSTRAINT_SUBACTIVITIES_PREFERRED_STARTING_TIMES ||
321 
322 	 type==CONSTRAINT_TEACHER_INTERVAL_MAX_DAYS_PER_WEEK ||
323 	 type==CONSTRAINT_TEACHERS_INTERVAL_MAX_DAYS_PER_WEEK ||
324 	 type==CONSTRAINT_STUDENTS_SET_INTERVAL_MAX_DAYS_PER_WEEK ||
325 	 type==CONSTRAINT_STUDENTS_INTERVAL_MAX_DAYS_PER_WEEK ||
326 
327 	 type==CONSTRAINT_ACTIVITIES_END_STUDENTS_DAY ||
328 
329 	 type==CONSTRAINT_TWO_ACTIVITIES_GROUPED ||
330 
331 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
332 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
333 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
334 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
335 
336 	 type==CONSTRAINT_TEACHERS_MAX_DAYS_PER_WEEK ||
337 
338 	 type==CONSTRAINT_THREE_ACTIVITIES_GROUPED ||
339 	 type==CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES ||
340 
341 	 type==CONSTRAINT_TEACHERS_MIN_DAYS_PER_WEEK ||
342 	 type==CONSTRAINT_TEACHER_MIN_DAYS_PER_WEEK ||
343 
344 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_DAILY ||
345 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_DAILY ||
346 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_DAILY ||
347 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_DAILY ||
348 
349 	 type==CONSTRAINT_STUDENTS_MAX_GAPS_PER_DAY ||
350 	 type==CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_DAY ||
351 
352 	 type==CONSTRAINT_ACTIVITIES_OCCUPY_MAX_TIME_SLOTS_FROM_SELECTION ||
353 	 type==CONSTRAINT_ACTIVITIES_MAX_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS ||
354 
355 	 type==CONSTRAINT_STUDENTS_SET_MAX_DAYS_PER_WEEK ||
356 	 type==CONSTRAINT_STUDENTS_MAX_DAYS_PER_WEEK ||
357 
358 	 //2017-02-06
359 	 type==CONSTRAINT_TEACHER_MAX_SPAN_PER_DAY ||
360 	 type==CONSTRAINT_TEACHERS_MAX_SPAN_PER_DAY ||
361 	 type==CONSTRAINT_TEACHER_MIN_RESTING_HOURS ||
362 	 type==CONSTRAINT_TEACHERS_MIN_RESTING_HOURS ||
363 	 type==CONSTRAINT_STUDENTS_SET_MAX_SPAN_PER_DAY ||
364 	 type==CONSTRAINT_STUDENTS_MAX_SPAN_PER_DAY ||
365 	 type==CONSTRAINT_STUDENTS_SET_MIN_RESTING_HOURS ||
366 	 type==CONSTRAINT_STUDENTS_MIN_RESTING_HOURS ||
367 
368 	 //2018-06-13
369 	 type==CONSTRAINT_TWO_ACTIVITIES_ORDERED_IF_SAME_DAY ||
370 
371 	 //2019-06-08
372 	 type==CONSTRAINT_STUDENTS_SET_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
373 	 type==CONSTRAINT_STUDENTS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
374 	 type==CONSTRAINT_TEACHER_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
375 	 type==CONSTRAINT_TEACHERS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
376 
377 	 type==CONSTRAINT_ACTIVITY_TAGS_NOT_OVERLAPPING ||
378 
379 	 type==CONSTRAINT_ACTIVITIES_OCCUPY_MIN_TIME_SLOTS_FROM_SELECTION ||
380 	 type==CONSTRAINT_ACTIVITIES_MIN_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS ||
381 
382 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MIN_HOURS_DAILY ||
383 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MIN_HOURS_DAILY ||
384 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MIN_HOURS_DAILY ||
385 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MIN_HOURS_DAILY ||
386 
387 	 type==CONSTRAINT_ACTIVITY_ENDS_TEACHERS_DAY ||
388 	 type==CONSTRAINT_ACTIVITIES_END_TEACHERS_DAY ||
389 
390 	 type==CONSTRAINT_TWO_SETS_OF_ACTIVITIES_ORDERED)
391 		return true;
392 
393 	return false;
394 }
395 
canBeUsedInMorningsAfternoonsMode()396 bool TimeConstraint::canBeUsedInMorningsAfternoonsMode()
397 {
398 	assert(type!=CONSTRAINT_GENERIC_TIME);
399 
400 	if(type==CONSTRAINT_BASIC_COMPULSORY_TIME ||
401 	 type==CONSTRAINT_BREAK_TIMES ||
402 	 type==CONSTRAINT_TEACHER_NOT_AVAILABLE_TIMES ||
403 	 type==CONSTRAINT_TEACHERS_MAX_HOURS_DAILY ||
404 	 type==CONSTRAINT_TEACHER_MAX_DAYS_PER_WEEK || /*newly enabled*/
405 	 type==CONSTRAINT_TEACHERS_MAX_GAPS_PER_WEEK ||
406 	 type==CONSTRAINT_TEACHER_MAX_GAPS_PER_WEEK ||
407 	 type==CONSTRAINT_TEACHER_MAX_HOURS_DAILY ||
408 	 type==CONSTRAINT_TEACHERS_MAX_HOURS_CONTINUOUSLY ||
409 	 type==CONSTRAINT_TEACHER_MAX_HOURS_CONTINUOUSLY ||
410 
411 	 type==CONSTRAINT_TEACHERS_MIN_HOURS_DAILY ||
412 	 type==CONSTRAINT_TEACHER_MIN_HOURS_DAILY ||
413 	 type==CONSTRAINT_TEACHERS_MAX_GAPS_PER_DAY ||
414 	 type==CONSTRAINT_TEACHER_MAX_GAPS_PER_DAY ||
415 
416 	 type==CONSTRAINT_STUDENTS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
417 	 type==CONSTRAINT_STUDENTS_SET_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
418 	 type==CONSTRAINT_STUDENTS_SET_NOT_AVAILABLE_TIMES ||
419 	 type==CONSTRAINT_STUDENTS_MAX_GAPS_PER_WEEK ||
420 	 type==CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_WEEK ||
421 
422 	 type==CONSTRAINT_STUDENTS_MAX_HOURS_DAILY ||
423 	 type==CONSTRAINT_STUDENTS_SET_MAX_HOURS_DAILY ||
424 	 type==CONSTRAINT_STUDENTS_MAX_HOURS_CONTINUOUSLY ||
425 	 type==CONSTRAINT_STUDENTS_SET_MAX_HOURS_CONTINUOUSLY ||
426 
427 	 type==CONSTRAINT_STUDENTS_MIN_HOURS_DAILY ||
428 	 type==CONSTRAINT_STUDENTS_SET_MIN_HOURS_DAILY ||
429 
430 	 type==CONSTRAINT_ACTIVITY_ENDS_STUDENTS_DAY ||
431 	 type==CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIME ||
432 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME ||
433 	 type==CONSTRAINT_ACTIVITIES_NOT_OVERLAPPING ||
434 	 type==CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES ||
435 	 type==CONSTRAINT_ACTIVITY_PREFERRED_TIME_SLOTS ||
436 	 type==CONSTRAINT_ACTIVITIES_PREFERRED_TIME_SLOTS ||
437 	 type==CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIMES ||
438 	 type==CONSTRAINT_ACTIVITIES_PREFERRED_STARTING_TIMES ||
439 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_HOUR ||
440 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_DAY ||
441 	 type==CONSTRAINT_TWO_ACTIVITIES_CONSECUTIVE ||
442 	 type==CONSTRAINT_TWO_ACTIVITIES_ORDERED ||
443 	 type==CONSTRAINT_MIN_GAPS_BETWEEN_ACTIVITIES ||
444 	 type==CONSTRAINT_SUBACTIVITIES_PREFERRED_TIME_SLOTS ||
445 	 type==CONSTRAINT_SUBACTIVITIES_PREFERRED_STARTING_TIMES ||
446 
447 	 type==CONSTRAINT_TEACHER_INTERVAL_MAX_DAYS_PER_WEEK ||
448 	 type==CONSTRAINT_TEACHERS_INTERVAL_MAX_DAYS_PER_WEEK ||
449 	 type==CONSTRAINT_STUDENTS_SET_INTERVAL_MAX_DAYS_PER_WEEK ||
450 	 type==CONSTRAINT_STUDENTS_INTERVAL_MAX_DAYS_PER_WEEK ||
451 
452 	 type==CONSTRAINT_ACTIVITIES_END_STUDENTS_DAY ||
453 
454 	 type==CONSTRAINT_TWO_ACTIVITIES_GROUPED ||
455 
456 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
457 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
458 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
459 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
460 
461 	 type==CONSTRAINT_TEACHERS_MAX_DAYS_PER_WEEK || /*newly enabled*/
462 
463 	 type==CONSTRAINT_THREE_ACTIVITIES_GROUPED ||
464 	 type==CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES ||
465 
466 //	 type==CONSTRAINT_TEACHERS_MIN_DAYS_PER_WEEK ||
467 //	 type==CONSTRAINT_TEACHER_MIN_DAYS_PER_WEEK ||
468 
469 //	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_DAILY ||
470 //	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_DAILY ||
471 //	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_DAILY ||
472 //	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_DAILY ||
473 
474 	 type==CONSTRAINT_STUDENTS_MAX_GAPS_PER_DAY ||
475 	 type==CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_DAY ||
476 
477 	 type==CONSTRAINT_ACTIVITIES_OCCUPY_MAX_TIME_SLOTS_FROM_SELECTION ||
478 	 type==CONSTRAINT_ACTIVITIES_MAX_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS ||
479 
480 	 type==CONSTRAINT_STUDENTS_SET_MAX_DAYS_PER_WEEK || /*newly enabled*/
481 	 type==CONSTRAINT_STUDENTS_MAX_DAYS_PER_WEEK || /*newly enabled*/
482 
483 	 //2017-02-06
484 //	 type==CONSTRAINT_TEACHER_MAX_SPAN_PER_DAY ||
485 //	 type==CONSTRAINT_TEACHERS_MAX_SPAN_PER_DAY ||
486 //	 type==CONSTRAINT_TEACHER_MIN_RESTING_HOURS ||
487 //	 type==CONSTRAINT_TEACHERS_MIN_RESTING_HOURS ||
488 //	 type==CONSTRAINT_STUDENTS_SET_MAX_SPAN_PER_DAY ||
489 //	 type==CONSTRAINT_STUDENTS_MAX_SPAN_PER_DAY ||
490 //	 type==CONSTRAINT_STUDENTS_SET_MIN_RESTING_HOURS ||
491 //	 type==CONSTRAINT_STUDENTS_MIN_RESTING_HOURS ||
492 
493 	 //2018-06-13
494 	 type==CONSTRAINT_TWO_ACTIVITIES_ORDERED_IF_SAME_DAY ||
495 
496 	 //2019-06-08
497 	 type==CONSTRAINT_STUDENTS_SET_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS || /*newly enabled*/
498 	 type==CONSTRAINT_STUDENTS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS || /*newly enabled*/
499 	 type==CONSTRAINT_TEACHER_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS || /*newly enabled*/
500 	 type==CONSTRAINT_TEACHERS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS || /*newly enabled*/
501 
502 	 type==CONSTRAINT_ACTIVITY_TAGS_NOT_OVERLAPPING ||
503 
504 	 type==CONSTRAINT_ACTIVITIES_OCCUPY_MIN_TIME_SLOTS_FROM_SELECTION ||
505 	 type==CONSTRAINT_ACTIVITIES_MIN_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS ||
506 
507 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MIN_HOURS_DAILY ||
508 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MIN_HOURS_DAILY ||
509 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MIN_HOURS_DAILY ||
510 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MIN_HOURS_DAILY ||
511 
512 	 type==CONSTRAINT_ACTIVITY_ENDS_TEACHERS_DAY ||
513 	 type==CONSTRAINT_ACTIVITIES_END_TEACHERS_DAY ||
514 
515 	 //mornings-afternoons
516 	 type==CONSTRAINT_TEACHERS_MAX_HOURS_DAILY_REAL_DAYS ||
517 	 type==CONSTRAINT_TEACHER_MAX_REAL_DAYS_PER_WEEK ||
518 	 type==CONSTRAINT_TEACHER_MAX_HOURS_DAILY_REAL_DAYS ||
519 
520 	 type==CONSTRAINT_STUDENTS_MAX_HOURS_DAILY_REAL_DAYS ||
521 	 type==CONSTRAINT_STUDENTS_SET_MAX_HOURS_DAILY_REAL_DAYS ||
522 
523 	 type==CONSTRAINT_TEACHERS_MAX_REAL_DAYS_PER_WEEK ||
524 
525 	 type==CONSTRAINT_TEACHERS_MIN_REAL_DAYS_PER_WEEK ||
526 	 type==CONSTRAINT_TEACHER_MIN_REAL_DAYS_PER_WEEK ||
527 
528 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS ||
529 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS ||
530 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS ||
531 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS ||
532 
533 	 type==CONSTRAINT_TEACHER_MAX_AFTERNOONS_PER_WEEK ||
534 	 type==CONSTRAINT_TEACHERS_MAX_AFTERNOONS_PER_WEEK ||
535 	 type==CONSTRAINT_TEACHER_MAX_MORNINGS_PER_WEEK ||
536 	 type==CONSTRAINT_TEACHERS_MAX_MORNINGS_PER_WEEK ||
537 
538 	 type==CONSTRAINT_TEACHER_MAX_TWO_ACTIVITY_TAGS_PER_DAY_FROM_N1N2N3 ||
539 	 type==CONSTRAINT_TEACHERS_MAX_TWO_ACTIVITY_TAGS_PER_DAY_FROM_N1N2N3 ||
540 
541 	 type==CONSTRAINT_TEACHERS_MIN_MORNINGS_PER_WEEK ||
542 	 type==CONSTRAINT_TEACHER_MIN_MORNINGS_PER_WEEK ||
543 	 type==CONSTRAINT_TEACHERS_MIN_AFTERNOONS_PER_WEEK ||
544 	 type==CONSTRAINT_TEACHER_MIN_AFTERNOONS_PER_WEEK ||
545 
546 	 type==CONSTRAINT_TEACHER_MAX_TWO_CONSECUTIVE_MORNINGS ||
547 	 type==CONSTRAINT_TEACHERS_MAX_TWO_CONSECUTIVE_MORNINGS ||
548 	 type==CONSTRAINT_TEACHER_MAX_TWO_CONSECUTIVE_AFTERNOONS ||
549 	 type==CONSTRAINT_TEACHERS_MAX_TWO_CONSECUTIVE_AFTERNOONS ||
550 
551 	 //Added in FET Algeria and Morocco on 2018-11-02
552 	 type==CONSTRAINT_TEACHERS_MAX_GAPS_PER_REAL_DAY ||
553 	 type==CONSTRAINT_TEACHER_MAX_GAPS_PER_REAL_DAY ||
554 	 type==CONSTRAINT_STUDENTS_MAX_GAPS_PER_REAL_DAY ||
555 	 type==CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_REAL_DAY ||
556 
557 	 //2019-07-03
558 	 type==CONSTRAINT_TEACHERS_MIN_HOURS_DAILY_REAL_DAYS ||
559 	 type==CONSTRAINT_TEACHER_MIN_HOURS_DAILY_REAL_DAYS ||
560 
561 	 //2019-08-18 - for Said213
562 	 type==CONSTRAINT_TEACHERS_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
563 	 type==CONSTRAINT_TEACHER_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
564 
565 	 type==CONSTRAINT_TEACHERS_MIN_HOURS_PER_MORNING ||
566 	 type==CONSTRAINT_TEACHER_MIN_HOURS_PER_MORNING ||
567 
568 	 type==CONSTRAINT_TEACHER_MAX_SPAN_PER_REAL_DAY ||
569 	 type==CONSTRAINT_TEACHERS_MAX_SPAN_PER_REAL_DAY ||
570 	 type==CONSTRAINT_STUDENTS_SET_MAX_SPAN_PER_REAL_DAY ||
571 	 type==CONSTRAINT_STUDENTS_MAX_SPAN_PER_REAL_DAY ||
572 
573 	 type==CONSTRAINT_TEACHER_MORNING_INTERVAL_MAX_DAYS_PER_WEEK ||
574 	 type==CONSTRAINT_TEACHERS_MORNING_INTERVAL_MAX_DAYS_PER_WEEK ||
575 
576 	 type==CONSTRAINT_TEACHER_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK ||
577 	 type==CONSTRAINT_TEACHERS_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK ||
578 
579 	 type==CONSTRAINT_STUDENTS_MIN_HOURS_PER_MORNING ||
580 	 type==CONSTRAINT_STUDENTS_SET_MIN_HOURS_PER_MORNING ||
581 
582 	 type==CONSTRAINT_TEACHER_MAX_ZERO_GAPS_PER_AFTERNOON ||
583 	 type==CONSTRAINT_TEACHERS_MAX_ZERO_GAPS_PER_AFTERNOON ||
584 
585 	 type==CONSTRAINT_STUDENTS_SET_MAX_AFTERNOONS_PER_WEEK ||
586 	 type==CONSTRAINT_STUDENTS_MAX_AFTERNOONS_PER_WEEK ||
587 	 type==CONSTRAINT_STUDENTS_SET_MAX_MORNINGS_PER_WEEK ||
588 	 type==CONSTRAINT_STUDENTS_MAX_MORNINGS_PER_WEEK ||
589 
590 	 type==CONSTRAINT_STUDENTS_MIN_MORNINGS_PER_WEEK ||
591 	 type==CONSTRAINT_STUDENTS_SET_MIN_MORNINGS_PER_WEEK ||
592 	 type==CONSTRAINT_STUDENTS_MIN_AFTERNOONS_PER_WEEK ||
593 	 type==CONSTRAINT_STUDENTS_SET_MIN_AFTERNOONS_PER_WEEK ||
594 
595 	 type==CONSTRAINT_STUDENTS_SET_MORNING_INTERVAL_MAX_DAYS_PER_WEEK ||
596 	 type==CONSTRAINT_STUDENTS_MORNING_INTERVAL_MAX_DAYS_PER_WEEK ||
597 	 type==CONSTRAINT_STUDENTS_SET_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK ||
598 	 type==CONSTRAINT_STUDENTS_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK ||
599 
600 	 type==CONSTRAINT_TEACHER_MAX_HOURS_PER_ALL_AFTERNOONS ||
601 	 type==CONSTRAINT_TEACHERS_MAX_HOURS_PER_ALL_AFTERNOONS ||
602 
603 	 type==CONSTRAINT_STUDENTS_SET_MAX_HOURS_PER_ALL_AFTERNOONS ||
604 	 type==CONSTRAINT_STUDENTS_MAX_HOURS_PER_ALL_AFTERNOONS ||
605 
606 	 type==CONSTRAINT_TEACHER_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON ||
607 	 type==CONSTRAINT_TEACHERS_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON ||
608 	 type==CONSTRAINT_STUDENTS_SET_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON ||
609 	 type==CONSTRAINT_STUDENTS_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON ||
610 
611 	 //2020-07-24 - for lakhdarbe
612 	 type==CONSTRAINT_STUDENTS_SET_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
613 	 type==CONSTRAINT_STUDENTS_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
614 
615 	 //Added in FET Algeria and Morocco on 2020-07-29
616 	 type==CONSTRAINT_TEACHERS_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS ||
617 	 type==CONSTRAINT_TEACHER_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS ||
618 	 type==CONSTRAINT_STUDENTS_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS ||
619 	 type==CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS ||
620 
621 	 type==CONSTRAINT_STUDENTS_SET_MAX_REAL_DAYS_PER_WEEK ||
622 	 type==CONSTRAINT_STUDENTS_MAX_REAL_DAYS_PER_WEEK ||
623 
624 	 //2021-08-12
625 	 type==CONSTRAINT_TEACHERS_MAX_GAPS_PER_MORNING_AND_AFTERNOON ||
626 	 type==CONSTRAINT_TEACHER_MAX_GAPS_PER_MORNING_AND_AFTERNOON ||
627 	 type==CONSTRAINT_TEACHERS_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
628 	 type==CONSTRAINT_TEACHER_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
629 	 type==CONSTRAINT_STUDENTS_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
630 	 type==CONSTRAINT_STUDENTS_SET_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
631 
632 	 type==CONSTRAINT_TWO_SETS_OF_ACTIVITIES_ORDERED ||
633 
634 	 //2021-09-26
635 	 type==CONSTRAINT_TEACHERS_MAX_THREE_CONSECUTIVE_DAYS ||
636 	 type==CONSTRAINT_TEACHER_MAX_THREE_CONSECUTIVE_DAYS)
637 		return true;
638 
639 	return false;
640 }
641 
canBeUsedInBlockPlanningMode()642 bool TimeConstraint::canBeUsedInBlockPlanningMode()
643 {
644 	assert(type!=CONSTRAINT_GENERIC_TIME);
645 
646 	if(type==CONSTRAINT_BASIC_COMPULSORY_TIME ||
647 	 type==CONSTRAINT_BREAK_TIMES ||
648 	 type==CONSTRAINT_TEACHER_NOT_AVAILABLE_TIMES ||
649 	 type==CONSTRAINT_TEACHERS_MAX_HOURS_DAILY ||
650 	 type==CONSTRAINT_TEACHER_MAX_DAYS_PER_WEEK ||
651 	 type==CONSTRAINT_TEACHERS_MAX_GAPS_PER_WEEK ||
652 	 type==CONSTRAINT_TEACHER_MAX_GAPS_PER_WEEK ||
653 	 type==CONSTRAINT_TEACHER_MAX_HOURS_DAILY ||
654 	 type==CONSTRAINT_TEACHERS_MAX_HOURS_CONTINUOUSLY ||
655 	 type==CONSTRAINT_TEACHER_MAX_HOURS_CONTINUOUSLY ||
656 
657 	 type==CONSTRAINT_TEACHERS_MIN_HOURS_DAILY ||
658 	 type==CONSTRAINT_TEACHER_MIN_HOURS_DAILY ||
659 	 type==CONSTRAINT_TEACHERS_MAX_GAPS_PER_DAY ||
660 	 type==CONSTRAINT_TEACHER_MAX_GAPS_PER_DAY ||
661 
662 	 type==CONSTRAINT_STUDENTS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
663 	 type==CONSTRAINT_STUDENTS_SET_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
664 	 type==CONSTRAINT_STUDENTS_SET_NOT_AVAILABLE_TIMES ||
665 	 type==CONSTRAINT_STUDENTS_MAX_GAPS_PER_WEEK ||
666 	 type==CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_WEEK ||
667 
668 	 type==CONSTRAINT_STUDENTS_MAX_HOURS_DAILY ||
669 	 type==CONSTRAINT_STUDENTS_SET_MAX_HOURS_DAILY ||
670 	 type==CONSTRAINT_STUDENTS_MAX_HOURS_CONTINUOUSLY ||
671 	 type==CONSTRAINT_STUDENTS_SET_MAX_HOURS_CONTINUOUSLY ||
672 
673 	 type==CONSTRAINT_STUDENTS_MIN_HOURS_DAILY ||
674 	 type==CONSTRAINT_STUDENTS_SET_MIN_HOURS_DAILY ||
675 
676 	 type==CONSTRAINT_ACTIVITY_ENDS_STUDENTS_DAY ||
677 	 type==CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIME ||
678 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME ||
679 	 type==CONSTRAINT_ACTIVITIES_NOT_OVERLAPPING ||
680 	 type==CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES ||
681 	 type==CONSTRAINT_ACTIVITY_PREFERRED_TIME_SLOTS ||
682 	 type==CONSTRAINT_ACTIVITIES_PREFERRED_TIME_SLOTS ||
683 	 type==CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIMES ||
684 	 type==CONSTRAINT_ACTIVITIES_PREFERRED_STARTING_TIMES ||
685 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_HOUR ||
686 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_DAY ||
687 	 type==CONSTRAINT_TWO_ACTIVITIES_CONSECUTIVE ||
688 	 type==CONSTRAINT_TWO_ACTIVITIES_ORDERED ||
689 	 type==CONSTRAINT_MIN_GAPS_BETWEEN_ACTIVITIES ||
690 	 type==CONSTRAINT_SUBACTIVITIES_PREFERRED_TIME_SLOTS ||
691 	 type==CONSTRAINT_SUBACTIVITIES_PREFERRED_STARTING_TIMES ||
692 
693 	 type==CONSTRAINT_TEACHER_INTERVAL_MAX_DAYS_PER_WEEK ||
694 	 type==CONSTRAINT_TEACHERS_INTERVAL_MAX_DAYS_PER_WEEK ||
695 	 type==CONSTRAINT_STUDENTS_SET_INTERVAL_MAX_DAYS_PER_WEEK ||
696 	 type==CONSTRAINT_STUDENTS_INTERVAL_MAX_DAYS_PER_WEEK ||
697 
698 	 type==CONSTRAINT_ACTIVITIES_END_STUDENTS_DAY ||
699 
700 	 type==CONSTRAINT_TWO_ACTIVITIES_GROUPED ||
701 
702 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
703 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
704 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
705 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
706 
707 	 type==CONSTRAINT_TEACHERS_MAX_DAYS_PER_WEEK ||
708 
709 	 type==CONSTRAINT_THREE_ACTIVITIES_GROUPED ||
710 	 type==CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES ||
711 
712 	 type==CONSTRAINT_TEACHERS_MIN_DAYS_PER_WEEK ||
713 	 type==CONSTRAINT_TEACHER_MIN_DAYS_PER_WEEK ||
714 
715 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_DAILY ||
716 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_DAILY ||
717 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_DAILY ||
718 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_DAILY ||
719 
720 	 type==CONSTRAINT_STUDENTS_MAX_GAPS_PER_DAY ||
721 	 type==CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_DAY ||
722 
723 	 type==CONSTRAINT_ACTIVITIES_OCCUPY_MAX_TIME_SLOTS_FROM_SELECTION ||
724 	 type==CONSTRAINT_ACTIVITIES_MAX_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS ||
725 
726 	 type==CONSTRAINT_STUDENTS_SET_MAX_DAYS_PER_WEEK ||
727 	 type==CONSTRAINT_STUDENTS_MAX_DAYS_PER_WEEK ||
728 
729 	 //2017-02-06
730 	 type==CONSTRAINT_TEACHER_MAX_SPAN_PER_DAY ||
731 	 type==CONSTRAINT_TEACHERS_MAX_SPAN_PER_DAY ||
732 	 type==CONSTRAINT_TEACHER_MIN_RESTING_HOURS ||
733 	 type==CONSTRAINT_TEACHERS_MIN_RESTING_HOURS ||
734 	 type==CONSTRAINT_STUDENTS_SET_MAX_SPAN_PER_DAY ||
735 	 type==CONSTRAINT_STUDENTS_MAX_SPAN_PER_DAY ||
736 	 type==CONSTRAINT_STUDENTS_SET_MIN_RESTING_HOURS ||
737 	 type==CONSTRAINT_STUDENTS_MIN_RESTING_HOURS ||
738 
739 	 //2018-06-13
740 	 type==CONSTRAINT_TWO_ACTIVITIES_ORDERED_IF_SAME_DAY ||
741 
742 	 //2019-06-08
743 	 type==CONSTRAINT_STUDENTS_SET_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
744 	 type==CONSTRAINT_STUDENTS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
745 	 type==CONSTRAINT_TEACHER_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
746 	 type==CONSTRAINT_TEACHERS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
747 
748 	 type==CONSTRAINT_ACTIVITY_TAGS_NOT_OVERLAPPING ||
749 
750 	 type==CONSTRAINT_ACTIVITIES_OCCUPY_MIN_TIME_SLOTS_FROM_SELECTION ||
751 	 type==CONSTRAINT_ACTIVITIES_MIN_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS ||
752 
753 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MIN_HOURS_DAILY ||
754 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MIN_HOURS_DAILY ||
755 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MIN_HOURS_DAILY ||
756 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MIN_HOURS_DAILY ||
757 
758 	 type==CONSTRAINT_ACTIVITY_ENDS_TEACHERS_DAY ||
759 	 type==CONSTRAINT_ACTIVITIES_END_TEACHERS_DAY ||
760 
761 	 //block-planning
762 	 type==CONSTRAINT_MAX_TOTAL_ACTIVITIES_FROM_SET_IN_SELECTED_TIME_SLOTS ||
763 	 type==CONSTRAINT_MAX_GAPS_BETWEEN_ACTIVITIES ||
764 
765 	 type==CONSTRAINT_TWO_SETS_OF_ACTIVITIES_ORDERED)
766 		return true;
767 
768 	return false;
769 }
770 
canBeUsedInTermsMode()771 bool TimeConstraint::canBeUsedInTermsMode()
772 {
773 	assert(type!=CONSTRAINT_GENERIC_TIME);
774 
775 	if(type==CONSTRAINT_BASIC_COMPULSORY_TIME ||
776 	 type==CONSTRAINT_BREAK_TIMES ||
777 	 type==CONSTRAINT_TEACHER_NOT_AVAILABLE_TIMES ||
778 	 type==CONSTRAINT_TEACHERS_MAX_HOURS_DAILY ||
779 	 type==CONSTRAINT_TEACHER_MAX_DAYS_PER_WEEK ||
780 	 type==CONSTRAINT_TEACHERS_MAX_GAPS_PER_WEEK ||
781 	 type==CONSTRAINT_TEACHER_MAX_GAPS_PER_WEEK ||
782 	 type==CONSTRAINT_TEACHER_MAX_HOURS_DAILY ||
783 	 type==CONSTRAINT_TEACHERS_MAX_HOURS_CONTINUOUSLY ||
784 	 type==CONSTRAINT_TEACHER_MAX_HOURS_CONTINUOUSLY ||
785 
786 	 type==CONSTRAINT_TEACHERS_MIN_HOURS_DAILY ||
787 	 type==CONSTRAINT_TEACHER_MIN_HOURS_DAILY ||
788 	 type==CONSTRAINT_TEACHERS_MAX_GAPS_PER_DAY ||
789 	 type==CONSTRAINT_TEACHER_MAX_GAPS_PER_DAY ||
790 
791 	 type==CONSTRAINT_STUDENTS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
792 	 type==CONSTRAINT_STUDENTS_SET_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR ||
793 	 type==CONSTRAINT_STUDENTS_SET_NOT_AVAILABLE_TIMES ||
794 	 type==CONSTRAINT_STUDENTS_MAX_GAPS_PER_WEEK ||
795 	 type==CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_WEEK ||
796 
797 	 type==CONSTRAINT_STUDENTS_MAX_HOURS_DAILY ||
798 	 type==CONSTRAINT_STUDENTS_SET_MAX_HOURS_DAILY ||
799 	 type==CONSTRAINT_STUDENTS_MAX_HOURS_CONTINUOUSLY ||
800 	 type==CONSTRAINT_STUDENTS_SET_MAX_HOURS_CONTINUOUSLY ||
801 
802 	 type==CONSTRAINT_STUDENTS_MIN_HOURS_DAILY ||
803 	 type==CONSTRAINT_STUDENTS_SET_MIN_HOURS_DAILY ||
804 
805 	 type==CONSTRAINT_ACTIVITY_ENDS_STUDENTS_DAY ||
806 	 type==CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIME ||
807 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME ||
808 	 type==CONSTRAINT_ACTIVITIES_NOT_OVERLAPPING ||
809 	 type==CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES ||
810 	 type==CONSTRAINT_ACTIVITY_PREFERRED_TIME_SLOTS ||
811 	 type==CONSTRAINT_ACTIVITIES_PREFERRED_TIME_SLOTS ||
812 	 type==CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIMES ||
813 	 type==CONSTRAINT_ACTIVITIES_PREFERRED_STARTING_TIMES ||
814 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_HOUR ||
815 	 type==CONSTRAINT_ACTIVITIES_SAME_STARTING_DAY ||
816 	 type==CONSTRAINT_TWO_ACTIVITIES_CONSECUTIVE ||
817 	 type==CONSTRAINT_TWO_ACTIVITIES_ORDERED ||
818 	 type==CONSTRAINT_MIN_GAPS_BETWEEN_ACTIVITIES ||
819 	 type==CONSTRAINT_SUBACTIVITIES_PREFERRED_TIME_SLOTS ||
820 	 type==CONSTRAINT_SUBACTIVITIES_PREFERRED_STARTING_TIMES ||
821 
822 	 type==CONSTRAINT_TEACHER_INTERVAL_MAX_DAYS_PER_WEEK ||
823 	 type==CONSTRAINT_TEACHERS_INTERVAL_MAX_DAYS_PER_WEEK ||
824 	 type==CONSTRAINT_STUDENTS_SET_INTERVAL_MAX_DAYS_PER_WEEK ||
825 	 type==CONSTRAINT_STUDENTS_INTERVAL_MAX_DAYS_PER_WEEK ||
826 
827 	 type==CONSTRAINT_ACTIVITIES_END_STUDENTS_DAY ||
828 
829 	 type==CONSTRAINT_TWO_ACTIVITIES_GROUPED ||
830 
831 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
832 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
833 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
834 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY ||
835 
836 	 type==CONSTRAINT_TEACHERS_MAX_DAYS_PER_WEEK ||
837 
838 	 type==CONSTRAINT_THREE_ACTIVITIES_GROUPED ||
839 	 type==CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES ||
840 
841 	 type==CONSTRAINT_TEACHERS_MIN_DAYS_PER_WEEK ||
842 	 type==CONSTRAINT_TEACHER_MIN_DAYS_PER_WEEK ||
843 
844 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_DAILY ||
845 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_DAILY ||
846 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_DAILY ||
847 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_DAILY ||
848 
849 	 type==CONSTRAINT_STUDENTS_MAX_GAPS_PER_DAY ||
850 	 type==CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_DAY ||
851 
852 	 type==CONSTRAINT_ACTIVITIES_OCCUPY_MAX_TIME_SLOTS_FROM_SELECTION ||
853 	 type==CONSTRAINT_ACTIVITIES_MAX_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS ||
854 
855 	 type==CONSTRAINT_STUDENTS_SET_MAX_DAYS_PER_WEEK ||
856 	 type==CONSTRAINT_STUDENTS_MAX_DAYS_PER_WEEK ||
857 
858 	 //2017-02-06
859 	 type==CONSTRAINT_TEACHER_MAX_SPAN_PER_DAY ||
860 	 type==CONSTRAINT_TEACHERS_MAX_SPAN_PER_DAY ||
861 	 type==CONSTRAINT_TEACHER_MIN_RESTING_HOURS ||
862 	 type==CONSTRAINT_TEACHERS_MIN_RESTING_HOURS ||
863 	 type==CONSTRAINT_STUDENTS_SET_MAX_SPAN_PER_DAY ||
864 	 type==CONSTRAINT_STUDENTS_MAX_SPAN_PER_DAY ||
865 	 type==CONSTRAINT_STUDENTS_SET_MIN_RESTING_HOURS ||
866 	 type==CONSTRAINT_STUDENTS_MIN_RESTING_HOURS ||
867 
868 	 //2018-06-13
869 	 type==CONSTRAINT_TWO_ACTIVITIES_ORDERED_IF_SAME_DAY ||
870 
871 	 //2019-06-08
872 	 type==CONSTRAINT_STUDENTS_SET_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
873 	 type==CONSTRAINT_STUDENTS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
874 	 type==CONSTRAINT_TEACHER_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
875 	 type==CONSTRAINT_TEACHERS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS ||
876 
877 	 type==CONSTRAINT_ACTIVITY_TAGS_NOT_OVERLAPPING ||
878 
879 	 type==CONSTRAINT_ACTIVITIES_OCCUPY_MIN_TIME_SLOTS_FROM_SELECTION ||
880 	 type==CONSTRAINT_ACTIVITIES_MIN_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS ||
881 
882 	 type==CONSTRAINT_TEACHERS_ACTIVITY_TAG_MIN_HOURS_DAILY ||
883 	 type==CONSTRAINT_TEACHER_ACTIVITY_TAG_MIN_HOURS_DAILY ||
884 	 type==CONSTRAINT_STUDENTS_ACTIVITY_TAG_MIN_HOURS_DAILY ||
885 	 type==CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MIN_HOURS_DAILY ||
886 
887 	 type==CONSTRAINT_ACTIVITY_ENDS_TEACHERS_DAY ||
888 	 type==CONSTRAINT_ACTIVITIES_END_TEACHERS_DAY ||
889 
890 	//terms
891 	 type==CONSTRAINT_ACTIVITIES_MAX_IN_A_TERM ||
892 	 type==CONSTRAINT_ACTIVITIES_OCCUPY_MAX_TERMS ||
893 
894 	 type==CONSTRAINT_TWO_SETS_OF_ACTIVITIES_ORDERED)
895 		return true;
896 
897 	return false;
898 }
899 
900 /////////////////////////////////////////////////////////////////////////////////////////////
901 /////////////////////////////////////////////////////////////////////////////////////////////
902 
ConstraintBasicCompulsoryTime()903 ConstraintBasicCompulsoryTime::ConstraintBasicCompulsoryTime(): TimeConstraint()
904 {
905 	this->type=CONSTRAINT_BASIC_COMPULSORY_TIME;
906 	this->weightPercentage=100;
907 }
908 
ConstraintBasicCompulsoryTime(double wp)909 ConstraintBasicCompulsoryTime::ConstraintBasicCompulsoryTime(double wp): TimeConstraint(wp)
910 {
911 	this->type=CONSTRAINT_BASIC_COMPULSORY_TIME;
912 }
913 
computeInternalStructure(QWidget * parent,Rules & r)914 bool ConstraintBasicCompulsoryTime::computeInternalStructure(QWidget* parent, Rules& r)
915 {
916 	Q_UNUSED(parent);
917 	Q_UNUSED(r);
918 
919 	return true;
920 }
921 
hasInactiveActivities(Rules & r)922 bool ConstraintBasicCompulsoryTime::hasInactiveActivities(Rules& r)
923 {
924 	Q_UNUSED(r);
925 	return false;
926 }
927 
getXmlDescription(Rules & r)928 QString ConstraintBasicCompulsoryTime::getXmlDescription(Rules& r)
929 {
930 	Q_UNUSED(r);
931 
932 	QString s = "<ConstraintBasicCompulsoryTime>\n";
933 	assert(this->weightPercentage==100.0);
934 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
935 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
936 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
937 	s+="</ConstraintBasicCompulsoryTime>\n";
938 	return s;
939 }
940 
getDescription(Rules & r)941 QString ConstraintBasicCompulsoryTime::getDescription(Rules& r)
942 {
943 	Q_UNUSED(r);
944 
945 	QString begin=QString("");
946 	if(!active)
947 		begin="X - ";
948 
949 	QString end=QString("");
950 	if(!comments.isEmpty())
951 		end=", "+tr("C: %1", "Comments").arg(comments);
952 
953 	return begin+tr("Basic compulsory constraints (time)") + ", " + tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage))+end;
954 }
955 
getDetailedDescription(Rules & r)956 QString ConstraintBasicCompulsoryTime::getDetailedDescription(Rules& r)
957 {
958 	Q_UNUSED(r);
959 
960 	QString s=tr("These are the basic compulsory constraints (referring to time allocation) for any timetable");
961 	s+="\n";
962 
963 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
964 	s+=tr("The basic time constraints try to avoid:");s+="\n";
965 	s+=QString("- ");s+=tr("teachers assigned to more than one activity simultaneously");s+="\n";
966 	s+=QString("- ");s+=tr("students assigned to more than one activity simultaneously");s+="\n";
967 
968 	if(!active){
969 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
970 		s+="\n";
971 	}
972 	if(!comments.isEmpty()){
973 		s+=tr("Comments=%1").arg(comments);
974 		s+="\n";
975 	}
976 
977 	return s;
978 }
979 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)980 double ConstraintBasicCompulsoryTime::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString){
981 	assert(r.internalStructureComputed);
982 
983 	int teachersConflicts, subgroupsConflicts;
984 
985 	assert(weightPercentage==100.0);
986 
987 	//This constraint fitness calculation routine is called firstly,
988 	//so we can compute the teacher and subgroups conflicts faster this way.
989 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
990 		c.teachersMatrixReady=true;
991 		c.subgroupsMatrixReady=true;
992 
993 		subgroups_conflicts = subgroupsConflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
994 		teachers_conflicts = teachersConflicts = c.getTeachersMatrix(r, teachersMatrix);
995 
996 		c.changedForMatrixCalculation=false;
997 	}
998 	else{
999 		assert(subgroups_conflicts>=0);
1000 		assert(teachers_conflicts>=0);
1001 		subgroupsConflicts = subgroups_conflicts;
1002 		teachersConflicts = teachers_conflicts;
1003 	}
1004 
1005 	int i,dd;
1006 
1007 	qint64 unallocated; //unallocated activities
1008 	int late; //late activities
1009 	int nte; //number of teacher exhaustions
1010 	int nse; //number of students exhaustions
1011 	int ntma;
1012 
1013 	//Part without logging..................................................................
1014 	if(conflictsString==nullptr){
1015 		//Unallocated or late activities
1016 		unallocated=0;
1017 		late=0;
1018 		for(i=0; i<r.nInternalActivities; i++){
1019 			if(c.times[i]==UNALLOCATED_TIME){
1020 				//Firstly, we consider a big clash each unallocated activity.
1021 				//Needs to be very a large constant, bigger than any other broken constraint.
1022 				//Take care: MAX_ACTIVITIES*this_constant <= INT_MAX
1023 				unallocated += /*r.internalActivitiesList[i].duration * r.internalActivitiesList[i].nSubgroups * */ 10000;
1024 				//(an unallocated activity for a year is more important than an unallocated activity for a subgroup)
1025 			}
1026 			else{
1027 				//Calculates the number of activities that are scheduled too late (in fact we
1028 				//calculate a function that increases as the activity is getting late)
1029 				int h=c.times[i]/r.nDaysPerWeek;
1030 				dd=r.internalActivitiesList[i].duration;
1031 				if(h+dd>r.nHoursPerDay){
1032 					int tmp;
1033 					tmp=1;
1034 					late += (h+dd-r.nHoursPerDay) * tmp * r.internalActivitiesList[i].iSubgroupsList.count();
1035 					//multiplied with the number
1036 					//of subgroups implied, for seeing the importance of the
1037 					//activity
1038 				}
1039 			}
1040 		}
1041 
1042 		assert(late==0);
1043 
1044 		//Below, for teachers and students, please remember that 2 means a weekly activity
1045 		//and 1 fortnightly one. So, if the matrix teachersMatrix[teacher][day][hour]==2, it is ok.
1046 
1047 		//Calculates the number of teachers exhaustion (when he has to teach more than
1048 		//one activity at the same time)
1049 		/*nte=0;
1050 		for(i=0; i<r.nInternalTeachers; i++)
1051 			for(int j=0; j<r.nDaysPerWeek; j++)
1052 				for(int k=0; k<r.nHoursPerDay; k++){
1053 					int tmp=teachersMatrix[i][j][k]-2;
1054 					if(tmp>0)
1055 						nte+=tmp;
1056 				}*/
1057 		nte = teachersConflicts; //faster
1058 
1059 		assert(nte==0);
1060 
1061 		//Calculates the number of subgroups exhaustion (a subgroup cannot attend two
1062 		//activities at the same time)
1063 		/*nse=0;
1064 		for(i=0; i<r.nInternalSubgroups; i++)
1065 			for(int j=0; j<r.nDaysPerWeek; j++)
1066 				for(int k=0; k<r.nHoursPerDay; k++){
1067 					int tmp=subgroupsMatrix[i][j][k]-2;
1068 					if(tmp>0)
1069 						nse += tmp;
1070 				}*/
1071 		nse = subgroupsConflicts; //faster
1072 
1073 		assert(nse==0);
1074 
1075 		ntma=0;
1076 		if(r.mode==MORNINGS_AFTERNOONS){
1077 			Matrix1D<bool> tm;
1078 			tm.resize(r.nDaysPerWeek);
1079 			for(int t=0; t<r.nInternalTeachers; t++){
1080 				Teacher* tch=r.internalTeachersList[t];
1081 				for(int d=0; d<r.nDaysPerWeek; d++){
1082 					tm[d]=false;
1083 					for(int h=0; h<r.nHoursPerDay; h++)
1084 						if(teachersMatrix[t][d][h]>0){
1085 							tm[d]=true;
1086 							break;
1087 						}
1088 				}
1089 				int nExceptions=0;
1090 				for(int d=0; d<r.nDaysPerWeek/2; d++)
1091 					if(tm[2*d] && tm[2*d+1])
1092 						nExceptions++;
1093 				//int tmp=0;
1094 				assert(tch->morningsAfternoonsBehavior!=TEACHER_MORNINGS_AFTERNOONS_BEHAVIOR_NOT_INITIALIZED);
1095 				if(tch->morningsAfternoonsBehavior==TEACHER_MORNING_OR_EXCLUSIVELY_AFTERNOON && nExceptions>0){
1096 					//tmp=nExceptions;
1097 					ntma+=nExceptions;
1098 				}
1099 				else if(tch->morningsAfternoonsBehavior==TEACHER_ONE_DAY_EXCEPTION && nExceptions>1){
1100 					//tmp=nExceptions-1;
1101 					ntma+=nExceptions-1;
1102 				}
1103 				else if(tch->morningsAfternoonsBehavior==TEACHER_TWO_DAYS_EXCEPTION && nExceptions>2){
1104 					//tmp=nExceptions-2;
1105 					ntma+=nExceptions-2;
1106 				}
1107 				else if(tch->morningsAfternoonsBehavior==TEACHER_THREE_DAYS_EXCEPTION && nExceptions>3){
1108 					//tmp=nExceptions-3;
1109 					ntma+=nExceptions-3;
1110 				}
1111 				else if(tch->morningsAfternoonsBehavior==TEACHER_FOUR_DAYS_EXCEPTION && nExceptions>4){
1112 					//tmp=nExceptions-4;
1113 					ntma+=nExceptions-4;
1114 				}
1115 				else if(tch->morningsAfternoonsBehavior==TEACHER_FIVE_DAYS_EXCEPTION && nExceptions>5){
1116 					//tmp=nExceptions-5;
1117 					ntma+=nExceptions-5;
1118 				}
1119 				else{
1120 					assert(0);
1121 				}
1122 			}
1123 		}
1124 
1125 		assert(ntma==0);
1126 	}
1127 	//part with logging....................................................................
1128 	else{
1129 		//Unallocated or late activities
1130 		unallocated=0;
1131 		late=0;
1132 		for(i=0; i<r.nInternalActivities; i++){
1133 			if(c.times[i]==UNALLOCATED_TIME){
1134 				//Firstly, we consider a big clash each unallocated activity.
1135 				//Needs to be very a large constant, bigger than any other broken constraint.
1136 				//Take care: MAX_ACTIVITIES*this_constant <= INT_MAX
1137 				unallocated += /*r.internalActivitiesList[i].duration * r.internalActivitiesList[i].nSubgroups * */ 10000;
1138 				//(an unallocated activity for a year is more important than an unallocated activity for a subgroup)
1139 				if(conflictsString!=nullptr){
1140 					QString s= tr("Time constraint basic compulsory broken: unallocated activity with id=%1 (%2)",
1141 						"%2 is the detailed description of activity - teachers, subject, students")
1142 						.arg(r.internalActivitiesList[i].id).arg(getActivityDetailedDescription(r, r.internalActivitiesList[i].id));
1143 					s+=" - ";
1144 					s += tr("this increases the conflicts total by %1")
1145 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100 * 10000));
1146 					//s += "\n";
1147 
1148 					dl.append(s);
1149 					cl.append(weightPercentage/100 * 10000);
1150 
1151 					(*conflictsString) += s + "\n";
1152 				}
1153 			}
1154 			else{
1155 				//Calculates the number of activities that are scheduled too late (in fact we
1156 				//calculate a function that increases as the activity is getting late)
1157 				int h=c.times[i]/r.nDaysPerWeek;
1158 				dd=r.internalActivitiesList[i].duration;
1159 				if(h+dd>r.nHoursPerDay){
1160 					assert(0);
1161 
1162 					int tmp;
1163 					tmp=1;
1164 					late += (h+dd-r.nHoursPerDay) * tmp * r.internalActivitiesList[i].iSubgroupsList.count();
1165 					//multiplied with the number
1166 					//of subgroups implied, for seeing the importance of the
1167 					//activity
1168 
1169 					if(conflictsString!=nullptr){
1170 						QString s=tr("Time constraint basic compulsory");
1171 						s+=": ";
1172 						s+=tr("activity with id=%1 is late.")
1173 						 .arg(r.internalActivitiesList[i].id);
1174 						s+=" ";
1175 						s+=tr("This increases the conflicts total by %1")
1176 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision((h+dd-r.nHoursPerDay)*tmp*r.internalActivitiesList[i].iSubgroupsList.count()*weightPercentage/100));
1177 						s+="\n";
1178 
1179 						dl.append(s);
1180 						cl.append((h+dd-r.nHoursPerDay)*tmp*r.internalActivitiesList[i].iSubgroupsList.count()*weightPercentage/100);
1181 
1182 						(*conflictsString) += s+"\n";
1183 					}
1184 				}
1185 			}
1186 		}
1187 
1188 		//Below, for teachers and students, please remember that 2 means a weekly activity
1189 		//and 1 fortnightly one. So, if the matrix teachersMatrix[teacher][day][hour]==2,
1190 		//that is ok.
1191 
1192 		//Calculates the number of teachers exhaustion (when he has to teach more than
1193 		//one activity at the same time)
1194 		nte=0;
1195 		for(i=0; i<r.nInternalTeachers; i++)
1196 			for(int j=0; j<r.nDaysPerWeek; j++)
1197 				for(int k=0; k<r.nHoursPerDay; k++){
1198 					int tmp=teachersMatrix[i][j][k]-1;
1199 					if(tmp>0){
1200 						if(conflictsString!=nullptr){
1201 							QString s=tr("Time constraint basic compulsory");
1202 							s+=": ";
1203 							s+=tr("teacher with name %1 has more than one allocated activity on day %2, hour %3")
1204 							 .arg(r.internalTeachersList[i]->name)
1205 							 .arg(r.daysOfTheWeek[j])
1206 							 .arg(r.hoursOfTheDay[k]);
1207 							s+=". ";
1208 							s+=tr("This increases the conflicts total by %1")
1209 							 .arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
1210 
1211 							(*conflictsString)+= s+"\n";
1212 
1213 							dl.append(s);
1214 							cl.append(tmp*weightPercentage/100);
1215 						}
1216 						nte+=tmp;
1217 					}
1218 				}
1219 
1220 		assert(nte==0);
1221 
1222 		//Calculates the number of subgroups exhaustion (a subgroup cannot attend two
1223 		//activities at the same time)
1224 		nse=0;
1225 		for(i=0; i<r.nInternalSubgroups; i++)
1226 			for(int j=0; j<r.nDaysPerWeek; j++)
1227 				for(int k=0; k<r.nHoursPerDay; k++){
1228 					int tmp=subgroupsMatrix[i][j][k]-1;
1229 					if(tmp>0){
1230 						if(conflictsString!=nullptr){
1231 							QString s=tr("Time constraint basic compulsory");
1232 							s+=": ";
1233 							s+=tr("subgroup %1 has more than one allocated activity on day %2, hour %3")
1234 							 .arg(r.internalSubgroupsList[i]->name)
1235 							 .arg(r.daysOfTheWeek[j])
1236 							 .arg(r.hoursOfTheDay[k]);
1237 							s+=". ";
1238 							s+=tr("This increases the conflicts total by %1")
1239 							 .arg(CustomFETString::numberPlusTwoDigitsPrecision((subgroupsMatrix[i][j][k]-1)*weightPercentage/100));
1240 
1241 							dl.append(s);
1242 							cl.append((subgroupsMatrix[i][j][k]-1)*weightPercentage/100);
1243 
1244 							*conflictsString += s+"\n";
1245 						}
1246 						nse += tmp;
1247 					}
1248 				}
1249 
1250 		assert(nse==0);
1251 
1252 		ntma=0;
1253 		if(r.mode==MORNINGS_AFTERNOONS){
1254 			Matrix1D<bool> tm;
1255 			tm.resize(r.nDaysPerWeek);
1256 			for(int t=0; t<r.nInternalTeachers; t++){
1257 				Teacher* tch=r.internalTeachersList[t];
1258 				for(int d=0; d<r.nDaysPerWeek; d++){
1259 					tm[d]=false;
1260 					for(int h=0; h<r.nHoursPerDay; h++)
1261 						if(teachersMatrix[t][d][h]>0){
1262 							tm[d]=true;
1263 							break;
1264 						}
1265 				}
1266 				int nExceptions=0;
1267 				for(int d=0; d<r.nDaysPerWeek/2; d++)
1268 					if(tm[2*d] && tm[2*d+1])
1269 						nExceptions++;
1270 				int tmp=0;
1271 				assert(tch->morningsAfternoonsBehavior!=TEACHER_MORNINGS_AFTERNOONS_BEHAVIOR_NOT_INITIALIZED);
1272 				if(tch->morningsAfternoonsBehavior==TEACHER_MORNING_OR_EXCLUSIVELY_AFTERNOON && nExceptions>0){
1273 					tmp=nExceptions;
1274 					ntma+=nExceptions;
1275 				}
1276 				else if(tch->morningsAfternoonsBehavior==TEACHER_ONE_DAY_EXCEPTION && nExceptions>1){
1277 					tmp=nExceptions-1;
1278 					ntma+=nExceptions-1;
1279 				}
1280 				else if(tch->morningsAfternoonsBehavior==TEACHER_TWO_DAYS_EXCEPTION && nExceptions>2){
1281 					tmp=nExceptions-2;
1282 					ntma+=nExceptions-2;
1283 				}
1284 				else if(tch->morningsAfternoonsBehavior==TEACHER_THREE_DAYS_EXCEPTION && nExceptions>3){
1285 					tmp=nExceptions-3;
1286 					ntma+=nExceptions-3;
1287 				}
1288 				else if(tch->morningsAfternoonsBehavior==TEACHER_FOUR_DAYS_EXCEPTION && nExceptions>4){
1289 					tmp=nExceptions-4;
1290 					ntma+=nExceptions-4;
1291 				}
1292 				else if(tch->morningsAfternoonsBehavior==TEACHER_FIVE_DAYS_EXCEPTION && nExceptions>5){
1293 					tmp=nExceptions-5;
1294 					ntma+=nExceptions-5;
1295 				}
1296 
1297 				if(tmp>0 && conflictsString!=nullptr){
1298 					QString s=tr("Time constraint basic compulsory");
1299 					s+=": ";
1300 					s+=tr("the teacher with name %1 does not respect mornings-afternoons behavior")
1301 					 .arg(r.internalTeachersList[t]->name);
1302 					s+=". ";
1303 					s+=tr("This increases the conflicts total by %1")
1304 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
1305 
1306 					(*conflictsString)+= s+"\n";
1307 
1308 					dl.append(s);
1309 					cl.append(tmp*weightPercentage/100);
1310 				}
1311 			}
1312 		}
1313 
1314 		assert(ntma==0);
1315 	}
1316 
1317 	/*if(nte!=teachersConflicts){
1318 		cout<<"nte=="<<nte<<", teachersConflicts=="<<teachersConflicts<<endl;
1319 		cout<<c.getTeachersMatrix(r, teachersMatrix)<<endl;
1320 	}
1321 	if(nse!=subgroupsConflicts){
1322 		cout<<"nse=="<<nse<<", subgroupsConflicts=="<<subgroupsConflicts<<endl;
1323 		cout<<c.getSubgroupsMatrix(r, subgroupsMatrix)<<endl;
1324 	}*/
1325 
1326 	/*assert(nte==teachersConflicts); //just a check, works only on logged fitness calculation
1327 	assert(nse==subgroupsConflicts);*/
1328 
1329 	return weightPercentage/100 * (unallocated + qint64(late) + qint64(nte) + qint64(nse) + qint64(ntma)); //conflicts factor
1330 }
1331 
isRelatedToActivity(Rules & r,Activity * a)1332 bool ConstraintBasicCompulsoryTime::isRelatedToActivity(Rules& r, Activity* a)
1333 {
1334 	Q_UNUSED(a);
1335 	Q_UNUSED(r);
1336 
1337 	return false;
1338 }
1339 
isRelatedToTeacher(Teacher * t)1340 bool ConstraintBasicCompulsoryTime::isRelatedToTeacher(Teacher* t)
1341 {
1342 	Q_UNUSED(t);
1343 
1344 	return false;
1345 }
1346 
isRelatedToSubject(Subject * s)1347 bool ConstraintBasicCompulsoryTime::isRelatedToSubject(Subject* s)
1348 {
1349 	Q_UNUSED(s);
1350 
1351 	return false;
1352 }
1353 
isRelatedToActivityTag(ActivityTag * s)1354 bool ConstraintBasicCompulsoryTime::isRelatedToActivityTag(ActivityTag* s)
1355 {
1356 	Q_UNUSED(s);
1357 
1358 	return false;
1359 }
1360 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)1361 bool ConstraintBasicCompulsoryTime::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
1362 {
1363 	Q_UNUSED(r);
1364 	Q_UNUSED(s);
1365 
1366 	return false;
1367 }
1368 
hasWrongDayOrHour(Rules & r)1369 bool ConstraintBasicCompulsoryTime::hasWrongDayOrHour(Rules& r)
1370 {
1371 	Q_UNUSED(r);
1372 	return false;
1373 }
1374 
canRepairWrongDayOrHour(Rules & r)1375 bool ConstraintBasicCompulsoryTime::canRepairWrongDayOrHour(Rules& r)
1376 {
1377 	Q_UNUSED(r);
1378 	assert(0);
1379 
1380 	return true;
1381 }
1382 
repairWrongDayOrHour(Rules & r)1383 bool ConstraintBasicCompulsoryTime::repairWrongDayOrHour(Rules& r)
1384 {
1385 	Q_UNUSED(r);
1386 	assert(0); //should check hasWrongDayOrHour, firstly
1387 
1388 	return true;
1389 }
1390 
1391 /////////////////////////////////////////////////////////////////////////////////////////////
1392 /////////////////////////////////////////////////////////////////////////////////////////////
1393 
ConstraintTeacherNotAvailableTimes()1394 ConstraintTeacherNotAvailableTimes::ConstraintTeacherNotAvailableTimes()
1395 	: TimeConstraint()
1396 {
1397 	this->type=CONSTRAINT_TEACHER_NOT_AVAILABLE_TIMES;
1398 }
1399 
ConstraintTeacherNotAvailableTimes(double wp,const QString & tn,QList<int> d,QList<int> h)1400 ConstraintTeacherNotAvailableTimes::ConstraintTeacherNotAvailableTimes(double wp, const QString& tn, QList<int> d, QList<int> h)
1401 	: TimeConstraint(wp)
1402 {
1403 	this->teacher=tn;
1404 	assert(d.count()==h.count());
1405 	this->days=d;
1406 	this->hours=h;
1407 	this->type=CONSTRAINT_TEACHER_NOT_AVAILABLE_TIMES;
1408 }
1409 
getXmlDescription(Rules & r)1410 QString ConstraintTeacherNotAvailableTimes::getXmlDescription(Rules& r)
1411 {
1412 	QString s="<ConstraintTeacherNotAvailableTimes>\n";
1413 	s+="	<Weight_Percentage>"+CustomFETString::number(weightPercentage)+"</Weight_Percentage>\n";
1414 	s+="	<Teacher>"+protect(this->teacher)+"</Teacher>\n";
1415 
1416 	s+="	<Number_of_Not_Available_Times>"+QString::number(this->days.count())+"</Number_of_Not_Available_Times>\n";
1417 	assert(days.count()==hours.count());
1418 	for(int i=0; i<days.count(); i++){
1419 		s+="	<Not_Available_Time>\n";
1420 		if(this->days.at(i)>=0)
1421 			s+="		<Day>"+protect(r.daysOfTheWeek[this->days.at(i)])+"</Day>\n";
1422 		if(this->hours.at(i)>=0)
1423 			s+="		<Hour>"+protect(r.hoursOfTheDay[this->hours.at(i)])+"</Hour>\n";
1424 		s+="	</Not_Available_Time>\n";
1425 	}
1426 
1427 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
1428 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
1429 	s+="</ConstraintTeacherNotAvailableTimes>\n";
1430 	return s;
1431 }
1432 
getDescription(Rules & r)1433 QString ConstraintTeacherNotAvailableTimes::getDescription(Rules& r)
1434 {
1435 	QString begin=QString("");
1436 	if(!active)
1437 		begin="X - ";
1438 
1439 	QString end=QString("");
1440 	if(!comments.isEmpty())
1441 		end=", "+tr("C: %1", "Comments").arg(comments);
1442 
1443 	QString s=tr("Teacher not available");s+=", ";
1444 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
1445 	s+=tr("T:%1", "Teacher").arg(this->teacher);s+=", ";
1446 
1447 	s+=tr("NA at:", "Not available at");
1448 	s+=" ";
1449 	assert(days.count()==hours.count());
1450 	for(int i=0; i<days.count(); i++){
1451 		if(this->days.at(i)>=0){
1452 			s+=r.daysOfTheWeek[this->days.at(i)];
1453 			s+=" ";
1454 		}
1455 		if(this->hours.at(i)>=0){
1456 			s+=r.hoursOfTheDay[this->hours.at(i)];
1457 		}
1458 		if(i<days.count()-1)
1459 			s+="; ";
1460 	}
1461 
1462 	return begin+s+end;
1463 }
1464 
getDetailedDescription(Rules & r)1465 QString ConstraintTeacherNotAvailableTimes::getDetailedDescription(Rules& r)
1466 {
1467 	QString s=tr("Time constraint");s+="\n";
1468 	s+=tr("A teacher is not available");s+="\n";
1469 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
1470 	s+=tr("Teacher=%1").arg(this->teacher);s+="\n";
1471 
1472 	s+=tr("Not available at:", "It refers to a teacher");
1473 	s+="\n";
1474 	assert(days.count()==hours.count());
1475 	for(int i=0; i<days.count(); i++){
1476 		if(this->days.at(i)>=0){
1477 			s+=r.daysOfTheWeek[this->days.at(i)];
1478 			s+=" ";
1479 		}
1480 		if(this->hours.at(i)>=0){
1481 			s+=r.hoursOfTheDay[this->hours.at(i)];
1482 		}
1483 		if(i<days.count()-1)
1484 			s+="; ";
1485 	}
1486 	s+="\n";
1487 
1488 	if(!active){
1489 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
1490 		s+="\n";
1491 	}
1492 	if(!comments.isEmpty()){
1493 		s+=tr("Comments=%1").arg(comments);
1494 		s+="\n";
1495 	}
1496 
1497 	return s;
1498 }
1499 
computeInternalStructure(QWidget * parent,Rules & r)1500 bool ConstraintTeacherNotAvailableTimes::computeInternalStructure(QWidget* parent, Rules& r){
1501 	//this->teacher_ID=r.searchTeacher(this->teacher);
1502 	teacher_ID=r.teachersHash.value(teacher, -1);
1503 
1504 	if(this->teacher_ID<0){
1505 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
1506 		 tr("Constraint teacher not available times is wrong because it refers to inexistent teacher."
1507 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
1508 
1509 		return false;
1510 	}
1511 
1512 	assert(days.count()==hours.count());
1513 	for(int k=0; k<days.count(); k++){
1514 		if(this->days.at(k) >= r.nDaysPerWeek){
1515 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
1516 			 tr("Constraint teacher not available times is wrong because it refers to removed day. Please correct"
1517 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
1518 
1519 			return false;
1520 		}
1521 		if(this->hours.at(k) >= r.nHoursPerDay){
1522 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
1523 			 tr("Constraint teacher not available times is wrong because an hour is too late (after the last acceptable slot). Please correct"
1524 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
1525 
1526 			return false;
1527 		}
1528 	}
1529 
1530 	assert(this->teacher_ID>=0);
1531 	return true;
1532 }
1533 
hasInactiveActivities(Rules & r)1534 bool ConstraintTeacherNotAvailableTimes::hasInactiveActivities(Rules& r)
1535 {
1536 	Q_UNUSED(r);
1537 	return false;
1538 }
1539 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)1540 double ConstraintTeacherNotAvailableTimes::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
1541 {
1542 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
1543 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
1544 		c.teachersMatrixReady=true;
1545 		c.subgroupsMatrixReady=true;
1546 
1547 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
1548 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
1549 
1550 		c.changedForMatrixCalculation=false;
1551 	}
1552 
1553 	//Calculates the number of hours when the teacher is supposed to be teaching, but he is not available
1554 	//This function consideres all the hours, I mean if there are for example 5 weekly courses
1555 	//scheduled on that hour (which is already a broken compulsory restriction - we only
1556 	//are allowed 1 weekly course for a certain teacher at a certain hour) we calculate
1557 	//5 broken restrictions for that function.
1558 	//TODO: decide if it is better to consider only 2 or 10 as a return value in this particular case
1559 	//(currently it is 10)
1560 	int tch=this->teacher_ID;
1561 
1562 	int nbroken;
1563 
1564 	nbroken=0;
1565 
1566 	assert(days.count()==hours.count());
1567 	for(int k=0; k<days.count(); k++){
1568 		int d=days.at(k);
1569 		int h=hours.at(k);
1570 
1571 		if(teachersMatrix[tch][d][h]>0){
1572 			nbroken+=teachersMatrix[tch][d][h];
1573 
1574 			if(conflictsString!=nullptr){
1575 				QString s= tr("Time constraint teacher not available");
1576 				s += " ";
1577 				s += tr("broken for teacher: %1 on day %2, hour %3")
1578 				 .arg(r.internalTeachersList[tch]->name)
1579 				 .arg(r.daysOfTheWeek[d])
1580 				 .arg(r.hoursOfTheDay[h]);
1581 				s += ". ";
1582 				s += tr("This increases the conflicts total by %1")
1583 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision(teachersMatrix[tch][d][h]*weightPercentage/100));
1584 
1585 				dl.append(s);
1586 				cl.append(teachersMatrix[tch][d][h]*weightPercentage/100);
1587 
1588 				*conflictsString += s+"\n";
1589 			}
1590 		}
1591 	}
1592 
1593 	if(weightPercentage==100.0)
1594 		assert(nbroken==0);
1595 	return weightPercentage/100 * nbroken;
1596 }
1597 
isRelatedToActivity(Rules & r,Activity * a)1598 bool ConstraintTeacherNotAvailableTimes::isRelatedToActivity(Rules& r, Activity* a)
1599 {
1600 	Q_UNUSED(a);
1601 	Q_UNUSED(r);
1602 
1603 	return false;
1604 }
1605 
isRelatedToTeacher(Teacher * t)1606 bool ConstraintTeacherNotAvailableTimes::isRelatedToTeacher(Teacher* t)
1607 {
1608 	if(this->teacher==t->name)
1609 		return true;
1610 	return false;
1611 }
1612 
isRelatedToSubject(Subject * s)1613 bool ConstraintTeacherNotAvailableTimes::isRelatedToSubject(Subject* s)
1614 {
1615 	Q_UNUSED(s);
1616 
1617 	return false;
1618 }
1619 
isRelatedToActivityTag(ActivityTag * s)1620 bool ConstraintTeacherNotAvailableTimes::isRelatedToActivityTag(ActivityTag* s)
1621 {
1622 	Q_UNUSED(s);
1623 
1624 	return false;
1625 }
1626 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)1627 bool ConstraintTeacherNotAvailableTimes::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
1628 {
1629 	Q_UNUSED(r);
1630 	Q_UNUSED(s);
1631 
1632 	return false;
1633 }
1634 
hasWrongDayOrHour(Rules & r)1635 bool ConstraintTeacherNotAvailableTimes::hasWrongDayOrHour(Rules& r)
1636 {
1637 	assert(days.count()==hours.count());
1638 
1639 	for(int i=0; i<days.count(); i++)
1640 		if(days.at(i)<0 || days.at(i)>=r.nDaysPerWeek
1641 		 || hours.at(i)<0 || hours.at(i)>=r.nHoursPerDay)
1642 			return true;
1643 
1644 	return false;
1645 }
1646 
canRepairWrongDayOrHour(Rules & r)1647 bool ConstraintTeacherNotAvailableTimes::canRepairWrongDayOrHour(Rules& r)
1648 {
1649 	assert(hasWrongDayOrHour(r));
1650 
1651 	return true;
1652 }
1653 
repairWrongDayOrHour(Rules & r)1654 bool ConstraintTeacherNotAvailableTimes::repairWrongDayOrHour(Rules& r)
1655 {
1656 	assert(hasWrongDayOrHour(r));
1657 
1658 	assert(days.count()==hours.count());
1659 
1660 	QList<int> newDays;
1661 	QList<int> newHours;
1662 
1663 	for(int i=0; i<days.count(); i++)
1664 		if(days.at(i)>=0 && days.at(i)<r.nDaysPerWeek
1665 		 && hours.at(i)>=0 && hours.at(i)<r.nHoursPerDay){
1666 			newDays.append(days.at(i));
1667 			newHours.append(hours.at(i));
1668 		}
1669 
1670 	days=newDays;
1671 	hours=newHours;
1672 
1673 	r.internalStructureComputed=false;
1674 	setRulesModifiedAndOtherThings(&r);
1675 
1676 	return true;
1677 }
1678 
1679 /////////////////////////////////////////////////////////////////////////////////////////////
1680 /////////////////////////////////////////////////////////////////////////////////////////////
1681 
ConstraintStudentsSetNotAvailableTimes()1682 ConstraintStudentsSetNotAvailableTimes::ConstraintStudentsSetNotAvailableTimes()
1683 	: TimeConstraint()
1684 {
1685 	this->type=CONSTRAINT_STUDENTS_SET_NOT_AVAILABLE_TIMES;
1686 }
1687 
ConstraintStudentsSetNotAvailableTimes(double wp,const QString & sn,QList<int> d,QList<int> h)1688 ConstraintStudentsSetNotAvailableTimes::ConstraintStudentsSetNotAvailableTimes(double wp, const QString& sn, QList<int> d, QList<int> h)
1689 	 : TimeConstraint(wp){
1690 	this->students = sn;
1691 	assert(d.count()==h.count());
1692 	this->days=d;
1693 	this->hours=h;
1694 	this->type=CONSTRAINT_STUDENTS_SET_NOT_AVAILABLE_TIMES;
1695 }
1696 
computeInternalStructure(QWidget * parent,Rules & r)1697 bool ConstraintStudentsSetNotAvailableTimes::computeInternalStructure(QWidget* parent, Rules& r){
1698 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
1699 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
1700 
1701 	if(ss==nullptr){
1702 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
1703 		 tr("Constraint students set not available is wrong because it refers to inexistent students set."
1704 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
1705 
1706 		return false;
1707 	}
1708 
1709 	assert(days.count()==hours.count());
1710 	for(int k=0; k<days.count(); k++){
1711 		if(this->days.at(k) >= r.nDaysPerWeek){
1712 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
1713 			 tr("Constraint students set not available times is wrong because it refers to removed day. Please correct"
1714 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
1715 
1716 			return false;
1717 		}
1718 		if(this->hours.at(k) >= r.nHoursPerDay){
1719 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
1720 			 tr("Constraint students set not available times is wrong because an hour is too late (after the last acceptable slot). Please correct"
1721 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
1722 
1723 			return false;
1724 		}
1725 	}
1726 
1727 	assert(ss!=nullptr);
1728 
1729 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
1730 	/*this->iSubgroupsList.clear();
1731 	if(ss->type==STUDENTS_SUBGROUP){
1732 		int tmp;
1733 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
1734 		assert(tmp>=0);
1735 		assert(tmp<r.nInternalSubgroups);
1736 		if(!this->iSubgroupsList.contains(tmp))
1737 			this->iSubgroupsList.append(tmp);
1738 	}
1739 	else if(ss->type==STUDENTS_GROUP){
1740 		StudentsGroup* stg=(StudentsGroup*)ss;
1741 		for(int i=0; i<stg->subgroupsList.size(); i++){
1742 			StudentsSubgroup* sts=stg->subgroupsList[i];
1743 			int tmp;
1744 			tmp=sts->indexInInternalSubgroupsList;
1745 			assert(tmp>=0);
1746 			assert(tmp<r.nInternalSubgroups);
1747 			if(!this->iSubgroupsList.contains(tmp))
1748 				this->iSubgroupsList.append(tmp);
1749 		}
1750 	}
1751 	else if(ss->type==STUDENTS_YEAR){
1752 		StudentsYear* sty=(StudentsYear*)ss;
1753 		for(int i=0; i<sty->groupsList.size(); i++){
1754 			StudentsGroup* stg=sty->groupsList[i];
1755 			for(int j=0; j<stg->subgroupsList.size(); j++){
1756 				StudentsSubgroup* sts=stg->subgroupsList[j];
1757 				int tmp;
1758 				tmp=sts->indexInInternalSubgroupsList;
1759 				assert(tmp>=0);
1760 				assert(tmp<r.nInternalSubgroups);
1761 				if(!this->iSubgroupsList.contains(tmp))
1762 					this->iSubgroupsList.append(tmp);
1763 			}
1764 		}
1765 	}
1766 	else
1767 		assert(0);*/
1768 	return true;
1769 }
1770 
hasInactiveActivities(Rules & r)1771 bool ConstraintStudentsSetNotAvailableTimes::hasInactiveActivities(Rules& r)
1772 {
1773 	Q_UNUSED(r);
1774 	return false;
1775 }
1776 
getXmlDescription(Rules & r)1777 QString ConstraintStudentsSetNotAvailableTimes::getXmlDescription(Rules& r)
1778 {
1779 	QString s="<ConstraintStudentsSetNotAvailableTimes>\n";
1780 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
1781 	s+="	<Students>"+protect(this->students)+"</Students>\n";
1782 
1783 	s+="	<Number_of_Not_Available_Times>"+QString::number(this->days.count())+"</Number_of_Not_Available_Times>\n";
1784 	assert(days.count()==hours.count());
1785 	for(int i=0; i<days.count(); i++){
1786 		s+="	<Not_Available_Time>\n";
1787 		if(this->days.at(i)>=0)
1788 			s+="		<Day>"+protect(r.daysOfTheWeek[this->days.at(i)])+"</Day>\n";
1789 		if(this->hours.at(i)>=0)
1790 			s+="		<Hour>"+protect(r.hoursOfTheDay[this->hours.at(i)])+"</Hour>\n";
1791 		s+="	</Not_Available_Time>\n";
1792 	}
1793 
1794 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
1795 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
1796 	s+="</ConstraintStudentsSetNotAvailableTimes>\n";
1797 	return s;
1798 }
1799 
getDescription(Rules & r)1800 QString ConstraintStudentsSetNotAvailableTimes::getDescription(Rules& r)
1801 {
1802 	QString begin=QString("");
1803 	if(!active)
1804 		begin="X - ";
1805 
1806 	QString end=QString("");
1807 	if(!comments.isEmpty())
1808 		end=", "+tr("C: %1", "Comments").arg(comments);
1809 
1810 	QString s;
1811 	s=tr("Students set not available");s+=", ";
1812 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
1813 	s+=tr("St:%1", "Students").arg(this->students);s+=", ";
1814 
1815 	s+=tr("NA at:", "Not available at");
1816 	s+=" ";
1817 	assert(days.count()==hours.count());
1818 	for(int i=0; i<days.count(); i++){
1819 		if(this->days.at(i)>=0){
1820 			s+=r.daysOfTheWeek[this->days.at(i)];
1821 			s+=" ";
1822 		}
1823 		if(this->hours.at(i)>=0){
1824 			s+=r.hoursOfTheDay[this->hours.at(i)];
1825 		}
1826 		if(i<days.count()-1)
1827 			s+="; ";
1828 	}
1829 
1830 	return begin+s+end;
1831 }
1832 
getDetailedDescription(Rules & r)1833 QString ConstraintStudentsSetNotAvailableTimes::getDetailedDescription(Rules& r)
1834 {
1835 	QString s=tr("Time constraint");s+="\n";
1836 	s+=tr("A students set is not available");s+="\n";
1837 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
1838 
1839 	s+=tr("Students=%1").arg(this->students);s+="\n";
1840 
1841 	s+=tr("Not available at:", "It refers to a students set");s+="\n";
1842 
1843 	assert(days.count()==hours.count());
1844 	for(int i=0; i<days.count(); i++){
1845 		if(this->days.at(i)>=0){
1846 			s+=r.daysOfTheWeek[this->days.at(i)];
1847 			s+=" ";
1848 		}
1849 		if(this->hours.at(i)>=0){
1850 			s+=r.hoursOfTheDay[this->hours.at(i)];
1851 		}
1852 		if(i<days.count()-1)
1853 			s+="; ";
1854 	}
1855 	s+="\n";
1856 
1857 	if(!active){
1858 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
1859 		s+="\n";
1860 	}
1861 	if(!comments.isEmpty()){
1862 		s+=tr("Comments=%1").arg(comments);
1863 		s+="\n";
1864 	}
1865 
1866 	return s;
1867 }
1868 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)1869 double ConstraintStudentsSetNotAvailableTimes::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
1870 {
1871 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
1872 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
1873 		c.teachersMatrixReady=true;
1874 		c.subgroupsMatrixReady=true;
1875 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
1876 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
1877 
1878 		c.changedForMatrixCalculation=false;
1879 	}
1880 
1881 	int nbroken;
1882 
1883 	nbroken=0;
1884 	for(int m=0; m<this->iSubgroupsList.count(); m++){
1885 		int sbg=this->iSubgroupsList.at(m);
1886 
1887 		assert(days.count()==hours.count());
1888 		for(int k=0; k<days.count(); k++){
1889 			int d=days.at(k);
1890 			int h=hours.at(k);
1891 
1892 			if(subgroupsMatrix[sbg][d][h]>0){
1893 				nbroken+=subgroupsMatrix[sbg][d][h];
1894 
1895 				if(conflictsString!=nullptr){
1896 					QString s= tr("Time constraint students set not available");
1897 					s += " ";
1898 					s += tr("broken for subgroup: %1 on day %2, hour %3")
1899 					 .arg(r.internalSubgroupsList[sbg]->name)
1900 					 .arg(r.daysOfTheWeek[d])
1901 					 .arg(r.hoursOfTheDay[h]);
1902 					s += ". ";
1903 					s += tr("This increases the conflicts total by %1")
1904 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(subgroupsMatrix[sbg][d][h]*weightPercentage/100));
1905 
1906 					dl.append(s);
1907 					cl.append(subgroupsMatrix[sbg][d][h]*weightPercentage/100);
1908 
1909 					*conflictsString += s+"\n";
1910 				}
1911 			}
1912 		}
1913 	}
1914 
1915 	if(weightPercentage==100.0)
1916 		assert(nbroken==0);
1917 	return weightPercentage/100 * nbroken;
1918 }
1919 
isRelatedToActivity(Rules & r,Activity * a)1920 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToActivity(Rules& r, Activity* a)
1921 {
1922 	Q_UNUSED(a);
1923 	Q_UNUSED(r);
1924 
1925 	return false;
1926 }
1927 
isRelatedToTeacher(Teacher * t)1928 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToTeacher(Teacher* t)
1929 {
1930 	Q_UNUSED(t);
1931 
1932 	return false;
1933 }
1934 
isRelatedToSubject(Subject * s)1935 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToSubject(Subject* s)
1936 {
1937 	Q_UNUSED(s);
1938 
1939 	return false;
1940 }
1941 
isRelatedToActivityTag(ActivityTag * s)1942 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToActivityTag(ActivityTag* s)
1943 {
1944 	Q_UNUSED(s);
1945 
1946 	return false;
1947 }
1948 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)1949 bool ConstraintStudentsSetNotAvailableTimes::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
1950 {
1951 	return r.setsShareStudents(this->students, s->name);
1952 }
1953 
hasWrongDayOrHour(Rules & r)1954 bool ConstraintStudentsSetNotAvailableTimes::hasWrongDayOrHour(Rules& r)
1955 {
1956 	assert(days.count()==hours.count());
1957 
1958 	for(int i=0; i<days.count(); i++)
1959 		if(days.at(i)<0 || days.at(i)>=r.nDaysPerWeek
1960 		 || hours.at(i)<0 || hours.at(i)>=r.nHoursPerDay)
1961 			return true;
1962 
1963 	return false;
1964 }
1965 
canRepairWrongDayOrHour(Rules & r)1966 bool ConstraintStudentsSetNotAvailableTimes::canRepairWrongDayOrHour(Rules& r)
1967 {
1968 	assert(hasWrongDayOrHour(r));
1969 
1970 	return true;
1971 }
1972 
repairWrongDayOrHour(Rules & r)1973 bool ConstraintStudentsSetNotAvailableTimes::repairWrongDayOrHour(Rules& r)
1974 {
1975 	assert(hasWrongDayOrHour(r));
1976 
1977 	assert(days.count()==hours.count());
1978 
1979 	QList<int> newDays;
1980 	QList<int> newHours;
1981 
1982 	for(int i=0; i<days.count(); i++)
1983 		if(days.at(i)>=0 && days.at(i)<r.nDaysPerWeek
1984 		 && hours.at(i)>=0 && hours.at(i)<r.nHoursPerDay){
1985 			newDays.append(days.at(i));
1986 			newHours.append(hours.at(i));
1987 		}
1988 
1989 	days=newDays;
1990 	hours=newHours;
1991 
1992 	r.internalStructureComputed=false;
1993 	setRulesModifiedAndOtherThings(&r);
1994 
1995 	return true;
1996 }
1997 
1998 /////////////////////////////////////////////////////////////////////////////////////////////////
1999 /////////////////////////////////////////////////////////////////////////////////////////////////
2000 
ConstraintActivitiesSameStartingTime()2001 ConstraintActivitiesSameStartingTime::ConstraintActivitiesSameStartingTime()
2002 	: TimeConstraint()
2003 {
2004 	type=CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME;
2005 }
2006 
ConstraintActivitiesSameStartingTime(double wp,int nact,const QList<int> & act)2007 ConstraintActivitiesSameStartingTime::ConstraintActivitiesSameStartingTime(double wp, int nact, const QList<int>& act)
2008  : TimeConstraint(wp)
2009  {
2010 	assert(nact>=2);
2011 	assert(act.count()==nact);
2012 	this->n_activities=nact;
2013 	this->activitiesId.clear();
2014 	for(int i=0; i<nact; i++)
2015 		this->activitiesId.append(act.at(i));
2016 
2017 	this->type=CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME;
2018 }
2019 
computeInternalStructure(QWidget * parent,Rules & r)2020 bool ConstraintActivitiesSameStartingTime::computeInternalStructure(QWidget* parent, Rules& r)
2021 {
2022 	//compute the indices of the activities,
2023 	//based on their unique ID
2024 
2025 	assert(this->n_activities==this->activitiesId.count());
2026 
2027 	this->_activities.clear();
2028 	for(int i=0; i<this->n_activities; i++){
2029 		int j=r.activitiesHash.value(activitiesId.at(i), -1);
2030 		//assert(j>=0);
2031 		if(j>=0)
2032 			_activities.append(j);
2033 		/*int j;
2034 		Activity* act;
2035 		for(j=0; j<r.nInternalActivities; j++){
2036 			act=&r.internalActivitiesList[j];
2037 			if(act->id==this->activitiesId[i]){
2038 				this->_activities.append(j);
2039 				break;
2040 			}
2041 		}*/
2042 	}
2043 	this->_n_activities=this->_activities.count();
2044 
2045 	if(this->_n_activities<=1){
2046 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
2047 			tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
2048 		//assert(0);
2049 		return false;
2050 	}
2051 
2052 	return true;
2053 }
2054 
removeUseless(Rules & r)2055 void ConstraintActivitiesSameStartingTime::removeUseless(Rules& r)
2056 {
2057 	//remove the activitiesId which no longer exist (used after the deletion of an activity)
2058 
2059 	assert(this->n_activities==this->activitiesId.count());
2060 
2061 	QList<int> tmpList;
2062 
2063 	for(int i=0; i<this->n_activities; i++){
2064 		Activity* act=r.activitiesPointerHash.value(activitiesId[i], nullptr);
2065 		if(act!=nullptr)
2066 			tmpList.append(act->id);
2067 		/*for(int k=0; k<r.activitiesList.size(); k++){
2068 			Activity* act=r.activitiesList[k];
2069 			if(act->id==this->activitiesId[i]){
2070 				tmpList.append(act->id);
2071 				break;
2072 			}
2073 		}*/
2074 	}
2075 
2076 	this->activitiesId=tmpList;
2077 	this->n_activities=this->activitiesId.count();
2078 
2079 	r.internalStructureComputed=false;
2080 }
2081 
hasInactiveActivities(Rules & r)2082 bool ConstraintActivitiesSameStartingTime::hasInactiveActivities(Rules& r)
2083 {
2084 	int count=0;
2085 
2086 	for(int i=0; i<this->n_activities; i++)
2087 		if(r.inactiveActivities.contains(this->activitiesId[i]))
2088 			count++;
2089 
2090 	if(this->n_activities-count<=1)
2091 		return true;
2092 	else
2093 		return false;
2094 }
2095 
getXmlDescription(Rules & r)2096 QString ConstraintActivitiesSameStartingTime::getXmlDescription(Rules& r)
2097 {
2098 	Q_UNUSED(r);
2099 
2100 	QString s="<ConstraintActivitiesSameStartingTime>\n";
2101 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
2102 	s+="	<Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
2103 	for(int i=0; i<this->n_activities; i++)
2104 		s+="	<Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
2105 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
2106 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
2107 	s+="</ConstraintActivitiesSameStartingTime>\n";
2108 	return s;
2109 }
2110 
getDescription(Rules & r)2111 QString ConstraintActivitiesSameStartingTime::getDescription(Rules& r)
2112 {
2113 	Q_UNUSED(r);
2114 
2115 	QString begin=QString("");
2116 	if(!active)
2117 		begin="X - ";
2118 
2119 	QString end=QString("");
2120 	if(!comments.isEmpty())
2121 		end=", "+tr("C: %1", "Comments").arg(comments);
2122 
2123 	QString s;
2124 	s+=tr("Activities same starting time");s+=", ";
2125 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
2126 	s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
2127 	for(int i=0; i<this->n_activities; i++){
2128 		s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);
2129 		if(i<this->n_activities-1)
2130 			s+=", ";
2131 	}
2132 
2133 	return begin+s+end;
2134 }
2135 
getDetailedDescription(Rules & r)2136 QString ConstraintActivitiesSameStartingTime::getDetailedDescription(Rules& r)
2137 {
2138 	QString s;
2139 
2140 	s=tr("Time constraint");s+="\n";
2141 	s+=tr("Activities must have the same starting time");s+="\n";
2142 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
2143 	s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
2144 	for(int i=0; i<this->n_activities; i++){
2145 		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]));
2146 		s+="\n";
2147 	}
2148 
2149 	if(!active){
2150 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
2151 		s+="\n";
2152 	}
2153 	if(!comments.isEmpty()){
2154 		s+=tr("Comments=%1").arg(comments);
2155 		s+="\n";
2156 	}
2157 
2158 	return s;
2159 }
2160 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)2161 double ConstraintActivitiesSameStartingTime::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
2162 {
2163 	assert(r.internalStructureComputed);
2164 
2165 	int nbroken;
2166 
2167 	//We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
2168 
2169 	//sum the differences in the scheduled time for all pairs of activities.
2170 
2171 	//without logging
2172 	if(conflictsString==nullptr){
2173 		nbroken=0;
2174 		for(int i=1; i<this->_n_activities; i++){
2175 			int t1=c.times[this->_activities[i]];
2176 			if(t1!=UNALLOCATED_TIME){
2177 				int day1=t1%r.nDaysPerWeek;
2178 				int hour1=t1/r.nDaysPerWeek;
2179 				for(int j=0; j<i; j++){
2180 					int t2=c.times[this->_activities[j]];
2181 					if(t2!=UNALLOCATED_TIME){
2182 						int day2=t2%r.nDaysPerWeek;
2183 						int hour2=t2/r.nDaysPerWeek;
2184 						int tmp=0;
2185 
2186 						tmp = abs(day1-day2) + abs(hour1-hour2);
2187 
2188 						if(tmp>0)
2189 							tmp=1;
2190 
2191 						nbroken+=tmp;
2192 					}
2193 				}
2194 			}
2195 		}
2196 	}
2197 	//with logging
2198 	else{
2199 		nbroken=0;
2200 		for(int i=1; i<this->_n_activities; i++){
2201 			int t1=c.times[this->_activities[i]];
2202 			if(t1!=UNALLOCATED_TIME){
2203 				int day1=t1%r.nDaysPerWeek;
2204 				int hour1=t1/r.nDaysPerWeek;
2205 				for(int j=0; j<i; j++){
2206 					int t2=c.times[this->_activities[j]];
2207 					if(t2!=UNALLOCATED_TIME){
2208 						int day2=t2%r.nDaysPerWeek;
2209 						int hour2=t2/r.nDaysPerWeek;
2210 						int tmp=0;
2211 
2212 						tmp = abs(day1-day2) + abs(hour1-hour2);
2213 
2214 						if(tmp>0)
2215 							tmp=1;
2216 
2217 						nbroken+=tmp;
2218 
2219 						if(tmp>0 && conflictsString!=nullptr){
2220 							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)",
2221 							"%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
2222 							 .arg(this->activitiesId[i])
2223 							 .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
2224 							 .arg(this->activitiesId[j])
2225 							 .arg(getActivityDetailedDescription(r, this->activitiesId[j]));
2226 							s+=". ";
2227 							s+=tr("Conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
2228 
2229 							dl.append(s);
2230 							cl.append(tmp*weightPercentage/100);
2231 
2232 							*conflictsString+= s+"\n";
2233 						}
2234 					}
2235 				}
2236 			}
2237 		}
2238 	}
2239 
2240 	if(weightPercentage==100)
2241 		assert(nbroken==0);
2242 	return weightPercentage/100 * nbroken;
2243 }
2244 
isRelatedToActivity(Rules & r,Activity * a)2245 bool ConstraintActivitiesSameStartingTime::isRelatedToActivity(Rules& r, Activity* a)
2246 {
2247 	Q_UNUSED(r);
2248 
2249 	for(int i=0; i<this->n_activities; i++)
2250 		if(this->activitiesId[i]==a->id)
2251 			return true;
2252 	return false;
2253 }
2254 
isRelatedToTeacher(Teacher * t)2255 bool ConstraintActivitiesSameStartingTime::isRelatedToTeacher(Teacher* t)
2256 {
2257 	Q_UNUSED(t);
2258 
2259 	return false;
2260 }
2261 
isRelatedToSubject(Subject * s)2262 bool ConstraintActivitiesSameStartingTime::isRelatedToSubject(Subject* s)
2263 {
2264 	Q_UNUSED(s);
2265 
2266 	return false;
2267 }
2268 
isRelatedToActivityTag(ActivityTag * s)2269 bool ConstraintActivitiesSameStartingTime::isRelatedToActivityTag(ActivityTag* s)
2270 {
2271 	Q_UNUSED(s);
2272 
2273 	return false;
2274 }
2275 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)2276 bool ConstraintActivitiesSameStartingTime::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
2277 {
2278 	Q_UNUSED(r);
2279 	Q_UNUSED(s);
2280 
2281 	return false;
2282 }
2283 
hasWrongDayOrHour(Rules & r)2284 bool ConstraintActivitiesSameStartingTime::hasWrongDayOrHour(Rules& r)
2285 {
2286 	Q_UNUSED(r);
2287 	return false;
2288 }
2289 
canRepairWrongDayOrHour(Rules & r)2290 bool ConstraintActivitiesSameStartingTime::canRepairWrongDayOrHour(Rules& r)
2291 {
2292 	Q_UNUSED(r);
2293 	assert(0);
2294 
2295 	return true;
2296 }
2297 
repairWrongDayOrHour(Rules & r)2298 bool ConstraintActivitiesSameStartingTime::repairWrongDayOrHour(Rules& r)
2299 {
2300 	Q_UNUSED(r);
2301 	assert(0); //should check hasWrongDayOrHour, firstly
2302 
2303 	return true;
2304 }
2305 
2306 ////////////////////////////////////////////////////////////////////////////////////////////
2307 ////////////////////////////////////////////////////////////////////////////////////////////
2308 
ConstraintActivitiesNotOverlapping()2309 ConstraintActivitiesNotOverlapping::ConstraintActivitiesNotOverlapping()
2310 	: TimeConstraint()
2311 {
2312 	type=CONSTRAINT_ACTIVITIES_NOT_OVERLAPPING;
2313 }
2314 
ConstraintActivitiesNotOverlapping(double wp,int nact,const QList<int> & act)2315 ConstraintActivitiesNotOverlapping::ConstraintActivitiesNotOverlapping(double wp, int nact, const QList<int>& act)
2316  : TimeConstraint(wp)
2317  {
2318   	assert(nact>=2);
2319   	assert(act.count()==nact);
2320 	this->n_activities=nact;
2321 	this->activitiesId.clear();
2322 	for(int i=0; i<nact; i++)
2323 		this->activitiesId.append(act.at(i));
2324 
2325 	this->type=CONSTRAINT_ACTIVITIES_NOT_OVERLAPPING;
2326 }
2327 
computeInternalStructure(QWidget * parent,Rules & r)2328 bool ConstraintActivitiesNotOverlapping::computeInternalStructure(QWidget* parent, Rules& r)
2329 {
2330 	//compute the indices of the activities,
2331 	//based on their unique ID
2332 
2333 	assert(this->n_activities==this->activitiesId.count());
2334 
2335 	this->_activities.clear();
2336 	for(int i=0; i<this->n_activities; i++){
2337 		int j=r.activitiesHash.value(activitiesId.at(i), -1);
2338 		//assert(j>=0);
2339 		if(j>=0)
2340 			_activities.append(j);
2341 		/*int j;
2342 		Activity* act;
2343 		for(j=0; j<r.nInternalActivities; j++){
2344 			act=&r.internalActivitiesList[j];
2345 			if(act->id==this->activitiesId[i]){
2346 				this->_activities.append(j);
2347 				break;
2348 			}
2349 		}*/
2350 	}
2351 	this->_n_activities=this->_activities.count();
2352 
2353 	if(this->_n_activities<=1){
2354 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
2355 			tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
2356 		//assert(0);
2357 		return false;
2358 	}
2359 
2360 	return true;
2361 }
2362 
removeUseless(Rules & r)2363 void ConstraintActivitiesNotOverlapping::removeUseless(Rules& r)
2364 {
2365 	//remove the activitiesId which no longer exist (used after the deletion of an activity)
2366 
2367 	assert(this->n_activities==this->activitiesId.count());
2368 
2369 	QList<int> tmpList;
2370 
2371 	for(int i=0; i<this->n_activities; i++){
2372 		Activity* act=r.activitiesPointerHash.value(activitiesId[i], nullptr);
2373 		if(act!=nullptr)
2374 			tmpList.append(act->id);
2375 		/*for(int k=0; k<r.activitiesList.size(); k++){
2376 			Activity* act=r.activitiesList[k];
2377 			if(act->id==this->activitiesId[i]){
2378 				tmpList.append(act->id);
2379 				break;
2380 			}
2381 		}*/
2382 	}
2383 
2384 	this->activitiesId=tmpList;
2385 	this->n_activities=this->activitiesId.count();
2386 
2387 	r.internalStructureComputed=false;
2388 }
2389 
hasInactiveActivities(Rules & r)2390 bool ConstraintActivitiesNotOverlapping::hasInactiveActivities(Rules& r)
2391 {
2392 	int count=0;
2393 
2394 	for(int i=0; i<this->n_activities; i++)
2395 		if(r.inactiveActivities.contains(this->activitiesId[i]))
2396 			count++;
2397 
2398 	if(this->n_activities-count<=1)
2399 		return true;
2400 	else
2401 		return false;
2402 }
2403 
getXmlDescription(Rules & r)2404 QString ConstraintActivitiesNotOverlapping::getXmlDescription(Rules& r)
2405 {
2406 	Q_UNUSED(r);
2407 
2408 	QString s="<ConstraintActivitiesNotOverlapping>\n";
2409 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
2410 	s+="	<Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
2411 	for(int i=0; i<this->n_activities; i++)
2412 		s+="	<Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
2413 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
2414 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
2415 	s+="</ConstraintActivitiesNotOverlapping>\n";
2416 	return s;
2417 }
2418 
getDescription(Rules & r)2419 QString ConstraintActivitiesNotOverlapping::getDescription(Rules& r)
2420 {
2421 	Q_UNUSED(r);
2422 
2423 	QString begin=QString("");
2424 	if(!active)
2425 		begin="X - ";
2426 
2427 	QString end=QString("");
2428 	if(!comments.isEmpty())
2429 		end=", "+tr("C: %1", "Comments").arg(comments);
2430 
2431 	QString s;
2432 	s+=tr("Activities not overlapping");s+=", ";
2433 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
2434 	s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
2435 	for(int i=0; i<this->n_activities; i++){
2436 		s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);
2437 		if(i<this->n_activities-1)
2438 			s+=", ";
2439 	}
2440 
2441 	return begin+s+end;
2442 }
2443 
getDetailedDescription(Rules & r)2444 QString ConstraintActivitiesNotOverlapping::getDetailedDescription(Rules& r)
2445 {
2446 	QString s=tr("Time constraint");s+="\n";
2447 	s+=tr("Activities must not overlap");s+="\n";
2448 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
2449 	s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
2450 	for(int i=0; i<this->n_activities; i++){
2451 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
2452 			.arg(this->activitiesId[i]).arg(getActivityDetailedDescription(r, this->activitiesId[i]));
2453 		s+="\n";
2454 	}
2455 
2456 	if(!active){
2457 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
2458 		s+="\n";
2459 	}
2460 	if(!comments.isEmpty()){
2461 		s+=tr("Comments=%1").arg(comments);
2462 		s+="\n";
2463 	}
2464 
2465 	return s;
2466 }
2467 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)2468 double ConstraintActivitiesNotOverlapping::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
2469 {
2470 	assert(r.internalStructureComputed);
2471 
2472 	int nbroken;
2473 
2474 	//We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
2475 
2476 	//sum the overlapping hours for all pairs of activities.
2477 	//without logging
2478 	if(conflictsString==nullptr){
2479 		nbroken=0;
2480 		for(int i=1; i<this->_n_activities; i++){
2481 			int t1=c.times[this->_activities[i]];
2482 			if(t1!=UNALLOCATED_TIME){
2483 				int day1=t1%r.nDaysPerWeek;
2484 				int hour1=t1/r.nDaysPerWeek;
2485 				int duration1=r.internalActivitiesList[this->_activities[i]].duration;
2486 
2487 				for(int j=0; j<i; j++){
2488 					int t2=c.times[this->_activities[j]];
2489 					if(t2!=UNALLOCATED_TIME){
2490 						int day2=t2%r.nDaysPerWeek;
2491 						int hour2=t2/r.nDaysPerWeek;
2492 						int duration2=r.internalActivitiesList[this->_activities[j]].duration;
2493 
2494 						//the number of overlapping hours
2495 						int tt=0;
2496 						if(day1==day2){
2497 							int start=std::max(hour1, hour2);
2498 							int stop=std::min(hour1+duration1, hour2+duration2);
2499 							if(stop>start)
2500 								tt+=stop-start;
2501 						}
2502 
2503 						nbroken+=tt;
2504 					}
2505 				}
2506 			}
2507 		}
2508 	}
2509 	//with logging
2510 	else{
2511 		nbroken=0;
2512 		for(int i=1; i<this->_n_activities; i++){
2513 			int t1=c.times[this->_activities[i]];
2514 			if(t1!=UNALLOCATED_TIME){
2515 				int day1=t1%r.nDaysPerWeek;
2516 				int hour1=t1/r.nDaysPerWeek;
2517 				int duration1=r.internalActivitiesList[this->_activities[i]].duration;
2518 
2519 				for(int j=0; j<i; j++){
2520 					int t2=c.times[this->_activities[j]];
2521 					if(t2!=UNALLOCATED_TIME){
2522 						int day2=t2%r.nDaysPerWeek;
2523 						int hour2=t2/r.nDaysPerWeek;
2524 						int duration2=r.internalActivitiesList[this->_activities[j]].duration;
2525 
2526 						//the number of overlapping hours
2527 						int tt=0;
2528 						if(day1==day2){
2529 							int start=std::max(hour1, hour2);
2530 							int stop=std::min(hour1+duration1, hour2+duration2);
2531 							if(stop>start)
2532 								tt+=stop-start;
2533 						}
2534 
2535 						//The overlapping hours
2536 						int tmp=tt;
2537 
2538 						nbroken+=tmp;
2539 
2540 						if(tt>0 && conflictsString!=nullptr){
2541 
2542 							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",
2543 							 "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
2544 							 .arg(this->activitiesId[i])
2545 							 .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
2546 							 .arg(this->activitiesId[j])
2547 							 .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
2548 							 .arg(tt);
2549 							s+=", ";
2550 							s+=tr("conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
2551 
2552 							dl.append(s);
2553 							cl.append(tmp*weightPercentage/100);
2554 
2555 							*conflictsString+= s+"\n";
2556 						}
2557 					}
2558 				}
2559 			}
2560 		}
2561 	}
2562 
2563 	if(weightPercentage==100)
2564 		assert(nbroken==0);
2565 	return weightPercentage/100 * nbroken;
2566 }
2567 
isRelatedToActivity(Rules & r,Activity * a)2568 bool ConstraintActivitiesNotOverlapping::isRelatedToActivity(Rules& r, Activity* a)
2569 {
2570 	Q_UNUSED(r);
2571 
2572 	for(int i=0; i<this->n_activities; i++)
2573 		if(this->activitiesId[i]==a->id)
2574 			return true;
2575 	return false;
2576 }
2577 
isRelatedToTeacher(Teacher * t)2578 bool ConstraintActivitiesNotOverlapping::isRelatedToTeacher(Teacher* t)
2579 {
2580 	Q_UNUSED(t);
2581 
2582 	return false;
2583 }
2584 
isRelatedToSubject(Subject * s)2585 bool ConstraintActivitiesNotOverlapping::isRelatedToSubject(Subject* s)
2586 {
2587 	Q_UNUSED(s);
2588 
2589 	return false;
2590 }
2591 
isRelatedToActivityTag(ActivityTag * s)2592 bool ConstraintActivitiesNotOverlapping::isRelatedToActivityTag(ActivityTag* s)
2593 {
2594 	Q_UNUSED(s);
2595 
2596 	return false;
2597 }
2598 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)2599 bool ConstraintActivitiesNotOverlapping::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
2600 {
2601 	Q_UNUSED(r);
2602 	Q_UNUSED(s);
2603 
2604 	return false;
2605 }
2606 
hasWrongDayOrHour(Rules & r)2607 bool ConstraintActivitiesNotOverlapping::hasWrongDayOrHour(Rules& r)
2608 {
2609 	Q_UNUSED(r);
2610 	return false;
2611 }
2612 
canRepairWrongDayOrHour(Rules & r)2613 bool ConstraintActivitiesNotOverlapping::canRepairWrongDayOrHour(Rules& r)
2614 {
2615 	Q_UNUSED(r);
2616 	assert(0);
2617 
2618 	return true;
2619 }
2620 
repairWrongDayOrHour(Rules & r)2621 bool ConstraintActivitiesNotOverlapping::repairWrongDayOrHour(Rules& r)
2622 {
2623 	Q_UNUSED(r);
2624 	assert(0); //should check hasWrongDayOrHour, firstly
2625 
2626 	return true;
2627 }
2628 
2629 ////////////////////////////////////////////////////////////////////////////////////////////
2630 ////////////////////////////////////////////////////////////////////////////////////////////
2631 
ConstraintActivityTagsNotOverlapping()2632 ConstraintActivityTagsNotOverlapping::ConstraintActivityTagsNotOverlapping()
2633 	: TimeConstraint()
2634 {
2635 	type=CONSTRAINT_ACTIVITY_TAGS_NOT_OVERLAPPING;
2636 }
2637 
ConstraintActivityTagsNotOverlapping(double wp,const QStringList & atl)2638 ConstraintActivityTagsNotOverlapping::ConstraintActivityTagsNotOverlapping(double wp, const QStringList& atl)
2639  : TimeConstraint(wp)
2640  {
2641 	activityTagsNames=atl;
2642 
2643 	this->type=CONSTRAINT_ACTIVITY_TAGS_NOT_OVERLAPPING;
2644 }
2645 
computeInternalStructure(QWidget * parent,Rules & r)2646 bool ConstraintActivityTagsNotOverlapping::computeInternalStructure(QWidget* parent, Rules& r)
2647 {
2648 	if(activityTagsNames.count()<2){
2649 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
2650 			tr("The following constraint is wrong (because it needs at least two activity tags). "
2651 			"Please correct it:\n%1").arg(this->getDetailedDescription(r)));
2652 		return false;
2653 	}
2654 
2655 	activityTagsIndices.clear();
2656 	activitiesIndicesLists.clear();
2657 	for(const QString& activityTagName : qAsConst(activityTagsNames)){
2658 		int activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
2659 		assert(activityTagIndex>=0);
2660 		activityTagsIndices.append(activityTagIndex);
2661 		activitiesIndicesLists.append(QList<int>());
2662 	}
2663 
2664 	for(int ai=0; ai<r.nInternalActivities; ai++){
2665 		Activity* act=&r.internalActivitiesList[ai];
2666 		for(int i=0; i<activityTagsIndices.count(); i++){
2667 			int at=activityTagsIndices.at(i);
2668 			if(act->iActivityTagsSet.contains(at))
2669 				activitiesIndicesLists[i].append(ai);
2670 		}
2671 	}
2672 
2673 	assert(activitiesIndicesLists.count()==activityTagsIndices.count());
2674 	assert(activityTagsNames.count()==activityTagsIndices.count());
2675 	for(int i=0; i<activityTagsIndices.count(); i++){
2676 		if(activitiesIndicesLists.at(i).count()<1){
2677 			TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
2678 				tr("Following constraint is wrong (because you need at least one activity for each activity tag, but"
2679 				 " no activity has activity tag %1). Please correct it:\n%2").arg(activityTagsNames.at(i)).arg(this->getDetailedDescription(r)));
2680 			return false;
2681 		}
2682 	}
2683 
2684 	return true;
2685 }
2686 
hasInactiveActivities(Rules & r)2687 bool ConstraintActivityTagsNotOverlapping::hasInactiveActivities(Rules& r)
2688 {
2689 	Q_UNUSED(r);
2690 	return false;
2691 }
2692 
getXmlDescription(Rules & r)2693 QString ConstraintActivityTagsNotOverlapping::getXmlDescription(Rules& r)
2694 {
2695 	Q_UNUSED(r);
2696 
2697 	QString s="<ConstraintActivityTagsNotOverlapping>\n";
2698 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
2699 	s+="	<Number_of_Activity_Tags>"+QString::number(this->activityTagsNames.count())+"</Number_of_Activity_Tags>\n";
2700 	for(const QString& atn : qAsConst(activityTagsNames))
2701 		s+="	<Activity_Tag>"+protect(atn)+"</Activity_Tag>\n";
2702 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
2703 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
2704 	s+="</ConstraintActivityTagsNotOverlapping>\n";
2705 	return s;
2706 }
2707 
getDescription(Rules & r)2708 QString ConstraintActivityTagsNotOverlapping::getDescription(Rules& r)
2709 {
2710 	Q_UNUSED(r);
2711 
2712 	QString begin=QString("");
2713 	if(!active)
2714 		begin="X - ";
2715 
2716 	QString end=QString("");
2717 	if(!comments.isEmpty())
2718 		end=", "+tr("C: %1", "Comments").arg(comments);
2719 
2720 	QString s;
2721 	s+=tr("Activity tags not overlapping");s+=", ";
2722 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
2723 	s+=tr("NAT:%1", "Number of activity tags").arg(this->activityTagsNames.count());s+=", ";
2724 	int i=0;
2725 	for(const QString& atn : qAsConst(activityTagsNames)){
2726 		s+=tr("AT:%1", "Activity tag").arg(atn);
2727 		if(i<this->activityTagsNames.count()-1)
2728 			s+=", ";
2729 		i++;
2730 	}
2731 
2732 	return begin+s+end;
2733 }
2734 
getDetailedDescription(Rules & r)2735 QString ConstraintActivityTagsNotOverlapping::getDetailedDescription(Rules& r)
2736 {
2737 	Q_UNUSED(r);
2738 
2739 	QString s=tr("Time constraint");s+="\n";
2740 	s+=tr("Activity tags must not overlap");s+="\n";
2741 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
2742 	s+=tr("Number of activity tags=%1").arg(this->activityTagsNames.count());s+="\n";
2743 	for(const QString& atn : qAsConst(activityTagsNames)){
2744 		s+=tr("Activity tag=%1").arg(atn);
2745 		s+="\n";
2746 	}
2747 
2748 	if(!active){
2749 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
2750 		s+="\n";
2751 	}
2752 	if(!comments.isEmpty()){
2753 		s+=tr("Comments=%1").arg(comments);
2754 		s+="\n";
2755 	}
2756 
2757 	return s;
2758 }
2759 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)2760 double ConstraintActivityTagsNotOverlapping::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
2761 {
2762 	assert(r.internalStructureComputed);
2763 
2764 	int nbroken;
2765 
2766 	//We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
2767 
2768 	//sum the overlapping hours for all pairs of activities.
2769 	nbroken=0;
2770 
2771 	for(int k1=1; k1<activitiesIndicesLists.count(); k1++){
2772 		const QList<int>& l1=activitiesIndicesLists.at(k1);
2773 		for(int k2=0; k2<k1; k2++){
2774 			const QList<int>& l2=activitiesIndicesLists.at(k2);
2775 
2776 			for(int i : qAsConst(l1)){
2777 				int t1=c.times[i];
2778 				if(t1!=UNALLOCATED_TIME){
2779 					int day1=t1%r.nDaysPerWeek;
2780 					int hour1=t1/r.nDaysPerWeek;
2781 					int duration1=r.internalActivitiesList[i].duration;
2782 
2783 					for(int j : qAsConst(l2)){
2784 						int t2=c.times[j];
2785 						if(t2!=UNALLOCATED_TIME){
2786 							int day2=t2%r.nDaysPerWeek;
2787 							int hour2=t2/r.nDaysPerWeek;
2788 							int duration2=r.internalActivitiesList[j].duration;
2789 
2790 							//the number of overlapping hours
2791 							int tt=0;
2792 							if(day1==day2){
2793 								int start=std::max(hour1, hour2);
2794 								int stop=std::min(hour1+duration1, hour2+duration2);
2795 								if(stop>start)
2796 									tt+=stop-start;
2797 							}
2798 
2799 							int tmp=tt;
2800 
2801 							nbroken+=tmp;
2802 
2803 							if(tt>0 && conflictsString!=nullptr){
2804 								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",
2805 								 "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
2806 								 .arg(r.internalActivitiesList[i].id)
2807 								 .arg(getActivityDetailedDescription(r, r.internalActivitiesList[i].id))
2808 								 .arg(r.internalActivitiesList[j].id)
2809 								 .arg(getActivityDetailedDescription(r, r.internalActivitiesList[j].id))
2810 								 .arg(tt);
2811 								s+=", ";
2812 								s+=tr("conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
2813 
2814 								dl.append(s);
2815 								cl.append(tmp*weightPercentage/100);
2816 
2817 								*conflictsString+= s+"\n";
2818 							}
2819 						}
2820 					}
2821 				}
2822 			}
2823 		}
2824 	}
2825 
2826 	if(weightPercentage==100)
2827 		assert(nbroken==0);
2828 	return weightPercentage/100 * nbroken;
2829 }
2830 
isRelatedToActivity(Rules & r,Activity * a)2831 bool ConstraintActivityTagsNotOverlapping::isRelatedToActivity(Rules& r, Activity* a)
2832 {
2833 	Q_UNUSED(r);
2834 
2835 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
2836 	QSet<QString> ats(activityTagsNames.begin(), activityTagsNames.end());
2837 	QSet<QString> aats(a->activityTagsNames.begin(), a->activityTagsNames.end());
2838 #else
2839 	QSet<QString> ats=activityTagsNames.toSet();
2840 	QSet<QString> aats=a->activityTagsNames.toSet();
2841 #endif
2842 	ats.intersect(aats);
2843 
2844 	if(ats.count()>0)
2845 		return true;
2846 
2847 	return false;
2848 }
2849 
isRelatedToTeacher(Teacher * t)2850 bool ConstraintActivityTagsNotOverlapping::isRelatedToTeacher(Teacher* t)
2851 {
2852 	Q_UNUSED(t);
2853 
2854 	return false;
2855 }
2856 
isRelatedToSubject(Subject * s)2857 bool ConstraintActivityTagsNotOverlapping::isRelatedToSubject(Subject* s)
2858 {
2859 	Q_UNUSED(s);
2860 
2861 	return false;
2862 }
2863 
isRelatedToActivityTag(ActivityTag * s)2864 bool ConstraintActivityTagsNotOverlapping::isRelatedToActivityTag(ActivityTag* s)
2865 {
2866 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
2867 	QSet<QString> ats(activityTagsNames.begin(), activityTagsNames.end());
2868 #else
2869 	QSet<QString> ats=activityTagsNames.toSet();
2870 #endif
2871 	if(ats.contains(s->name))
2872 		return true;
2873 
2874 	return false;
2875 }
2876 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)2877 bool ConstraintActivityTagsNotOverlapping::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
2878 {
2879 	Q_UNUSED(r);
2880 	Q_UNUSED(s);
2881 
2882 	return false;
2883 }
2884 
hasWrongDayOrHour(Rules & r)2885 bool ConstraintActivityTagsNotOverlapping::hasWrongDayOrHour(Rules& r)
2886 {
2887 	Q_UNUSED(r);
2888 	return false;
2889 }
2890 
canRepairWrongDayOrHour(Rules & r)2891 bool ConstraintActivityTagsNotOverlapping::canRepairWrongDayOrHour(Rules& r)
2892 {
2893 	Q_UNUSED(r);
2894 	assert(0);
2895 
2896 	return true;
2897 }
2898 
repairWrongDayOrHour(Rules & r)2899 bool ConstraintActivityTagsNotOverlapping::repairWrongDayOrHour(Rules& r)
2900 {
2901 	Q_UNUSED(r);
2902 	assert(0); //should check hasWrongDayOrHour, firstly
2903 
2904 	return true;
2905 }
2906 
2907 ////////////////////////////////////////////////////////////////////////////////////////////
2908 ////////////////////////////////////////////////////////////////////////////////////////////
2909 
ConstraintMinDaysBetweenActivities()2910 ConstraintMinDaysBetweenActivities::ConstraintMinDaysBetweenActivities()
2911 	: TimeConstraint()
2912 {
2913 	type=CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES;
2914 }
2915 
ConstraintMinDaysBetweenActivities(double wp,bool cisd,int nact,const QList<int> & act,int n)2916 ConstraintMinDaysBetweenActivities::ConstraintMinDaysBetweenActivities(double wp, bool cisd, int nact, const QList<int>& act, int n)
2917  : TimeConstraint(wp)
2918  {
2919 	this->consecutiveIfSameDay=cisd;
2920 
2921 	assert(nact>=2);
2922 	assert(act.count()==nact);
2923 	this->n_activities=nact;
2924 	this->activitiesId.clear();
2925 	for(int i=0; i<nact; i++)
2926 		this->activitiesId.append(act.at(i));
2927 
2928 	assert(n>0);
2929 	this->minDays=n;
2930 
2931 	this->type=CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES;
2932 }
2933 
operator ==(ConstraintMinDaysBetweenActivities & c)2934 bool ConstraintMinDaysBetweenActivities::operator==(ConstraintMinDaysBetweenActivities& c){
2935 	assert(this->n_activities==this->activitiesId.count());
2936 	assert(c.n_activities==c.activitiesId.count());
2937 
2938 	if(this->n_activities!=c.n_activities)
2939 		return false;
2940 	for(int i=0; i<this->n_activities; i++)
2941 		if(this->activitiesId[i]!=c.activitiesId[i])
2942 			return false;
2943 	if(this->minDays!=c.minDays)
2944 		return false;
2945 	if(this->weightPercentage!=c.weightPercentage)
2946 		return false;
2947 	if(this->consecutiveIfSameDay!=c.consecutiveIfSameDay)
2948 		return false;
2949 	return true;
2950 }
2951 
computeInternalStructure(QWidget * parent,Rules & r)2952 bool ConstraintMinDaysBetweenActivities::computeInternalStructure(QWidget* parent, Rules& r)
2953 {
2954 	//compute the indices of the activities,
2955 	//based on their unique ID
2956 
2957 	assert(this->n_activities==this->activitiesId.count());
2958 
2959 	this->_activities.clear();
2960 	for(int i=0; i<this->n_activities; i++){
2961 		int j=r.activitiesHash.value(activitiesId.at(i), -1);
2962 		//assert(j>=0);
2963 		if(j>=0)
2964 			_activities.append(j);
2965 		/*Activity* act;
2966 		for(j=0; j<r.nInternalActivities; j++){
2967 			act=&r.internalActivitiesList[j];
2968 			if(act->id==this->activitiesId[i]){
2969 				this->_activities.append(j);
2970 				break;
2971 			}
2972 		}*/
2973 	}
2974 	this->_n_activities=this->_activities.count();
2975 
2976 	if(this->_n_activities<=1){
2977 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
2978 			tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
2979 		//assert(0);
2980 		return false;
2981 	}
2982 
2983 	return true;
2984 }
2985 
removeUseless(Rules & r)2986 void ConstraintMinDaysBetweenActivities::removeUseless(Rules& r)
2987 {
2988 	//remove the activitiesId which no longer exist (used after the deletion of an activity)
2989 
2990 	assert(this->n_activities==this->activitiesId.count());
2991 
2992 	QList<int> tmpList;
2993 
2994 	for(int i=0; i<this->n_activities; i++){
2995 		Activity* act=r.activitiesPointerHash.value(activitiesId[i], nullptr);
2996 		if(act!=nullptr)
2997 			tmpList.append(act->id);
2998 		/*for(int k=0; k<r.activitiesList.size(); k++){
2999 			Activity* act=r.activitiesList[k];
3000 			if(act->id==this->activitiesId[i]){
3001 				tmpList.append(act->id);
3002 				break;
3003 			}
3004 		}*/
3005 	}
3006 
3007 	this->activitiesId=tmpList;
3008 	this->n_activities=this->activitiesId.count();
3009 
3010 	r.internalStructureComputed=false;
3011 }
3012 
hasInactiveActivities(Rules & r)3013 bool ConstraintMinDaysBetweenActivities::hasInactiveActivities(Rules& r)
3014 {
3015 	int count=0;
3016 
3017 	for(int i=0; i<this->n_activities; i++)
3018 		if(r.inactiveActivities.contains(this->activitiesId[i]))
3019 			count++;
3020 
3021 	if(this->n_activities-count<=1)
3022 		return true;
3023 	else
3024 		return false;
3025 }
3026 
getXmlDescription(Rules & r)3027 QString ConstraintMinDaysBetweenActivities::getXmlDescription(Rules& r)
3028 {
3029 	Q_UNUSED(r);
3030 
3031 	QString s="<ConstraintMinDaysBetweenActivities>\n";
3032 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
3033 	s+="	<Consecutive_If_Same_Day>";s+=trueFalse(this->consecutiveIfSameDay);s+="</Consecutive_If_Same_Day>\n";
3034 	s+="	<Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
3035 	for(int i=0; i<this->n_activities; i++)
3036 		s+="	<Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
3037 	s+="	<MinDays>"+CustomFETString::number(this->minDays)+"</MinDays>\n";
3038 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
3039 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
3040 	s+="</ConstraintMinDaysBetweenActivities>\n";
3041 	return s;
3042 }
3043 
getDescription(Rules & r)3044 QString ConstraintMinDaysBetweenActivities::getDescription(Rules& r)
3045 {
3046 	Q_UNUSED(r);
3047 
3048 	QString begin=QString("");
3049 	if(!active)
3050 		begin="X - ";
3051 
3052 	QString end=QString("");
3053 	if(!comments.isEmpty())
3054 		end=", "+tr("C: %1", "Comments").arg(comments);
3055 
3056 	QString s;
3057 	s+=tr("Min days between activities");s+=", ";
3058 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
3059 	s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
3060 	for(int i=0; i<this->n_activities; i++){
3061 		s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);s+=", ";
3062 	}
3063 	s+=tr("mD:%1", "Min days").arg(this->minDays);s+=", ";
3064 	s+=tr("CSD:%1", "Consecutive if same day").arg(yesNoTranslated(this->consecutiveIfSameDay));
3065 
3066 	return begin+s+end;
3067 }
3068 
getDetailedDescription(Rules & r)3069 QString ConstraintMinDaysBetweenActivities::getDetailedDescription(Rules& r)
3070 {
3071 	QString s=tr("Time constraint");s+="\n";
3072 	s+=tr("Minimum number of days between activities");s+="\n";
3073 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
3074 	s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
3075 	for(int i=0; i<this->n_activities; i++){
3076 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
3077 			.arg(this->activitiesId[i])
3078 			.arg(getActivityDetailedDescription(r, this->activitiesId[i]));
3079 		s+="\n";
3080 	}
3081 	s+=tr("Minimum number of days=%1").arg(this->minDays);s+="\n";
3082 	s+=tr("Consecutive if same day=%1").arg(yesNoTranslated(this->consecutiveIfSameDay));s+="\n";
3083 
3084 	if(!active){
3085 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
3086 		s+="\n";
3087 	}
3088 	if(!comments.isEmpty()){
3089 		s+=tr("Comments=%1").arg(comments);
3090 		s+="\n";
3091 	}
3092 
3093 	return s;
3094 }
3095 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)3096 double ConstraintMinDaysBetweenActivities::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
3097 {
3098 	assert(r.internalStructureComputed);
3099 
3100 	int nbroken;
3101 
3102 	//We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
3103 
3104 	//sum the overlapping hours for all pairs of activities.
3105 	//without logging
3106 	if(conflictsString==nullptr){
3107 		nbroken=0;
3108 		for(int i=1; i<this->_n_activities; i++){
3109 			int t1=c.times[this->_activities[i]];
3110 			if(t1!=UNALLOCATED_TIME){
3111 				int day1=t1%r.nDaysPerWeek;
3112 				int hour1=t1/r.nDaysPerWeek;
3113 				int duration1=r.internalActivitiesList[this->_activities[i]].duration;
3114 
3115 				for(int j=0; j<i; j++){
3116 					int t2=c.times[this->_activities[j]];
3117 					if(t2!=UNALLOCATED_TIME){
3118 						int day2=t2%r.nDaysPerWeek;
3119 						int hour2=t2/r.nDaysPerWeek;
3120 						int duration2=r.internalActivitiesList[this->_activities[j]].duration;
3121 
3122 						int tmp;
3123 						int tt=0;
3124 						int dist = r.mode==MORNINGS_AFTERNOONS ? abs(day1/2-day2/2) : abs(day1-day2);
3125 						if(dist<minDays){
3126 							tt=minDays-dist;
3127 
3128 							if(r.mode!=MORNINGS_AFTERNOONS){
3129 								if(this->consecutiveIfSameDay && day1==day2)
3130 									assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
3131 							}
3132 							else{
3133 								if(this->consecutiveIfSameDay)
3134 									assert( ( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) ) || (day1/2!=day2/2) );
3135 							}
3136 						}
3137 
3138 						tmp=tt;
3139 
3140 						nbroken+=tmp;
3141 					}
3142 				}
3143 			}
3144 		}
3145 	}
3146 	//with logging
3147 	else{
3148 		nbroken=0;
3149 		for(int i=1; i<this->_n_activities; i++){
3150 			int t1=c.times[this->_activities[i]];
3151 			if(t1!=UNALLOCATED_TIME){
3152 				int day1=t1%r.nDaysPerWeek;
3153 				int hour1=t1/r.nDaysPerWeek;
3154 				int duration1=r.internalActivitiesList[this->_activities[i]].duration;
3155 
3156 				for(int j=0; j<i; j++){
3157 					int t2=c.times[this->_activities[j]];
3158 					if(t2!=UNALLOCATED_TIME){
3159 						int day2=t2%r.nDaysPerWeek;
3160 						int hour2=t2/r.nDaysPerWeek;
3161 						int duration2=r.internalActivitiesList[this->_activities[j]].duration;
3162 
3163 						int tmp;
3164 						int tt=0;
3165 						int dist = r.mode==MORNINGS_AFTERNOONS ? abs(day1/2-day2/2) : abs(day1-day2);
3166 
3167 						if(dist<minDays){
3168 							tt=minDays-dist;
3169 
3170 							if(r.mode!=MORNINGS_AFTERNOONS){
3171 								if(this->consecutiveIfSameDay && day1==day2)
3172 									assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
3173 							}
3174 							else{
3175 								if(this->consecutiveIfSameDay)
3176 									assert( ( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) ) || (day1/2!=day2/2) );
3177 							}
3178 						}
3179 
3180 						tmp=tt;
3181 
3182 						nbroken+=tmp;
3183 
3184 						if(tt>0 && conflictsString!=nullptr){
3185 							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",
3186 							 "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr. Close here means near")
3187 							 .arg(this->activitiesId[i])
3188 							 .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
3189 							 .arg(this->activitiesId[j])
3190 							 .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
3191 							 .arg(tt)
3192 							 .arg(r.daysOfTheWeek[day1])
3193 							 .arg(r.daysOfTheWeek[day2]);
3194 							 ;
3195 
3196 							s+=", ";
3197 							s+=tr("conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
3198 							s+=".";
3199 
3200 							if(this->consecutiveIfSameDay && ((r.mode!=MORNINGS_AFTERNOONS && day1==day2) || (r.mode==MORNINGS_AFTERNOONS && day1/2==day2/2))){
3201 								s+=" ";
3202 								s+=tr("The activities are placed consecutively in the timetable, because you selected this option"
3203 								 " in case the activities are in the same day");
3204 							}
3205 
3206 							dl.append(s);
3207 							cl.append(tmp*weightPercentage/100);
3208 
3209 							*conflictsString+= s+"\n";
3210 						}
3211 					}
3212 				}
3213 			}
3214 		}
3215 	}
3216 
3217 	if(weightPercentage==100)
3218 		assert(nbroken==0);
3219 	return weightPercentage/100 * nbroken;
3220 }
3221 
isRelatedToActivity(Rules & r,Activity * a)3222 bool ConstraintMinDaysBetweenActivities::isRelatedToActivity(Rules& r, Activity* a)
3223 {
3224 	Q_UNUSED(r);
3225 
3226 	for(int i=0; i<this->n_activities; i++)
3227 		if(this->activitiesId[i]==a->id)
3228 			return true;
3229 	return false;
3230 }
3231 
isRelatedToTeacher(Teacher * t)3232 bool ConstraintMinDaysBetweenActivities::isRelatedToTeacher(Teacher* t)
3233 {
3234 	Q_UNUSED(t);
3235 
3236 	return false;
3237 }
3238 
isRelatedToSubject(Subject * s)3239 bool ConstraintMinDaysBetweenActivities::isRelatedToSubject(Subject* s)
3240 {
3241 	Q_UNUSED(s);
3242 
3243 	return false;
3244 }
3245 
isRelatedToActivityTag(ActivityTag * s)3246 bool ConstraintMinDaysBetweenActivities::isRelatedToActivityTag(ActivityTag* s)
3247 {
3248 	Q_UNUSED(s);
3249 
3250 	return false;
3251 }
3252 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)3253 bool ConstraintMinDaysBetweenActivities::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
3254 {
3255 	Q_UNUSED(r);
3256 	Q_UNUSED(s);
3257 
3258 	return false;
3259 }
3260 
hasWrongDayOrHour(Rules & r)3261 bool ConstraintMinDaysBetweenActivities::hasWrongDayOrHour(Rules& r)
3262 {
3263 	if(r.mode!=MORNINGS_AFTERNOONS){
3264 		if(minDays>=r.nDaysPerWeek)
3265 			return true;
3266 	}
3267 	else{
3268 		if(minDays>=r.nDaysPerWeek/2)
3269 			return true;
3270 	}
3271 
3272 	return false;
3273 }
3274 
canRepairWrongDayOrHour(Rules & r)3275 bool ConstraintMinDaysBetweenActivities::canRepairWrongDayOrHour(Rules& r)
3276 {
3277 	assert(hasWrongDayOrHour(r));
3278 
3279 	return true;
3280 }
3281 
repairWrongDayOrHour(Rules & r)3282 bool ConstraintMinDaysBetweenActivities::repairWrongDayOrHour(Rules& r)
3283 {
3284 	assert(hasWrongDayOrHour(r));
3285 
3286 	if(r.mode!=MORNINGS_AFTERNOONS){
3287 		if(minDays>=r.nDaysPerWeek)
3288 			minDays=r.nDaysPerWeek-1;
3289 	}
3290 	else{
3291 		if(minDays>=r.nDaysPerWeek/2)
3292 			minDays=r.nDaysPerWeek/2-1;
3293 	}
3294 
3295 	return true;
3296 }
3297 
3298 ////////////////////////////////////////////////////////////////////////////////////////////
3299 ////////////////////////////////////////////////////////////////////////////////////////////
3300 
ConstraintMaxDaysBetweenActivities()3301 ConstraintMaxDaysBetweenActivities::ConstraintMaxDaysBetweenActivities()
3302 	: TimeConstraint()
3303 {
3304 	type=CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES;
3305 }
3306 
ConstraintMaxDaysBetweenActivities(double wp,int nact,const QList<int> & act,int n)3307 ConstraintMaxDaysBetweenActivities::ConstraintMaxDaysBetweenActivities(double wp, int nact, const QList<int>& act, int n)
3308  : TimeConstraint(wp)
3309  {
3310   	assert(nact>=2);
3311   	assert(act.count()==nact);
3312 	this->n_activities=nact;
3313 	this->activitiesId.clear();
3314 	for(int i=0; i<nact; i++)
3315 		this->activitiesId.append(act.at(i));
3316 
3317 	assert(n>=0);
3318 	this->maxDays=n;
3319 
3320 	this->type=CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES;
3321 }
3322 
computeInternalStructure(QWidget * parent,Rules & r)3323 bool ConstraintMaxDaysBetweenActivities::computeInternalStructure(QWidget* parent, Rules& r)
3324 {
3325 	//compute the indices of the activities,
3326 	//based on their unique ID
3327 
3328 	assert(this->n_activities==this->activitiesId.count());
3329 
3330 	this->_activities.clear();
3331 	for(int i=0; i<this->n_activities; i++){
3332 		int j=r.activitiesHash.value(activitiesId.at(i), -1);
3333 		//assert(j>=0);
3334 		if(j>=0)
3335 			_activities.append(j);
3336 		/*int j;
3337 		Activity* act;
3338 		for(j=0; j<r.nInternalActivities; j++){
3339 			act=&r.internalActivitiesList[j];
3340 			if(act->id==this->activitiesId[i]){
3341 				this->_activities.append(j);
3342 				break;
3343 			}
3344 		}*/
3345 	}
3346 	this->_n_activities=this->_activities.count();
3347 
3348 	if(this->_n_activities<=1){
3349 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
3350 			tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
3351 		//assert(0);
3352 		return false;
3353 	}
3354 
3355 	return true;
3356 }
3357 
removeUseless(Rules & r)3358 void ConstraintMaxDaysBetweenActivities::removeUseless(Rules& r)
3359 {
3360 	//remove the activitiesId which no longer exist (used after the deletion of an activity)
3361 
3362 	assert(this->n_activities==this->activitiesId.count());
3363 
3364 	QList<int> tmpList;
3365 
3366 	for(int i=0; i<this->n_activities; i++){
3367 		Activity* act=r.activitiesPointerHash.value(activitiesId[i], nullptr);
3368 		if(act!=nullptr)
3369 			tmpList.append(act->id);
3370 		/*for(int k=0; k<r.activitiesList.size(); k++){
3371 			Activity* act=r.activitiesList[k];
3372 			if(act->id==this->activitiesId[i]){
3373 				tmpList.append(act->id);
3374 				break;
3375 			}
3376 		}*/
3377 	}
3378 
3379 	this->activitiesId=tmpList;
3380 	this->n_activities=this->activitiesId.count();
3381 
3382 	r.internalStructureComputed=false;
3383 }
3384 
hasInactiveActivities(Rules & r)3385 bool ConstraintMaxDaysBetweenActivities::hasInactiveActivities(Rules& r)
3386 {
3387 	int count=0;
3388 
3389 	for(int i=0; i<this->n_activities; i++)
3390 		if(r.inactiveActivities.contains(this->activitiesId[i]))
3391 			count++;
3392 
3393 	if(this->n_activities-count<=1)
3394 		return true;
3395 	else
3396 		return false;
3397 }
3398 
getXmlDescription(Rules & r)3399 QString ConstraintMaxDaysBetweenActivities::getXmlDescription(Rules& r)
3400 {
3401 	Q_UNUSED(r);
3402 
3403 	QString s="<ConstraintMaxDaysBetweenActivities>\n";
3404 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
3405 	s+="	<Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
3406 	for(int i=0; i<this->n_activities; i++)
3407 		s+="	<Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
3408 	s+="	<MaxDays>"+CustomFETString::number(this->maxDays)+"</MaxDays>\n";
3409 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
3410 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
3411 	s+="</ConstraintMaxDaysBetweenActivities>\n";
3412 	return s;
3413 }
3414 
getDescription(Rules & r)3415 QString ConstraintMaxDaysBetweenActivities::getDescription(Rules& r)
3416 {
3417 	Q_UNUSED(r);
3418 
3419 	QString begin=QString("");
3420 	if(!active)
3421 		begin="X - ";
3422 
3423 	QString end=QString("");
3424 	if(!comments.isEmpty())
3425 		end=", "+tr("C: %1", "Comments").arg(comments);
3426 
3427 	QString s;
3428 	s+=tr("Max days between activities");s+=", ";
3429 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
3430 	s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
3431 	for(int i=0; i<this->n_activities; i++){
3432 		s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);s+=", ";
3433 	}
3434 	s+=tr("MD:%1", "Abbreviation for maximum days").arg(this->maxDays);
3435 
3436 	return begin+s+end;
3437 }
3438 
getDetailedDescription(Rules & r)3439 QString ConstraintMaxDaysBetweenActivities::getDetailedDescription(Rules& r)
3440 {
3441 	QString s=tr("Time constraint");s+="\n";
3442 	s+=tr("Maximum number of days between activities");s+="\n";
3443 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
3444 	s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
3445 	for(int i=0; i<this->n_activities; i++){
3446 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
3447 			.arg(this->activitiesId[i])
3448 			.arg(getActivityDetailedDescription(r, this->activitiesId[i]));
3449 		s+="\n";
3450 	}
3451 	s+=tr("Maximum number of days=%1").arg(this->maxDays);s+="\n";
3452 
3453 	if(!active){
3454 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
3455 		s+="\n";
3456 	}
3457 	if(!comments.isEmpty()){
3458 		s+=tr("Comments=%1").arg(comments);
3459 		s+="\n";
3460 	}
3461 
3462 	return s;
3463 }
3464 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)3465 double ConstraintMaxDaysBetweenActivities::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
3466 {
3467 	assert(r.internalStructureComputed);
3468 
3469 	int nbroken;
3470 
3471 	//We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
3472 
3473 	//sum the overlapping hours for all pairs of activities.
3474 	//without logging
3475 	if(conflictsString==nullptr){
3476 		nbroken=0;
3477 		for(int i=1; i<this->_n_activities; i++){
3478 			int t1=c.times[this->_activities[i]];
3479 			if(t1!=UNALLOCATED_TIME){
3480 				int day1=t1%r.nDaysPerWeek;
3481 				//int hour1=t1/r.nDaysPerWeek;
3482 				//int duration1=r.internalActivitiesList[this->_activities[i]].duration;
3483 
3484 				for(int j=0; j<i; j++){
3485 					int t2=c.times[this->_activities[j]];
3486 					if(t2!=UNALLOCATED_TIME){
3487 						int day2=t2%r.nDaysPerWeek;
3488 						//int hour2=t2/r.nDaysPerWeek;
3489 						//int duration2=r.internalActivitiesList[this->_activities[j]].duration;
3490 
3491 						int tmp;
3492 						int tt=0;
3493 						int dist = r.mode==MORNINGS_AFTERNOONS ? abs(day1/2-day2/2) : abs(day1-day2);
3494 						if(dist>maxDays){
3495 							tt=dist-maxDays;
3496 
3497 							//if(this->consecutiveIfSameDay && day1==day2)
3498 							//	assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
3499 						}
3500 
3501 						tmp=tt;
3502 
3503 						nbroken+=tmp;
3504 					}
3505 				}
3506 			}
3507 		}
3508 	}
3509 	//with logging
3510 	else{
3511 		nbroken=0;
3512 		for(int i=1; i<this->_n_activities; i++){
3513 			int t1=c.times[this->_activities[i]];
3514 			if(t1!=UNALLOCATED_TIME){
3515 				int day1=t1%r.nDaysPerWeek;
3516 				//int hour1=t1/r.nDaysPerWeek;
3517 				//int duration1=r.internalActivitiesList[this->_activities[i]].duration;
3518 
3519 				for(int j=0; j<i; j++){
3520 					int t2=c.times[this->_activities[j]];
3521 					if(t2!=UNALLOCATED_TIME){
3522 						int day2=t2%r.nDaysPerWeek;
3523 						//int hour2=t2/r.nDaysPerWeek;
3524 						//int duration2=r.internalActivitiesList[this->_activities[j]].duration;
3525 
3526 						int tmp;
3527 						int tt=0;
3528 						int dist = r.mode==MORNINGS_AFTERNOONS ? abs(day1/2-day2/2) : abs(day1-day2);
3529 
3530 						if(dist>maxDays){
3531 							tt=dist-maxDays;
3532 
3533 							//if(this->consecutiveIfSameDay && day1==day2)
3534 							//	assert( day1==day2 && (hour1+duration1==hour2 || hour2+duration2==hour1) );
3535 						}
3536 
3537 						tmp=tt;
3538 
3539 						nbroken+=tmp;
3540 
3541 						if(tt>0 && conflictsString!=nullptr){
3542 							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"
3543 							 ", on days %6 and %7", "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
3544 							 .arg(this->activitiesId[i])
3545 							 .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
3546 							 .arg(this->activitiesId[j])
3547 							 .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
3548 							 .arg(tt)
3549 							 .arg(r.daysOfTheWeek[day1])
3550 							 .arg(r.daysOfTheWeek[day2]);
3551 
3552 							s+=", ";
3553 							s+=tr("conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
3554 							s+=".";
3555 
3556 							dl.append(s);
3557 							cl.append(tmp*weightPercentage/100);
3558 
3559 							*conflictsString+= s+"\n";
3560 						}
3561 					}
3562 				}
3563 			}
3564 		}
3565 	}
3566 
3567 	if(weightPercentage==100)
3568 		assert(nbroken==0);
3569 	return weightPercentage/100 * nbroken;
3570 }
3571 
isRelatedToActivity(Rules & r,Activity * a)3572 bool ConstraintMaxDaysBetweenActivities::isRelatedToActivity(Rules& r, Activity* a)
3573 {
3574 	Q_UNUSED(r);
3575 
3576 	for(int i=0; i<this->n_activities; i++)
3577 		if(this->activitiesId[i]==a->id)
3578 			return true;
3579 	return false;
3580 }
3581 
isRelatedToTeacher(Teacher * t)3582 bool ConstraintMaxDaysBetweenActivities::isRelatedToTeacher(Teacher* t)
3583 {
3584 	Q_UNUSED(t);
3585 
3586 	return false;
3587 }
3588 
isRelatedToSubject(Subject * s)3589 bool ConstraintMaxDaysBetweenActivities::isRelatedToSubject(Subject* s)
3590 {
3591 	Q_UNUSED(s);
3592 
3593 	return false;
3594 }
3595 
isRelatedToActivityTag(ActivityTag * s)3596 bool ConstraintMaxDaysBetweenActivities::isRelatedToActivityTag(ActivityTag* s)
3597 {
3598 	Q_UNUSED(s);
3599 
3600 	return false;
3601 }
3602 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)3603 bool ConstraintMaxDaysBetweenActivities::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
3604 {
3605 	Q_UNUSED(r);
3606 	Q_UNUSED(s);
3607 
3608 	return false;
3609 }
3610 
hasWrongDayOrHour(Rules & r)3611 bool ConstraintMaxDaysBetweenActivities::hasWrongDayOrHour(Rules& r)
3612 {
3613 	if(r.mode!=MORNINGS_AFTERNOONS){
3614 		if(maxDays>=r.nDaysPerWeek)
3615 			return true;
3616 	}
3617 	else{
3618 		if(maxDays>=r.nDaysPerWeek/2)
3619 			return true;
3620 	}
3621 
3622 	return false;
3623 }
3624 
canRepairWrongDayOrHour(Rules & r)3625 bool ConstraintMaxDaysBetweenActivities::canRepairWrongDayOrHour(Rules& r)
3626 {
3627 	assert(hasWrongDayOrHour(r));
3628 
3629 	return true;
3630 }
3631 
repairWrongDayOrHour(Rules & r)3632 bool ConstraintMaxDaysBetweenActivities::repairWrongDayOrHour(Rules& r)
3633 {
3634 	assert(hasWrongDayOrHour(r));
3635 
3636 	if(r.mode!=MORNINGS_AFTERNOONS){
3637 		if(maxDays>=r.nDaysPerWeek)
3638 			maxDays=r.nDaysPerWeek-1;
3639 	}
3640 	else{
3641 		if(maxDays>=r.nDaysPerWeek/2)
3642 			maxDays=r.nDaysPerWeek/2-1;
3643 	}
3644 
3645 	return true;
3646 }
3647 
3648 ////////////////////////////////////////////////////////////////////////////////////////////
3649 ////////////////////////////////////////////////////////////////////////////////////////////
3650 
ConstraintMinGapsBetweenActivities()3651 ConstraintMinGapsBetweenActivities::ConstraintMinGapsBetweenActivities()
3652 	: TimeConstraint()
3653 {
3654 	type=CONSTRAINT_MIN_GAPS_BETWEEN_ACTIVITIES;
3655 }
3656 
ConstraintMinGapsBetweenActivities(double wp,int nact,const QList<int> & actList,int ngaps)3657 ConstraintMinGapsBetweenActivities::ConstraintMinGapsBetweenActivities(double wp, int nact, const QList<int>& actList, int ngaps)
3658  : TimeConstraint(wp)
3659  {
3660 	this->n_activities=nact;
3661 	assert(nact==actList.count());
3662 	this->activitiesId.clear();
3663 	for(int i=0; i<nact; i++)
3664 		this->activitiesId.append(actList.at(i));
3665 
3666 	assert(ngaps>0);
3667 	this->minGaps=ngaps;
3668 
3669 	this->type=CONSTRAINT_MIN_GAPS_BETWEEN_ACTIVITIES;
3670 }
3671 
computeInternalStructure(QWidget * parent,Rules & r)3672 bool ConstraintMinGapsBetweenActivities::computeInternalStructure(QWidget* parent, Rules& r)
3673 {
3674 	//compute the indices of the activities,
3675 	//based on their unique ID
3676 
3677 	assert(this->n_activities==this->activitiesId.count());
3678 
3679 	this->_activities.clear();
3680 	for(int i=0; i<this->n_activities; i++){
3681 		int j=r.activitiesHash.value(activitiesId.at(i), -1);
3682 		//assert(j>=0);
3683 		if(j>=0)
3684 			_activities.append(j);
3685 		/*int j;
3686 		Activity* act;
3687 		for(j=0; j<r.nInternalActivities; j++){
3688 			act=&r.internalActivitiesList[j];
3689 			if(act->id==this->activitiesId[i]){
3690 				this->_activities.append(j);
3691 				break;
3692 			}
3693 		}*/
3694 	}
3695 	this->_n_activities=this->_activities.count();
3696 
3697 	if(this->_n_activities<=1){
3698 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
3699 			tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
3700 		//assert(0);
3701 		return false;
3702 	}
3703 
3704 	return true;
3705 }
3706 
removeUseless(Rules & r)3707 void ConstraintMinGapsBetweenActivities::removeUseless(Rules& r)
3708 {
3709 	//remove the activitiesId which no longer exist (used after the deletion of an activity)
3710 
3711 	assert(this->n_activities==this->activitiesId.count());
3712 
3713 	QList<int> tmpList;
3714 
3715 	for(int i=0; i<this->n_activities; i++){
3716 		Activity* act=r.activitiesPointerHash.value(activitiesId[i], nullptr);
3717 		if(act!=nullptr)
3718 			tmpList.append(act->id);
3719 		/*for(int k=0; k<r.activitiesList.size(); k++){
3720 			Activity* act=r.activitiesList[k];
3721 			if(act->id==this->activitiesId[i]){
3722 				tmpList.append(act->id);
3723 				break;
3724 			}
3725 		}*/
3726 	}
3727 
3728 	this->activitiesId=tmpList;
3729 	this->n_activities=this->activitiesId.count();
3730 
3731 	r.internalStructureComputed=false;
3732 }
3733 
hasInactiveActivities(Rules & r)3734 bool ConstraintMinGapsBetweenActivities::hasInactiveActivities(Rules& r)
3735 {
3736 	int count=0;
3737 
3738 	for(int i=0; i<this->n_activities; i++)
3739 		if(r.inactiveActivities.contains(this->activitiesId[i]))
3740 			count++;
3741 
3742 	if(this->n_activities-count<=1)
3743 		return true;
3744 	else
3745 		return false;
3746 }
3747 
getXmlDescription(Rules & r)3748 QString ConstraintMinGapsBetweenActivities::getXmlDescription(Rules& r)
3749 {
3750 	Q_UNUSED(r);
3751 
3752 	QString s="<ConstraintMinGapsBetweenActivities>\n";
3753 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
3754 	s+="	<Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
3755 	for(int i=0; i<this->n_activities; i++)
3756 		s+="	<Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
3757 	s+="	<MinGaps>"+CustomFETString::number(this->minGaps)+"</MinGaps>\n";
3758 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
3759 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
3760 	s+="</ConstraintMinGapsBetweenActivities>\n";
3761 	return s;
3762 }
3763 
getDescription(Rules & r)3764 QString ConstraintMinGapsBetweenActivities::getDescription(Rules& r)
3765 {
3766 	Q_UNUSED(r);
3767 
3768 	QString begin=QString("");
3769 	if(!active)
3770 		begin="X - ";
3771 
3772 	QString end=QString("");
3773 	if(!comments.isEmpty())
3774 		end=", "+tr("C: %1", "Comments").arg(comments);
3775 
3776 	QString s;
3777 	s+=tr("Min gaps between activities");s+=", ";
3778 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
3779 	s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
3780 	for(int i=0; i<this->n_activities; i++){
3781 		s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);s+=", ";
3782 	}
3783 	s+=tr("mG:%1", "Minimum number of gaps").arg(this->minGaps);
3784 
3785 	return begin+s+end;
3786 }
3787 
getDetailedDescription(Rules & r)3788 QString ConstraintMinGapsBetweenActivities::getDetailedDescription(Rules& r)
3789 {
3790 	QString s=tr("Time constraint");s+="\n";
3791 	s+=tr("Minimum gaps between activities (if activities on the same day)");s+="\n";
3792 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
3793 	s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
3794 	for(int i=0; i<this->n_activities; i++){
3795 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
3796 			.arg(this->activitiesId[i])
3797 			.arg(getActivityDetailedDescription(r, this->activitiesId[i]));
3798 		s+="\n";
3799 	}
3800 	s+=tr("Minimum number of gaps=%1").arg(this->minGaps);s+="\n";
3801 
3802 	if(!active){
3803 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
3804 		s+="\n";
3805 	}
3806 	if(!comments.isEmpty()){
3807 		s+=tr("Comments=%1").arg(comments);
3808 		s+="\n";
3809 	}
3810 
3811 	return s;
3812 }
3813 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)3814 double ConstraintMinGapsBetweenActivities::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
3815 {
3816 	assert(r.internalStructureComputed);
3817 
3818 	int nbroken;
3819 
3820 	//We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
3821 
3822 	nbroken=0;
3823 	for(int i=1; i<this->_n_activities; i++){
3824 		int t1=c.times[this->_activities[i]];
3825 		if(t1!=UNALLOCATED_TIME){
3826 			int day1=t1%r.nDaysPerWeek;
3827 			int hour1=t1/r.nDaysPerWeek;
3828 			int duration1=r.internalActivitiesList[this->_activities[i]].duration;
3829 
3830 			for(int j=0; j<i; j++){
3831 				int t2=c.times[this->_activities[j]];
3832 				if(t2!=UNALLOCATED_TIME){
3833 					int day2=t2%r.nDaysPerWeek;
3834 					int hour2=t2/r.nDaysPerWeek;
3835 					int duration2=r.internalActivitiesList[this->_activities[j]].duration;
3836 
3837 					int tmp;
3838 					int tt=0;
3839 					int dist=abs(day1-day2);
3840 
3841 					if(dist==0){ //same day
3842 						assert(day1==day2);
3843 						if(hour2>=hour1){
3844 							//assert(hour1+duration1<=hour2); not true for activities which are not incompatible
3845 							if(hour1+duration1+minGaps > hour2)
3846 								tt = (hour1+duration1+minGaps) - hour2;
3847 						}
3848 						else{
3849 							//assert(hour2+duration2<=hour1); not true for activities which are not incompatible
3850 							if(hour2+duration2+minGaps > hour1)
3851 								tt = (hour2+duration2+minGaps) - hour1;
3852 						}
3853 					}
3854 
3855 					tmp=tt;
3856 
3857 					nbroken+=tmp;
3858 
3859 					if(tt>0 && conflictsString!=nullptr){
3860 						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",
3861 							"%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
3862 						 .arg(this->activitiesId[i])
3863 						 .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
3864 						 .arg(this->activitiesId[j])
3865 						 .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
3866 						 .arg(r.daysOfTheWeek[day1])
3867 						 .arg(tt);
3868 
3869 						s+=", ";
3870 						s+=tr("conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
3871 						s+=".";
3872 
3873 						dl.append(s);
3874 						cl.append(tmp*weightPercentage/100);
3875 
3876 						*conflictsString+= s+"\n";
3877 					}
3878 				}
3879 			}
3880 		}
3881 	}
3882 
3883 	if(weightPercentage==100)
3884 		assert(nbroken==0);
3885 	return weightPercentage/100 * nbroken;
3886 }
3887 
isRelatedToActivity(Rules & r,Activity * a)3888 bool ConstraintMinGapsBetweenActivities::isRelatedToActivity(Rules& r, Activity* a)
3889 {
3890 	Q_UNUSED(r);
3891 
3892 	for(int i=0; i<this->n_activities; i++)
3893 		if(this->activitiesId[i]==a->id)
3894 			return true;
3895 	return false;
3896 }
3897 
isRelatedToTeacher(Teacher * t)3898 bool ConstraintMinGapsBetweenActivities::isRelatedToTeacher(Teacher* t)
3899 {
3900 	Q_UNUSED(t);
3901 
3902 	return false;
3903 }
3904 
isRelatedToSubject(Subject * s)3905 bool ConstraintMinGapsBetweenActivities::isRelatedToSubject(Subject* s)
3906 {
3907 	Q_UNUSED(s);
3908 
3909 	return false;
3910 }
3911 
isRelatedToActivityTag(ActivityTag * s)3912 bool ConstraintMinGapsBetweenActivities::isRelatedToActivityTag(ActivityTag* s)
3913 {
3914 	Q_UNUSED(s);
3915 
3916 	return false;
3917 }
3918 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)3919 bool ConstraintMinGapsBetweenActivities::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
3920 {
3921 	Q_UNUSED(r);
3922 	Q_UNUSED(s);
3923 
3924 	return false;
3925 }
3926 
hasWrongDayOrHour(Rules & r)3927 bool ConstraintMinGapsBetweenActivities::hasWrongDayOrHour(Rules& r)
3928 {
3929 	if(minGaps>r.nHoursPerDay)
3930 		return true;
3931 
3932 	return false;
3933 }
3934 
canRepairWrongDayOrHour(Rules & r)3935 bool ConstraintMinGapsBetweenActivities::canRepairWrongDayOrHour(Rules& r)
3936 {
3937 	assert(hasWrongDayOrHour(r));
3938 
3939 	return true;
3940 }
3941 
repairWrongDayOrHour(Rules & r)3942 bool ConstraintMinGapsBetweenActivities::repairWrongDayOrHour(Rules& r)
3943 {
3944 	assert(hasWrongDayOrHour(r));
3945 
3946 	if(minGaps>r.nHoursPerDay)
3947 		minGaps=r.nHoursPerDay;
3948 
3949 	return true;
3950 }
3951 
3952 ///////////////////////////////////////////////////////////////////////////////////////////
3953 ////////////////////////////////////////////////////////////////////////////////////////////
3954 
ConstraintMaxGapsBetweenActivities()3955 ConstraintMaxGapsBetweenActivities::ConstraintMaxGapsBetweenActivities()
3956 	: TimeConstraint()
3957 {
3958 	type=CONSTRAINT_MAX_GAPS_BETWEEN_ACTIVITIES;
3959 }
3960 
ConstraintMaxGapsBetweenActivities(double wp,int nact,const QList<int> & actList,int ngaps)3961 ConstraintMaxGapsBetweenActivities::ConstraintMaxGapsBetweenActivities(double wp, int nact, const QList<int>& actList, int ngaps)
3962  : TimeConstraint(wp)
3963  {
3964 	this->n_activities=nact;
3965 	assert(nact==actList.count());
3966 	this->activitiesId.clear();
3967 	for(int i=0; i<nact; i++)
3968 		this->activitiesId.append(actList.at(i));
3969 
3970 	assert(ngaps>0);
3971 	this->maxGaps=ngaps;
3972 
3973 	this->type=CONSTRAINT_MAX_GAPS_BETWEEN_ACTIVITIES;
3974 }
3975 
computeInternalStructure(QWidget * parent,Rules & r)3976 bool ConstraintMaxGapsBetweenActivities::computeInternalStructure(QWidget* parent, Rules& r)
3977 {
3978 	//compute the indices of the activities,
3979 	//based on their unique ID
3980 
3981 	assert(this->n_activities==this->activitiesId.count());
3982 
3983 	this->_activities.clear();
3984 	for(int i=0; i<this->n_activities; i++){
3985 		int j=r.activitiesHash.value(activitiesId.at(i), -1);
3986 		//assert(j>=0);
3987 		if(j>=0)
3988 			_activities.append(j);
3989 		/*int j;
3990 		Activity* act;
3991 		for(j=0; j<r.nInternalActivities; j++){
3992 			act=&r.internalActivitiesList[j];
3993 			if(act->id==this->activitiesId[i]){
3994 				this->_activities.append(j);
3995 				break;
3996 			}
3997 		}*/
3998 	}
3999 	this->_n_activities=this->_activities.count();
4000 
4001 	if(this->_n_activities<=1){
4002 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
4003 			tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
4004 		//assert(0);
4005 		return false;
4006 	}
4007 
4008 	return true;
4009 }
4010 
removeUseless(Rules & r)4011 void ConstraintMaxGapsBetweenActivities::removeUseless(Rules& r)
4012 {
4013 	//remove the activitiesId which no longer exist (used after the deletion of an activity)
4014 
4015 	assert(this->n_activities==this->activitiesId.count());
4016 
4017 	QList<int> tmpList;
4018 
4019 	for(int i=0; i<this->n_activities; i++){
4020 		Activity* act=r.activitiesPointerHash.value(activitiesId[i], nullptr);
4021 		if(act!=nullptr)
4022 			tmpList.append(act->id);
4023 		/*for(int k=0; k<r.activitiesList.size(); k++){
4024 			Activity* act=r.activitiesList[k];
4025 			if(act->id==this->activitiesId[i]){
4026 				tmpList.append(act->id);
4027 				break;
4028 			}
4029 		}*/
4030 	}
4031 
4032 	this->activitiesId=tmpList;
4033 	this->n_activities=this->activitiesId.count();
4034 
4035 	r.internalStructureComputed=false;
4036 }
4037 
hasInactiveActivities(Rules & r)4038 bool ConstraintMaxGapsBetweenActivities::hasInactiveActivities(Rules& r)
4039 {
4040 	int count=0;
4041 
4042 	for(int i=0; i<this->n_activities; i++)
4043 		if(r.inactiveActivities.contains(this->activitiesId[i]))
4044 			count++;
4045 
4046 	if(this->n_activities-count<=1)
4047 		return true;
4048 	else
4049 		return false;
4050 }
4051 
getXmlDescription(Rules & r)4052 QString ConstraintMaxGapsBetweenActivities::getXmlDescription(Rules& r){
4053 	Q_UNUSED(r);
4054 
4055 	QString s="<ConstraintMaxGapsBetweenActivities>\n";
4056 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
4057 	s+="	<Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
4058 	for(int i=0; i<this->n_activities; i++)
4059 		s+="	<Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
4060 	s+="	<MaxGaps>"+CustomFETString::number(this->maxGaps)+"</MaxGaps>\n";
4061 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
4062 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
4063 	s+="</ConstraintMaxGapsBetweenActivities>\n";
4064 	return s;
4065 }
4066 
getDescription(Rules & r)4067 QString ConstraintMaxGapsBetweenActivities::getDescription(Rules& r){
4068 	Q_UNUSED(r);
4069 
4070 	QString begin=QString("");
4071 	if(!active)
4072 		begin="X - ";
4073 
4074 	QString end=QString("");
4075 	if(!comments.isEmpty())
4076 		end=", "+tr("C: %1", "Comments").arg(comments);
4077 
4078 	QString s;
4079 	s+=tr("Max gaps between activities");s+=", ";
4080 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
4081 	s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
4082 	for(int i=0; i<this->n_activities; i++){
4083 		s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);s+=", ";
4084 	}
4085 	s+=tr("MG:%1", "Maximum number of gaps").arg(this->maxGaps);
4086 
4087 	return begin+s+end;
4088 }
4089 
getDetailedDescription(Rules & r)4090 QString ConstraintMaxGapsBetweenActivities::getDetailedDescription(Rules& r){
4091 	QString s=tr("Time constraint");s+="\n";
4092 	s+=tr("Maximum gaps between activities (if activities on the same day)");s+="\n";
4093 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
4094 	s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
4095 	for(int i=0; i<this->n_activities; i++){
4096 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
4097 			.arg(this->activitiesId[i])
4098 			.arg(getActivityDetailedDescription(r, this->activitiesId[i]));
4099 		s+="\n";
4100 	}
4101 	s+=tr("Maximum number of gaps=%1").arg(this->maxGaps);s+="\n";
4102 
4103 	if(!active){
4104 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
4105 		s+="\n";
4106 	}
4107 	if(!comments.isEmpty()){
4108 		s+=tr("Comments=%1").arg(comments);
4109 		s+="\n";
4110 	}
4111 
4112 	return s;
4113 }
4114 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)4115 double ConstraintMaxGapsBetweenActivities::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
4116 {
4117 	assert(r.internalStructureComputed);
4118 
4119 	int nbroken;
4120 
4121 	//We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
4122 
4123 	nbroken=0;
4124 	for(int i=1; i<this->_n_activities; i++){
4125 		int t1=c.times[this->_activities[i]];
4126 		if(t1!=UNALLOCATED_TIME){
4127 			int day1=t1%r.nDaysPerWeek;
4128 			int hour1=t1/r.nDaysPerWeek;
4129 			int duration1=r.internalActivitiesList[this->_activities[i]].duration;
4130 
4131 			for(int j=0; j<i; j++){
4132 				int t2=c.times[this->_activities[j]];
4133 				if(t2!=UNALLOCATED_TIME){
4134 					int day2=t2%r.nDaysPerWeek;
4135 					int hour2=t2/r.nDaysPerWeek;
4136 					int duration2=r.internalActivitiesList[this->_activities[j]].duration;
4137 
4138 					int tmp;
4139 					int tt=0;
4140 					int dist=abs(day1-day2);
4141 
4142 					if(dist==0){ //same day
4143 						assert(day1==day2);
4144 						if(hour2>=hour1){
4145 							//assert(hour1+duration1<=hour2); not true for activities which are not incompatible
4146 							if(hour1+duration1+maxGaps < hour2)
4147 								tt = - (hour1+duration1+maxGaps) + hour2;
4148 						}
4149 						else{
4150 							//assert(hour2+duration2<=hour1); not true for activities which are not incompatible
4151 							if(hour2+duration2+maxGaps < hour1)
4152 								tt = - (hour2+duration2+maxGaps) + hour1;
4153 						}
4154 					}
4155 
4156 					tmp=tt;
4157 
4158 					nbroken+=tmp;
4159 
4160 					if(tt>0 && conflictsString!=nullptr){
4161 						QString s=tr("Time constraint max gaps between activities broken: activity with id=%1 (%2) conflicts with activity with id=%3 (%4), they are on the same day %5 and there are %6 extra hours between them",
4162 							"%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
4163 						 .arg(this->activitiesId[i])
4164 						 .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
4165 						 .arg(this->activitiesId[j])
4166 						 .arg(getActivityDetailedDescription(r, this->activitiesId[j]))
4167 						 .arg(r.daysOfTheWeek[day1])
4168 						 .arg(tt);
4169 
4170 						s+=", ";
4171 						s+=tr("conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
4172 						s+=".";
4173 
4174 						dl.append(s);
4175 						cl.append(tmp*weightPercentage/100);
4176 
4177 						*conflictsString+= s+"\n";
4178 					}
4179 				}
4180 			}
4181 		}
4182 	}
4183 
4184 	if(weightPercentage==100)
4185 		assert(nbroken==0);
4186 	return weightPercentage/100 * nbroken;
4187 }
4188 
isRelatedToActivity(Rules & r,Activity * a)4189 bool ConstraintMaxGapsBetweenActivities::isRelatedToActivity(Rules& r, Activity* a)
4190 {
4191 	Q_UNUSED(r);
4192 
4193 	for(int i=0; i<this->n_activities; i++)
4194 		if(this->activitiesId[i]==a->id)
4195 			return true;
4196 	return false;
4197 }
4198 
isRelatedToTeacher(Teacher * t)4199 bool ConstraintMaxGapsBetweenActivities::isRelatedToTeacher(Teacher* t)
4200 {
4201 	Q_UNUSED(t);
4202 
4203 	return false;
4204 }
4205 
isRelatedToSubject(Subject * s)4206 bool ConstraintMaxGapsBetweenActivities::isRelatedToSubject(Subject* s)
4207 {
4208 	Q_UNUSED(s);
4209 
4210 	return false;
4211 }
4212 
isRelatedToActivityTag(ActivityTag * s)4213 bool ConstraintMaxGapsBetweenActivities::isRelatedToActivityTag(ActivityTag* s)
4214 {
4215 	Q_UNUSED(s);
4216 
4217 	return false;
4218 }
4219 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)4220 bool ConstraintMaxGapsBetweenActivities::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
4221 {
4222 	Q_UNUSED(r);
4223 	Q_UNUSED(s);
4224 
4225 	return false;
4226 }
4227 
hasWrongDayOrHour(Rules & r)4228 bool ConstraintMaxGapsBetweenActivities::hasWrongDayOrHour(Rules& r)
4229 {
4230 	if(maxGaps>r.nHoursPerDay)
4231 		return true;
4232 
4233 	return false;
4234 }
4235 
canRepairWrongDayOrHour(Rules & r)4236 bool ConstraintMaxGapsBetweenActivities::canRepairWrongDayOrHour(Rules& r)
4237 {
4238 	assert(hasWrongDayOrHour(r));
4239 
4240 	return true;
4241 }
4242 
repairWrongDayOrHour(Rules & r)4243 bool ConstraintMaxGapsBetweenActivities::repairWrongDayOrHour(Rules& r)
4244 {
4245 	assert(hasWrongDayOrHour(r));
4246 
4247 	if(maxGaps>r.nHoursPerDay)
4248 		maxGaps=r.nHoursPerDay;
4249 
4250 	return true;
4251 }
4252 
4253 ///////////////////////////////////////////////////////////////////////////////////////////
4254 ///////////////////////////////////////////////////////////////////////////////////////////
4255 
ConstraintTeachersMaxHoursDaily()4256 ConstraintTeachersMaxHoursDaily::ConstraintTeachersMaxHoursDaily()
4257 	: TimeConstraint()
4258 {
4259 	this->type=CONSTRAINT_TEACHERS_MAX_HOURS_DAILY;
4260 }
4261 
ConstraintTeachersMaxHoursDaily(double wp,int maxhours)4262 ConstraintTeachersMaxHoursDaily::ConstraintTeachersMaxHoursDaily(double wp, int maxhours)
4263  : TimeConstraint(wp)
4264  {
4265 	assert(maxhours>0);
4266 	this->maxHoursDaily=maxhours;
4267 
4268 	this->type=CONSTRAINT_TEACHERS_MAX_HOURS_DAILY;
4269 }
4270 
computeInternalStructure(QWidget * parent,Rules & r)4271 bool ConstraintTeachersMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
4272 {
4273 	Q_UNUSED(parent);
4274 	Q_UNUSED(r);
4275 
4276 	return true;
4277 }
4278 
hasInactiveActivities(Rules & r)4279 bool ConstraintTeachersMaxHoursDaily::hasInactiveActivities(Rules& r)
4280 {
4281 	Q_UNUSED(r);
4282 	return false;
4283 }
4284 
getXmlDescription(Rules & r)4285 QString ConstraintTeachersMaxHoursDaily::getXmlDescription(Rules& r)
4286 {
4287 	Q_UNUSED(r);
4288 
4289 	QString s="<ConstraintTeachersMaxHoursDaily>\n";
4290 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
4291 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
4292 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
4293 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
4294 	s+="</ConstraintTeachersMaxHoursDaily>\n";
4295 	return s;
4296 }
4297 
getDescription(Rules & r)4298 QString ConstraintTeachersMaxHoursDaily::getDescription(Rules& r)
4299 {
4300 	Q_UNUSED(r);
4301 
4302 	QString begin=QString("");
4303 	if(!active)
4304 		begin="X - ";
4305 
4306 	QString end=QString("");
4307 	if(!comments.isEmpty())
4308 		end=", "+tr("C: %1", "Comments").arg(comments);
4309 
4310 	QString s;
4311 	s+=tr("Teachers max hours daily"), s+=", ";
4312 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
4313 	s+=tr("MH:%1", "Maximum hours (daily)").arg(this->maxHoursDaily);
4314 
4315 	return begin+s+end;
4316 }
4317 
getDetailedDescription(Rules & r)4318 QString ConstraintTeachersMaxHoursDaily::getDetailedDescription(Rules& r)
4319 {
4320 	Q_UNUSED(r);
4321 
4322 	QString s=tr("Time constraint");s+="\n";
4323 	s+=tr("All teachers must respect the maximum number of hours daily");s+="\n";
4324 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
4325 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
4326 
4327 	if(!active){
4328 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
4329 		s+="\n";
4330 	}
4331 	if(!comments.isEmpty()){
4332 		s+=tr("Comments=%1").arg(comments);
4333 		s+="\n";
4334 	}
4335 
4336 	return s;
4337 }
4338 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)4339 double ConstraintTeachersMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
4340 {
4341 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
4342 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
4343 		c.teachersMatrixReady=true;
4344 		c.subgroupsMatrixReady=true;
4345 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
4346 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
4347 
4348 		c.changedForMatrixCalculation=false;
4349 	}
4350 
4351 	int nbroken;
4352 
4353 	//without logging
4354 	if(conflictsString==nullptr){
4355 		nbroken=0;
4356 		for(int i=0; i<r.nInternalTeachers; i++){
4357 			for(int d=0; d<r.nDaysPerWeek; d++){
4358 				int n_hours_daily=0;
4359 				for(int h=0; h<r.nHoursPerDay; h++)
4360 					if(teachersMatrix[i][d][h]>0)
4361 						n_hours_daily++;
4362 
4363 				if(n_hours_daily>this->maxHoursDaily)
4364 					nbroken++;
4365 			}
4366 		}
4367 	}
4368 	//with logging
4369 	else{
4370 		nbroken=0;
4371 		for(int i=0; i<r.nInternalTeachers; i++){
4372 			for(int d=0; d<r.nDaysPerWeek; d++){
4373 				int n_hours_daily=0;
4374 				for(int h=0; h<r.nHoursPerDay; h++)
4375 					if(teachersMatrix[i][d][h]>0)
4376 						n_hours_daily++;
4377 
4378 				if(n_hours_daily>this->maxHoursDaily){
4379 					nbroken++;
4380 
4381 					if(conflictsString!=nullptr){
4382 						QString s=(tr(
4383 						 "Time constraint teachers max %1 hours daily broken for teacher %2, on day %3, length=%4.")
4384 						 .arg(CustomFETString::number(this->maxHoursDaily))
4385 						 .arg(r.internalTeachersList[i]->name)
4386 						 .arg(r.daysOfTheWeek[d])
4387 						 .arg(n_hours_daily)
4388 						 )
4389 						 +
4390 						 " "
4391 						 +
4392 						 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
4393 
4394 						dl.append(s);
4395 						cl.append(weightPercentage/100);
4396 
4397 						*conflictsString+= s+"\n";
4398 					}
4399 				}
4400 			}
4401 		}
4402 	}
4403 
4404 	if(weightPercentage==100)
4405 		assert(nbroken==0);
4406 	return weightPercentage/100 * nbroken;
4407 }
4408 
isRelatedToActivity(Rules & r,Activity * a)4409 bool ConstraintTeachersMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
4410 {
4411 	Q_UNUSED(r);
4412 	Q_UNUSED(a);
4413 
4414 	return false;
4415 }
4416 
isRelatedToTeacher(Teacher * t)4417 bool ConstraintTeachersMaxHoursDaily::isRelatedToTeacher(Teacher* t)
4418 {
4419 	Q_UNUSED(t);
4420 
4421 	return true;
4422 }
4423 
isRelatedToSubject(Subject * s)4424 bool ConstraintTeachersMaxHoursDaily::isRelatedToSubject(Subject* s)
4425 {
4426 	Q_UNUSED(s);
4427 
4428 	return false;
4429 }
4430 
isRelatedToActivityTag(ActivityTag * s)4431 bool ConstraintTeachersMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
4432 {
4433 	Q_UNUSED(s);
4434 
4435 	return false;
4436 }
4437 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)4438 bool ConstraintTeachersMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
4439 {
4440 	Q_UNUSED(r);
4441 	Q_UNUSED(s);
4442 
4443 	return false;
4444 }
4445 
hasWrongDayOrHour(Rules & r)4446 bool ConstraintTeachersMaxHoursDaily::hasWrongDayOrHour(Rules& r)
4447 {
4448 	if(maxHoursDaily>r.nHoursPerDay)
4449 		return true;
4450 
4451 	return false;
4452 }
4453 
canRepairWrongDayOrHour(Rules & r)4454 bool ConstraintTeachersMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
4455 {
4456 	assert(hasWrongDayOrHour(r));
4457 
4458 	return true;
4459 }
4460 
repairWrongDayOrHour(Rules & r)4461 bool ConstraintTeachersMaxHoursDaily::repairWrongDayOrHour(Rules& r)
4462 {
4463 	assert(hasWrongDayOrHour(r));
4464 
4465 	if(maxHoursDaily>r.nHoursPerDay)
4466 		maxHoursDaily=r.nHoursPerDay;
4467 
4468 	return true;
4469 }
4470 
4471 ///////////////////////////////////////////////////////////////////////////////////////////
4472 ///////////////////////////////////////////////////////////////////////////////////////////
4473 
ConstraintTeacherMaxHoursDaily()4474 ConstraintTeacherMaxHoursDaily::ConstraintTeacherMaxHoursDaily()
4475 	: TimeConstraint()
4476 {
4477 	this->type=CONSTRAINT_TEACHER_MAX_HOURS_DAILY;
4478 }
4479 
ConstraintTeacherMaxHoursDaily(double wp,int maxhours,const QString & teacher)4480 ConstraintTeacherMaxHoursDaily::ConstraintTeacherMaxHoursDaily(double wp, int maxhours, const QString& teacher)
4481  : TimeConstraint(wp)
4482  {
4483 	assert(maxhours>0);
4484 	this->maxHoursDaily=maxhours;
4485 	this->teacherName=teacher;
4486 
4487 	this->type=CONSTRAINT_TEACHER_MAX_HOURS_DAILY;
4488 }
4489 
computeInternalStructure(QWidget * parent,Rules & r)4490 bool ConstraintTeacherMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
4491 {
4492 	Q_UNUSED(parent);
4493 
4494 	//this->teacher_ID=r.searchTeacher(this->teacherName);
4495 	teacher_ID=r.teachersHash.value(teacherName, -1);
4496 	assert(this->teacher_ID>=0);
4497 	return true;
4498 }
4499 
hasInactiveActivities(Rules & r)4500 bool ConstraintTeacherMaxHoursDaily::hasInactiveActivities(Rules& r)
4501 {
4502 	Q_UNUSED(r);
4503 	return false;
4504 }
4505 
getXmlDescription(Rules & r)4506 QString ConstraintTeacherMaxHoursDaily::getXmlDescription(Rules& r)
4507 {
4508 	Q_UNUSED(r);
4509 
4510 	QString s="<ConstraintTeacherMaxHoursDaily>\n";
4511 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
4512 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
4513 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
4514 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
4515 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
4516 	s+="</ConstraintTeacherMaxHoursDaily>\n";
4517 	return s;
4518 }
4519 
getDescription(Rules & r)4520 QString ConstraintTeacherMaxHoursDaily::getDescription(Rules& r)
4521 {
4522 	Q_UNUSED(r);
4523 
4524 	QString begin=QString("");
4525 	if(!active)
4526 		begin="X - ";
4527 
4528 	QString end=QString("");
4529 	if(!comments.isEmpty())
4530 		end=", "+tr("C: %1", "Comments").arg(comments);
4531 
4532 	QString s;
4533 	s+=tr("Teacher max hours daily");s+=", ";
4534 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
4535 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
4536 	s+=tr("MH:%1", "Maximum hours (daily)").arg(this->maxHoursDaily);
4537 
4538 	return begin+s+end;
4539 }
4540 
getDetailedDescription(Rules & r)4541 QString ConstraintTeacherMaxHoursDaily::getDetailedDescription(Rules& r)
4542 {
4543 	Q_UNUSED(r);
4544 
4545 	QString s=tr("Time constraint");s+="\n";
4546 	s+=tr("A teacher must respect the maximum number of hours daily");s+="\n";
4547 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
4548 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
4549 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
4550 
4551 	if(!active){
4552 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
4553 		s+="\n";
4554 	}
4555 	if(!comments.isEmpty()){
4556 		s+=tr("Comments=%1").arg(comments);
4557 		s+="\n";
4558 	}
4559 
4560 	return s;
4561 }
4562 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)4563 double ConstraintTeacherMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
4564 {
4565 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
4566 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
4567 		c.teachersMatrixReady=true;
4568 		c.subgroupsMatrixReady=true;
4569 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
4570 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
4571 
4572 		c.changedForMatrixCalculation=false;
4573 	}
4574 
4575 	int nbroken;
4576 
4577 	//without logging
4578 	if(conflictsString==nullptr){
4579 		nbroken=0;
4580 		int i=this->teacher_ID;
4581 		for(int d=0; d<r.nDaysPerWeek; d++){
4582 			int n_hours_daily=0;
4583 			for(int h=0; h<r.nHoursPerDay; h++)
4584 				if(teachersMatrix[i][d][h]>0)
4585 					n_hours_daily++;
4586 
4587 			if(n_hours_daily>this->maxHoursDaily){
4588 				nbroken++;
4589 			}
4590 		}
4591 	}
4592 	//with logging
4593 	else{
4594 		nbroken=0;
4595 		int i=this->teacher_ID;
4596 		for(int d=0; d<r.nDaysPerWeek; d++){
4597 			int n_hours_daily=0;
4598 			for(int h=0; h<r.nHoursPerDay; h++)
4599 				if(teachersMatrix[i][d][h]>0)
4600 					n_hours_daily++;
4601 
4602 			if(n_hours_daily>this->maxHoursDaily){
4603 				nbroken++;
4604 
4605 				if(conflictsString!=nullptr){
4606 					QString s=(tr(
4607 					 "Time constraint teacher max %1 hours daily broken for teacher %2, on day %3, length=%4.")
4608 					 .arg(CustomFETString::number(this->maxHoursDaily))
4609 					 .arg(r.internalTeachersList[i]->name)
4610 					 .arg(r.daysOfTheWeek[d])
4611 					 .arg(n_hours_daily)
4612 					 )
4613 					 +" "
4614 					 +
4615 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
4616 
4617 					dl.append(s);
4618 					cl.append(weightPercentage/100);
4619 
4620 					*conflictsString+= s+"\n";
4621 				}
4622 			}
4623 		}
4624 	}
4625 
4626 	if(weightPercentage==100)
4627 		assert(nbroken==0);
4628 	return weightPercentage/100 * nbroken;
4629 }
4630 
isRelatedToActivity(Rules & r,Activity * a)4631 bool ConstraintTeacherMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
4632 {
4633 	Q_UNUSED(r);
4634 	Q_UNUSED(a);
4635 
4636 	return false;
4637 }
4638 
isRelatedToTeacher(Teacher * t)4639 bool ConstraintTeacherMaxHoursDaily::isRelatedToTeacher(Teacher* t)
4640 {
4641 	if(this->teacherName==t->name)
4642 		return true;
4643 	return false;
4644 }
4645 
isRelatedToSubject(Subject * s)4646 bool ConstraintTeacherMaxHoursDaily::isRelatedToSubject(Subject* s)
4647 {
4648 	Q_UNUSED(s);
4649 
4650 	return false;
4651 }
4652 
isRelatedToActivityTag(ActivityTag * s)4653 bool ConstraintTeacherMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
4654 {
4655 	Q_UNUSED(s);
4656 
4657 	return false;
4658 }
4659 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)4660 bool ConstraintTeacherMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
4661 {
4662 	Q_UNUSED(r);
4663 	Q_UNUSED(s);
4664 
4665 	return false;
4666 }
4667 
hasWrongDayOrHour(Rules & r)4668 bool ConstraintTeacherMaxHoursDaily::hasWrongDayOrHour(Rules& r)
4669 {
4670 	if(maxHoursDaily>r.nHoursPerDay)
4671 		return true;
4672 
4673 	return false;
4674 }
4675 
canRepairWrongDayOrHour(Rules & r)4676 bool ConstraintTeacherMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
4677 {
4678 	assert(hasWrongDayOrHour(r));
4679 
4680 	return true;
4681 }
4682 
repairWrongDayOrHour(Rules & r)4683 bool ConstraintTeacherMaxHoursDaily::repairWrongDayOrHour(Rules& r)
4684 {
4685 	assert(hasWrongDayOrHour(r));
4686 
4687 	if(maxHoursDaily>r.nHoursPerDay)
4688 		maxHoursDaily=r.nHoursPerDay;
4689 
4690 	return true;
4691 }
4692 
4693 ///////////////////////////////////////////////////////////////////////////////////////////
4694 ///////////////////////////////////////////////////////////////////////////////////////////
4695 
ConstraintTeachersMaxHoursContinuously()4696 ConstraintTeachersMaxHoursContinuously::ConstraintTeachersMaxHoursContinuously()
4697 	: TimeConstraint()
4698 {
4699 	this->type=CONSTRAINT_TEACHERS_MAX_HOURS_CONTINUOUSLY;
4700 }
4701 
ConstraintTeachersMaxHoursContinuously(double wp,int maxhours)4702 ConstraintTeachersMaxHoursContinuously::ConstraintTeachersMaxHoursContinuously(double wp, int maxhours)
4703  : TimeConstraint(wp)
4704  {
4705 	assert(maxhours>0);
4706 	this->maxHoursContinuously=maxhours;
4707 
4708 	this->type=CONSTRAINT_TEACHERS_MAX_HOURS_CONTINUOUSLY;
4709 }
4710 
computeInternalStructure(QWidget * parent,Rules & r)4711 bool ConstraintTeachersMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
4712 {
4713 	Q_UNUSED(parent);
4714 	Q_UNUSED(r);
4715 
4716 	return true;
4717 }
4718 
hasInactiveActivities(Rules & r)4719 bool ConstraintTeachersMaxHoursContinuously::hasInactiveActivities(Rules& r)
4720 {
4721 	Q_UNUSED(r);
4722 	return false;
4723 }
4724 
getXmlDescription(Rules & r)4725 QString ConstraintTeachersMaxHoursContinuously::getXmlDescription(Rules& r)
4726 {
4727 	Q_UNUSED(r);
4728 
4729 	QString s="<ConstraintTeachersMaxHoursContinuously>\n";
4730 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
4731 	s+="	<Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
4732 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
4733 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
4734 	s+="</ConstraintTeachersMaxHoursContinuously>\n";
4735 	return s;
4736 }
4737 
getDescription(Rules & r)4738 QString ConstraintTeachersMaxHoursContinuously::getDescription(Rules& r)
4739 {
4740 	Q_UNUSED(r);
4741 
4742 	QString begin=QString("");
4743 	if(!active)
4744 		begin="X - ";
4745 
4746 	QString end=QString("");
4747 	if(!comments.isEmpty())
4748 		end=", "+tr("C: %1", "Comments").arg(comments);
4749 
4750 	QString s;
4751 	s+=tr("Teachers max hours continuously");s+=", ";
4752 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
4753 	s+=tr("MH:%1", "Maximum hours (continuously)").arg(this->maxHoursContinuously);
4754 
4755 	return begin+s+end;
4756 }
4757 
getDetailedDescription(Rules & r)4758 QString ConstraintTeachersMaxHoursContinuously::getDetailedDescription(Rules& r)
4759 {
4760 	Q_UNUSED(r);
4761 
4762 	QString s=tr("Time constraint");s+="\n";
4763 	s+=tr("All teachers must respect the maximum number of hours continuously");s+="\n";
4764 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
4765 	s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously);s+="\n";
4766 
4767 	if(!active){
4768 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
4769 		s+="\n";
4770 	}
4771 	if(!comments.isEmpty()){
4772 		s+=tr("Comments=%1").arg(comments);
4773 		s+="\n";
4774 	}
4775 
4776 	return s;
4777 }
4778 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)4779 double ConstraintTeachersMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
4780 {
4781 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
4782 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
4783 		c.teachersMatrixReady=true;
4784 		c.subgroupsMatrixReady=true;
4785 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
4786 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
4787 
4788 		c.changedForMatrixCalculation=false;
4789 	}
4790 
4791 	int nbroken;
4792 
4793 	nbroken=0;
4794 	for(int i=0; i<r.nInternalTeachers; i++){
4795 		for(int d=0; d<r.nDaysPerWeek; d++){
4796 			int nc=0;
4797 			for(int h=0; h<r.nHoursPerDay; h++){
4798 				if(teachersMatrix[i][d][h]>0)
4799 					nc++;
4800 				else{
4801 					if(nc>this->maxHoursContinuously){
4802 						nbroken++;
4803 
4804 						if(conflictsString!=nullptr){
4805 							QString s=(tr(
4806 							 "Time constraint teachers max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
4807 							 .arg(CustomFETString::number(this->maxHoursContinuously))
4808 							 .arg(r.internalTeachersList[i]->name)
4809 							 .arg(r.daysOfTheWeek[d])
4810 							 .arg(nc)
4811 							 )
4812 							 +
4813 							 " "
4814 							 +
4815 							 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
4816 
4817 							dl.append(s);
4818 							cl.append(weightPercentage/100);
4819 
4820 							*conflictsString+= s+"\n";
4821 						}
4822 					}
4823 
4824 					nc=0;
4825 				}
4826 			}
4827 
4828 			if(nc>this->maxHoursContinuously){
4829 				nbroken++;
4830 
4831 				if(conflictsString!=nullptr){
4832 					QString s=(tr(
4833 					 "Time constraint teachers max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
4834 					 .arg(CustomFETString::number(this->maxHoursContinuously))
4835 					 .arg(r.internalTeachersList[i]->name)
4836 					 .arg(r.daysOfTheWeek[d])
4837 					 .arg(nc)
4838 					 )
4839 					 +
4840 					 " "
4841 					 +
4842 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
4843 
4844 					dl.append(s);
4845 					cl.append(weightPercentage/100);
4846 
4847 					*conflictsString+= s+"\n";
4848 				}
4849 			}
4850 		}
4851 	}
4852 
4853 	if(weightPercentage==100)
4854 		assert(nbroken==0);
4855 	return weightPercentage/100 * nbroken;
4856 }
4857 
isRelatedToActivity(Rules & r,Activity * a)4858 bool ConstraintTeachersMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
4859 {
4860 	Q_UNUSED(r);
4861 	Q_UNUSED(a);
4862 
4863 	return false;
4864 }
4865 
isRelatedToTeacher(Teacher * t)4866 bool ConstraintTeachersMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
4867 {
4868 	Q_UNUSED(t);
4869 
4870 	return true;
4871 }
4872 
isRelatedToSubject(Subject * s)4873 bool ConstraintTeachersMaxHoursContinuously::isRelatedToSubject(Subject* s)
4874 {
4875 	Q_UNUSED(s);
4876 
4877 	return false;
4878 }
4879 
isRelatedToActivityTag(ActivityTag * s)4880 bool ConstraintTeachersMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
4881 {
4882 	Q_UNUSED(s);
4883 
4884 	return false;
4885 }
4886 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)4887 bool ConstraintTeachersMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
4888 {
4889 	Q_UNUSED(r);
4890 	Q_UNUSED(s);
4891 
4892 	return false;
4893 }
4894 
hasWrongDayOrHour(Rules & r)4895 bool ConstraintTeachersMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
4896 {
4897 	if(maxHoursContinuously>r.nHoursPerDay)
4898 		return true;
4899 
4900 	return false;
4901 }
4902 
canRepairWrongDayOrHour(Rules & r)4903 bool ConstraintTeachersMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
4904 {
4905 	assert(hasWrongDayOrHour(r));
4906 
4907 	return true;
4908 }
4909 
repairWrongDayOrHour(Rules & r)4910 bool ConstraintTeachersMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
4911 {
4912 	assert(hasWrongDayOrHour(r));
4913 
4914 	if(maxHoursContinuously>r.nHoursPerDay)
4915 		maxHoursContinuously=r.nHoursPerDay;
4916 
4917 	return true;
4918 }
4919 
4920 ///////////////////////////////////////////////////////////////////////////////////////////
4921 ///////////////////////////////////////////////////////////////////////////////////////////
4922 
ConstraintTeacherMaxHoursContinuously()4923 ConstraintTeacherMaxHoursContinuously::ConstraintTeacherMaxHoursContinuously()
4924 	: TimeConstraint()
4925 {
4926 	this->type=CONSTRAINT_TEACHER_MAX_HOURS_CONTINUOUSLY;
4927 }
4928 
ConstraintTeacherMaxHoursContinuously(double wp,int maxhours,const QString & teacher)4929 ConstraintTeacherMaxHoursContinuously::ConstraintTeacherMaxHoursContinuously(double wp, int maxhours, const QString& teacher)
4930  : TimeConstraint(wp)
4931  {
4932 	assert(maxhours>0);
4933 	this->maxHoursContinuously=maxhours;
4934 	this->teacherName=teacher;
4935 
4936 	this->type=CONSTRAINT_TEACHER_MAX_HOURS_CONTINUOUSLY;
4937 }
4938 
computeInternalStructure(QWidget * parent,Rules & r)4939 bool ConstraintTeacherMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
4940 {
4941 	Q_UNUSED(parent);
4942 
4943 	//this->teacher_ID=r.searchTeacher(this->teacherName);
4944 	teacher_ID=r.teachersHash.value(teacherName, -1);
4945 	assert(this->teacher_ID>=0);
4946 	return true;
4947 }
4948 
hasInactiveActivities(Rules & r)4949 bool ConstraintTeacherMaxHoursContinuously::hasInactiveActivities(Rules& r)
4950 {
4951 	Q_UNUSED(r);
4952 	return false;
4953 }
4954 
getXmlDescription(Rules & r)4955 QString ConstraintTeacherMaxHoursContinuously::getXmlDescription(Rules& r)
4956 {
4957 	Q_UNUSED(r);
4958 
4959 	QString s="<ConstraintTeacherMaxHoursContinuously>\n";
4960 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
4961 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
4962 	s+="	<Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
4963 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
4964 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
4965 	s+="</ConstraintTeacherMaxHoursContinuously>\n";
4966 	return s;
4967 }
4968 
getDescription(Rules & r)4969 QString ConstraintTeacherMaxHoursContinuously::getDescription(Rules& r)
4970 {
4971 	Q_UNUSED(r);
4972 
4973 	QString begin=QString("");
4974 	if(!active)
4975 		begin="X - ";
4976 
4977 	QString end=QString("");
4978 	if(!comments.isEmpty())
4979 		end=", "+tr("C: %1", "Comments").arg(comments);
4980 
4981 	QString s;
4982 	s+=tr("Teacher max hours continuously");s+=", ";
4983 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
4984 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
4985 	s+=tr("MH:%1", "Maximum hours continuously").arg(this->maxHoursContinuously);
4986 
4987 	return begin+s+end;
4988 }
4989 
getDetailedDescription(Rules & r)4990 QString ConstraintTeacherMaxHoursContinuously::getDetailedDescription(Rules& r)
4991 {
4992 	Q_UNUSED(r);
4993 
4994 	QString s=tr("Time constraint");s+="\n";
4995 	s+=tr("A teacher must respect the maximum number of hours continuously");s+="\n";
4996 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
4997 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
4998 	s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously);s+="\n";
4999 
5000 	if(!active){
5001 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
5002 		s+="\n";
5003 	}
5004 	if(!comments.isEmpty()){
5005 		s+=tr("Comments=%1").arg(comments);
5006 		s+="\n";
5007 	}
5008 
5009 	return s;
5010 }
5011 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)5012 double ConstraintTeacherMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
5013 {
5014 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
5015 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
5016 		c.teachersMatrixReady=true;
5017 		c.subgroupsMatrixReady=true;
5018 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
5019 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
5020 
5021 		c.changedForMatrixCalculation=false;
5022 	}
5023 
5024 	int nbroken;
5025 
5026 	nbroken=0;
5027 	int i=this->teacher_ID;
5028 	for(int d=0; d<r.nDaysPerWeek; d++){
5029 		int nc=0;
5030 		for(int h=0; h<r.nHoursPerDay; h++){
5031 			if(teachersMatrix[i][d][h]>0)
5032 				nc++;
5033 			else{
5034 				if(nc>this->maxHoursContinuously){
5035 					nbroken++;
5036 
5037 					if(conflictsString!=nullptr){
5038 						QString s=(tr(
5039 						 "Time constraint teacher max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
5040 						 .arg(CustomFETString::number(this->maxHoursContinuously))
5041 						 .arg(r.internalTeachersList[i]->name)
5042 						 .arg(r.daysOfTheWeek[d])
5043 						 .arg(nc)
5044 						 )
5045 						 +
5046 						 " "
5047 						 +
5048 						 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
5049 
5050 						dl.append(s);
5051 						cl.append(weightPercentage/100);
5052 
5053 						*conflictsString+= s+"\n";
5054 					}
5055 				}
5056 
5057 				nc=0;
5058 			}
5059 		}
5060 
5061 		if(nc>this->maxHoursContinuously){
5062 			nbroken++;
5063 
5064 			if(conflictsString!=nullptr){
5065 				QString s=(tr(
5066 				 "Time constraint teacher max %1 hours continuously broken for teacher %2, on day %3, length=%4.")
5067 				 .arg(CustomFETString::number(this->maxHoursContinuously))
5068 				 .arg(r.internalTeachersList[i]->name)
5069 				 .arg(r.daysOfTheWeek[d])
5070 				 .arg(nc)
5071 				 )
5072 				 +
5073 				 " "
5074 				 +
5075 				 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
5076 
5077 				dl.append(s);
5078 				cl.append(weightPercentage/100);
5079 
5080 				*conflictsString+= s+"\n";
5081 			}
5082 		}
5083 	}
5084 
5085 	if(weightPercentage==100)
5086 		assert(nbroken==0);
5087 	return weightPercentage/100 * nbroken;
5088 }
5089 
isRelatedToActivity(Rules & r,Activity * a)5090 bool ConstraintTeacherMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
5091 {
5092 	Q_UNUSED(r);
5093 	Q_UNUSED(a);
5094 
5095 	return false;
5096 }
5097 
isRelatedToTeacher(Teacher * t)5098 bool ConstraintTeacherMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
5099 {
5100 	if(this->teacherName==t->name)
5101 		return true;
5102 	return false;
5103 }
5104 
isRelatedToSubject(Subject * s)5105 bool ConstraintTeacherMaxHoursContinuously::isRelatedToSubject(Subject* s)
5106 {
5107 	Q_UNUSED(s);
5108 
5109 	return false;
5110 }
5111 
isRelatedToActivityTag(ActivityTag * s)5112 bool ConstraintTeacherMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
5113 {
5114 	Q_UNUSED(s);
5115 
5116 	return false;
5117 }
5118 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)5119 bool ConstraintTeacherMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
5120 {
5121 	Q_UNUSED(r);
5122 	Q_UNUSED(s);
5123 
5124 	return false;
5125 }
5126 
hasWrongDayOrHour(Rules & r)5127 bool ConstraintTeacherMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
5128 {
5129 	if(maxHoursContinuously>r.nHoursPerDay)
5130 		return true;
5131 
5132 	return false;
5133 }
5134 
canRepairWrongDayOrHour(Rules & r)5135 bool ConstraintTeacherMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
5136 {
5137 	assert(hasWrongDayOrHour(r));
5138 
5139 	return true;
5140 }
5141 
repairWrongDayOrHour(Rules & r)5142 bool ConstraintTeacherMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
5143 {
5144 	assert(hasWrongDayOrHour(r));
5145 
5146 	if(maxHoursContinuously>r.nHoursPerDay)
5147 		maxHoursContinuously=r.nHoursPerDay;
5148 
5149 	return true;
5150 }
5151 
5152 ///////////////////////////////////////////////////////////////////////////////////////////
5153 ///////////////////////////////////////////////////////////////////////////////////////////
5154 
ConstraintTeachersActivityTagMaxHoursContinuously()5155 ConstraintTeachersActivityTagMaxHoursContinuously::ConstraintTeachersActivityTagMaxHoursContinuously()
5156 	: TimeConstraint()
5157 {
5158 	this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
5159 }
5160 
ConstraintTeachersActivityTagMaxHoursContinuously(double wp,int maxhours,const QString & activityTag)5161 ConstraintTeachersActivityTagMaxHoursContinuously::ConstraintTeachersActivityTagMaxHoursContinuously(double wp, int maxhours, const QString& activityTag)
5162  : TimeConstraint(wp)
5163  {
5164 	assert(maxhours>0);
5165 	this->maxHoursContinuously=maxhours;
5166 	this->activityTagName=activityTag;
5167 
5168 	this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
5169 }
5170 
computeInternalStructure(QWidget * parent,Rules & r)5171 bool ConstraintTeachersActivityTagMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
5172 {
5173 	Q_UNUSED(parent);
5174 
5175 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
5176 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
5177 	assert(this->activityTagIndex>=0);
5178 
5179 	this->canonicalTeachersList.clear();
5180 	for(int i=0; i<r.nInternalTeachers; i++){
5181 		bool found=false;
5182 
5183 		Teacher* tch=r.internalTeachersList[i];
5184 		for(int actIndex : qAsConst(tch->activitiesForTeacher)){
5185 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
5186 				found=true;
5187 				break;
5188 			}
5189 		}
5190 
5191 		if(found)
5192 			this->canonicalTeachersList.append(i);
5193 	}
5194 
5195 	return true;
5196 }
5197 
hasInactiveActivities(Rules & r)5198 bool ConstraintTeachersActivityTagMaxHoursContinuously::hasInactiveActivities(Rules& r)
5199 {
5200 	Q_UNUSED(r);
5201 	return false;
5202 }
5203 
getXmlDescription(Rules & r)5204 QString ConstraintTeachersActivityTagMaxHoursContinuously::getXmlDescription(Rules& r)
5205 {
5206 	Q_UNUSED(r);
5207 
5208 	QString s="<ConstraintTeachersActivityTagMaxHoursContinuously>\n";
5209 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
5210 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
5211 	s+="	<Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
5212 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
5213 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
5214 	s+="</ConstraintTeachersActivityTagMaxHoursContinuously>\n";
5215 	return s;
5216 }
5217 
getDescription(Rules & r)5218 QString ConstraintTeachersActivityTagMaxHoursContinuously::getDescription(Rules& r)
5219 {
5220 	Q_UNUSED(r);
5221 
5222 	QString begin=QString("");
5223 	if(!active)
5224 		begin="X - ";
5225 
5226 	QString end=QString("");
5227 	if(!comments.isEmpty())
5228 		end=", "+tr("C: %1", "Comments").arg(comments);
5229 
5230 	QString s;
5231 	s+=tr("Teachers for activity tag %1 have max %2 hours continuously").arg(this->activityTagName).arg(this->maxHoursContinuously);s+=", ";
5232 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
5233 
5234 	return begin+s+end;
5235 }
5236 
getDetailedDescription(Rules & r)5237 QString ConstraintTeachersActivityTagMaxHoursContinuously::getDetailedDescription(Rules& r)
5238 {
5239 	Q_UNUSED(r);
5240 
5241 	QString s=tr("Time constraint");s+="\n";
5242 	s+=tr("All teachers, for an activity tag, must respect the maximum number of hours continuously");s+="\n";
5243 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
5244 	s+=tr("Activity tag=%1").arg(this->activityTagName); s+="\n";
5245 	s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously); s+="\n";
5246 
5247 	if(!active){
5248 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
5249 		s+="\n";
5250 	}
5251 	if(!comments.isEmpty()){
5252 		s+=tr("Comments=%1").arg(comments);
5253 		s+="\n";
5254 	}
5255 
5256 	return s;
5257 }
5258 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)5259 double ConstraintTeachersActivityTagMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
5260 {
5261 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
5262 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
5263 		c.teachersMatrixReady=true;
5264 		c.subgroupsMatrixReady=true;
5265 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
5266 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
5267 
5268 		c.changedForMatrixCalculation=false;
5269 	}
5270 
5271 	int nbroken;
5272 
5273 	nbroken=0;
5274 
5275 	Matrix2D<int> crtTeacherTimetableActivityTag;
5276 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
5277 
5278 	for(int i : qAsConst(this->canonicalTeachersList)){
5279 		Teacher* tch=r.internalTeachersList[i];
5280 		for(int d=0; d<r.nDaysPerWeek; d++)
5281 			for(int h=0; h<r.nHoursPerDay; h++)
5282 				crtTeacherTimetableActivityTag[d][h]=-1;
5283 		for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
5284 			int d=c.times[ai]%r.nDaysPerWeek;
5285 			int h=c.times[ai]/r.nDaysPerWeek;
5286 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
5287 				assert(h+dur<r.nHoursPerDay);
5288 				assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
5289 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
5290 					crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
5291 			}
5292 		}
5293 
5294 		for(int d=0; d<r.nDaysPerWeek; d++){
5295 			int nc=0;
5296 			for(int h=0; h<r.nHoursPerDay; h++){
5297 				bool inc=false;
5298 				if(crtTeacherTimetableActivityTag[d][h]==this->activityTagIndex)
5299 					inc=true;
5300 
5301 				if(inc){
5302 					nc++;
5303 				}
5304 				else{
5305 					if(nc>this->maxHoursContinuously){
5306 						nbroken++;
5307 
5308 						if(conflictsString!=nullptr){
5309 							QString s=(tr(
5310 							 "Time constraint teachers activity tag %1 max %2 hours continuously broken for teacher %3, on day %4, length=%5.")
5311 							 .arg(this->activityTagName)
5312 							 .arg(CustomFETString::number(this->maxHoursContinuously))
5313 							 .arg(r.internalTeachersList[i]->name)
5314 							 .arg(r.daysOfTheWeek[d])
5315 							 .arg(nc)
5316 							 )
5317 							 +
5318 							 " "
5319 							 +
5320 							 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
5321 
5322 							dl.append(s);
5323 							cl.append(weightPercentage/100);
5324 
5325 							*conflictsString+= s+"\n";
5326 						}
5327 					}
5328 
5329 					nc=0;
5330 				}
5331 			}
5332 
5333 			if(nc>this->maxHoursContinuously){
5334 				nbroken++;
5335 
5336 				if(conflictsString!=nullptr){
5337 					QString s=(tr(
5338 					 "Time constraint teachers activity tag %1 max %2 hours continuously broken for teacher %3, on day %4, length=%5.")
5339 					 .arg(this->activityTagName)
5340 					 .arg(CustomFETString::number(this->maxHoursContinuously))
5341 					 .arg(r.internalTeachersList[i]->name)
5342 					 .arg(r.daysOfTheWeek[d])
5343 					 .arg(nc)
5344 					 )
5345 					 +
5346 					 " "
5347 					 +
5348 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
5349 
5350 					dl.append(s);
5351 					cl.append(weightPercentage/100);
5352 
5353 					*conflictsString+= s+"\n";
5354 				}
5355 			}
5356 		}
5357 	}
5358 
5359 	if(weightPercentage==100)
5360 		assert(nbroken==0);
5361 	return weightPercentage/100 * nbroken;
5362 }
5363 
isRelatedToActivity(Rules & r,Activity * a)5364 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
5365 {
5366 	Q_UNUSED(r);
5367 	Q_UNUSED(a);
5368 
5369 	return false;
5370 }
5371 
isRelatedToTeacher(Teacher * t)5372 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
5373 {
5374 	Q_UNUSED(t);
5375 
5376 	return true;
5377 }
5378 
isRelatedToSubject(Subject * s)5379 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToSubject(Subject* s)
5380 {
5381 	Q_UNUSED(s);
5382 
5383 	return false;
5384 }
5385 
isRelatedToActivityTag(ActivityTag * s)5386 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
5387 {
5388 	return s->name==this->activityTagName;
5389 }
5390 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)5391 bool ConstraintTeachersActivityTagMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
5392 {
5393 	Q_UNUSED(r);
5394 	Q_UNUSED(s);
5395 
5396 	return false;
5397 }
5398 
hasWrongDayOrHour(Rules & r)5399 bool ConstraintTeachersActivityTagMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
5400 {
5401 	if(maxHoursContinuously>r.nHoursPerDay)
5402 		return true;
5403 
5404 	return false;
5405 }
5406 
canRepairWrongDayOrHour(Rules & r)5407 bool ConstraintTeachersActivityTagMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
5408 {
5409 	assert(hasWrongDayOrHour(r));
5410 
5411 	return true;
5412 }
5413 
repairWrongDayOrHour(Rules & r)5414 bool ConstraintTeachersActivityTagMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
5415 {
5416 	assert(hasWrongDayOrHour(r));
5417 
5418 	if(maxHoursContinuously>r.nHoursPerDay)
5419 		maxHoursContinuously=r.nHoursPerDay;
5420 
5421 	return true;
5422 }
5423 
5424 ///////////////////////////////////////////////////////////////////////////////////////////
5425 ///////////////////////////////////////////////////////////////////////////////////////////
ConstraintTeacherActivityTagMaxHoursContinuously()5426 ConstraintTeacherActivityTagMaxHoursContinuously::ConstraintTeacherActivityTagMaxHoursContinuously()
5427 	: TimeConstraint()
5428 {
5429 	this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
5430 }
5431 
ConstraintTeacherActivityTagMaxHoursContinuously(double wp,int maxhours,const QString & teacher,const QString & activityTag)5432 ConstraintTeacherActivityTagMaxHoursContinuously::ConstraintTeacherActivityTagMaxHoursContinuously(double wp, int maxhours, const QString& teacher, const QString& activityTag)
5433  : TimeConstraint(wp)
5434  {
5435 	assert(maxhours>0);
5436 	this->maxHoursContinuously=maxhours;
5437 	this->teacherName=teacher;
5438 	this->activityTagName=activityTag;
5439 
5440 	this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
5441 }
5442 
computeInternalStructure(QWidget * parent,Rules & r)5443 bool ConstraintTeacherActivityTagMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
5444 {
5445 	Q_UNUSED(parent);
5446 
5447 	//this->teacher_ID=r.searchTeacher(this->teacherName);
5448 	teacher_ID=r.teachersHash.value(teacherName, -1);
5449 	assert(this->teacher_ID>=0);
5450 
5451 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
5452 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
5453 	assert(this->activityTagIndex>=0);
5454 
5455 	this->canonicalTeachersList.clear();
5456 	int i=this->teacher_ID;
5457 	bool found=false;
5458 
5459 	Teacher* tch=r.internalTeachersList[i];
5460 	for(int actIndex : qAsConst(tch->activitiesForTeacher)){
5461 		if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
5462 			found=true;
5463 			break;
5464 		}
5465 	}
5466 
5467 	if(found)
5468 		this->canonicalTeachersList.append(i);
5469 
5470 	return true;
5471 }
5472 
hasInactiveActivities(Rules & r)5473 bool ConstraintTeacherActivityTagMaxHoursContinuously::hasInactiveActivities(Rules& r)
5474 {
5475 	Q_UNUSED(r);
5476 	return false;
5477 }
5478 
getXmlDescription(Rules & r)5479 QString ConstraintTeacherActivityTagMaxHoursContinuously::getXmlDescription(Rules& r)
5480 {
5481 	Q_UNUSED(r);
5482 
5483 	QString s="<ConstraintTeacherActivityTagMaxHoursContinuously>\n";
5484 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
5485 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
5486 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
5487 	s+="	<Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
5488 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
5489 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
5490 	s+="</ConstraintTeacherActivityTagMaxHoursContinuously>\n";
5491 	return s;
5492 }
5493 
getDescription(Rules & r)5494 QString ConstraintTeacherActivityTagMaxHoursContinuously::getDescription(Rules& r)
5495 {
5496 	Q_UNUSED(r);
5497 
5498 	QString begin=QString("");
5499 	if(!active)
5500 		begin="X - ";
5501 
5502 	QString end=QString("");
5503 	if(!comments.isEmpty())
5504 		end=", "+tr("C: %1", "Comments").arg(comments);
5505 
5506 	QString s;
5507 	s+=tr("Teacher %1 for activity tag %2 has max %3 hours continuously").arg(this->teacherName).arg(this->activityTagName).arg(this->maxHoursContinuously);s+=", ";
5508 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
5509 
5510 	return begin+s+end;
5511 }
5512 
getDetailedDescription(Rules & r)5513 QString ConstraintTeacherActivityTagMaxHoursContinuously::getDetailedDescription(Rules& r)
5514 {
5515 	Q_UNUSED(r);
5516 
5517 	QString s=tr("Time constraint");s+="\n";
5518 	s+=tr("A teacher for an activity tag must respect the maximum number of hours continuously");s+="\n";
5519 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
5520 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
5521 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
5522 	s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously); s+="\n";
5523 
5524 	if(!active){
5525 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
5526 		s+="\n";
5527 	}
5528 	if(!comments.isEmpty()){
5529 		s+=tr("Comments=%1").arg(comments);
5530 		s+="\n";
5531 	}
5532 
5533 	return s;
5534 }
5535 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)5536 double ConstraintTeacherActivityTagMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
5537 {
5538 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
5539 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
5540 		c.teachersMatrixReady=true;
5541 		c.subgroupsMatrixReady=true;
5542 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
5543 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
5544 
5545 		c.changedForMatrixCalculation=false;
5546 	}
5547 
5548 	int nbroken;
5549 
5550 	nbroken=0;
5551 
5552 	Matrix2D<int> crtTeacherTimetableActivityTag;
5553 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
5554 
5555 	for(int i : qAsConst(this->canonicalTeachersList)){
5556 		Teacher* tch=r.internalTeachersList[i];
5557 		for(int d=0; d<r.nDaysPerWeek; d++)
5558 			for(int h=0; h<r.nHoursPerDay; h++)
5559 				crtTeacherTimetableActivityTag[d][h]=-1;
5560 		for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
5561 			int d=c.times[ai]%r.nDaysPerWeek;
5562 			int h=c.times[ai]/r.nDaysPerWeek;
5563 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
5564 				assert(h+dur<r.nHoursPerDay);
5565 				assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
5566 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
5567 					crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
5568 			}
5569 		}
5570 
5571 		for(int d=0; d<r.nDaysPerWeek; d++){
5572 			int nc=0;
5573 			for(int h=0; h<r.nHoursPerDay; h++){
5574 				bool inc=false;
5575 
5576 				if(crtTeacherTimetableActivityTag[d][h]==this->activityTagIndex)
5577 					inc=true;
5578 
5579 				if(inc)
5580 					nc++;
5581 				else{
5582 					if(nc>this->maxHoursContinuously){
5583 						nbroken++;
5584 
5585 						if(conflictsString!=nullptr){
5586 							QString s=(tr(
5587 							 "Time constraint teacher activity tag max %1 hours continuously broken for teacher %2, activity tag %3, on day %4, length=%5.")
5588 							 .arg(CustomFETString::number(this->maxHoursContinuously))
5589 							 .arg(r.internalTeachersList[i]->name)
5590 							 .arg(this->activityTagName)
5591 							 .arg(r.daysOfTheWeek[d])
5592 							 .arg(nc)
5593 							 )
5594 							 +
5595 							 " "
5596 							 +
5597 							 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
5598 
5599 							dl.append(s);
5600 							cl.append(weightPercentage/100);
5601 
5602 							*conflictsString+= s+"\n";
5603 						}
5604 					}
5605 
5606 					nc=0;
5607 				}
5608 			}
5609 
5610 			if(nc>this->maxHoursContinuously){
5611 				nbroken++;
5612 
5613 				if(conflictsString!=nullptr){
5614 					QString s=(tr(
5615 					 "Time constraint teacher activity tag max %1 hours continuously broken for teacher %2, activity tag %3, on day %4, length=%5.")
5616 					 .arg(CustomFETString::number(this->maxHoursContinuously))
5617 					 .arg(r.internalTeachersList[i]->name)
5618 					 .arg(this->activityTagName)
5619 					 .arg(r.daysOfTheWeek[d])
5620 					 .arg(nc)
5621 					 )
5622 					 +
5623 					 " "
5624 					 +
5625 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
5626 
5627 					dl.append(s);
5628 					cl.append(weightPercentage/100);
5629 
5630 					*conflictsString+= s+"\n";
5631 				}
5632 			}
5633 		}
5634 	}
5635 
5636 	if(weightPercentage==100)
5637 		assert(nbroken==0);
5638 	return weightPercentage/100 * nbroken;
5639 }
5640 
isRelatedToActivity(Rules & r,Activity * a)5641 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
5642 {
5643 	Q_UNUSED(r);
5644 	Q_UNUSED(a);
5645 
5646 	return false;
5647 }
5648 
isRelatedToTeacher(Teacher * t)5649 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
5650 {
5651 	if(this->teacherName==t->name)
5652 		return true;
5653 	return false;
5654 }
5655 
isRelatedToSubject(Subject * s)5656 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToSubject(Subject* s)
5657 {
5658 	Q_UNUSED(s);
5659 
5660 	return false;
5661 }
5662 
isRelatedToActivityTag(ActivityTag * s)5663 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
5664 {
5665 	return this->activityTagName==s->name;
5666 }
5667 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)5668 bool ConstraintTeacherActivityTagMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
5669 {
5670 	Q_UNUSED(r);
5671 	Q_UNUSED(s);
5672 
5673 	return false;
5674 }
5675 
hasWrongDayOrHour(Rules & r)5676 bool ConstraintTeacherActivityTagMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
5677 {
5678 	if(maxHoursContinuously>r.nHoursPerDay)
5679 		return true;
5680 
5681 	return false;
5682 }
5683 
canRepairWrongDayOrHour(Rules & r)5684 bool ConstraintTeacherActivityTagMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
5685 {
5686 	assert(hasWrongDayOrHour(r));
5687 
5688 	return true;
5689 }
5690 
repairWrongDayOrHour(Rules & r)5691 bool ConstraintTeacherActivityTagMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
5692 {
5693 	assert(hasWrongDayOrHour(r));
5694 
5695 	if(maxHoursContinuously>r.nHoursPerDay)
5696 		maxHoursContinuously=r.nHoursPerDay;
5697 
5698 	return true;
5699 }
5700 
5701 ///////////////////////////////////////////////////////////////////////////////////////////
5702 ///////////////////////////////////////////////////////////////////////////////////////////
5703 
ConstraintTeacherMaxDaysPerWeek()5704 ConstraintTeacherMaxDaysPerWeek::ConstraintTeacherMaxDaysPerWeek()
5705 	: TimeConstraint()
5706 {
5707 	this->type=CONSTRAINT_TEACHER_MAX_DAYS_PER_WEEK;
5708 }
5709 
ConstraintTeacherMaxDaysPerWeek(double wp,int maxnd,const QString & tn)5710 ConstraintTeacherMaxDaysPerWeek::ConstraintTeacherMaxDaysPerWeek(double wp, int maxnd, const QString& tn)
5711 	 : TimeConstraint(wp)
5712 {
5713 	this->teacherName = tn;
5714 	this->maxDaysPerWeek=maxnd;
5715 	this->type=CONSTRAINT_TEACHER_MAX_DAYS_PER_WEEK;
5716 }
5717 
computeInternalStructure(QWidget * parent,Rules & r)5718 bool ConstraintTeacherMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
5719 {
5720 	Q_UNUSED(parent);
5721 
5722 	//this->teacher_ID=r.searchTeacher(this->teacherName);
5723 	teacher_ID=r.teachersHash.value(teacherName, -1);
5724 	assert(this->teacher_ID>=0);
5725 	return true;
5726 }
5727 
hasInactiveActivities(Rules & r)5728 bool ConstraintTeacherMaxDaysPerWeek::hasInactiveActivities(Rules& r)
5729 {
5730 	Q_UNUSED(r);
5731 	return false;
5732 }
5733 
getXmlDescription(Rules & r)5734 QString ConstraintTeacherMaxDaysPerWeek::getXmlDescription(Rules& r)
5735 {
5736 	Q_UNUSED(r);
5737 
5738 	QString s="<ConstraintTeacherMaxDaysPerWeek>\n";
5739 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
5740 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
5741 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
5742 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
5743 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
5744 	s+="</ConstraintTeacherMaxDaysPerWeek>\n";
5745 	return s;
5746 }
5747 
getDescription(Rules & r)5748 QString ConstraintTeacherMaxDaysPerWeek::getDescription(Rules& r)
5749 {
5750 	Q_UNUSED(r);
5751 
5752 	QString begin=QString("");
5753 	if(!active)
5754 		begin="X - ";
5755 
5756 	QString end=QString("");
5757 	if(!comments.isEmpty())
5758 		end=", "+tr("C: %1", "Comments").arg(comments);
5759 
5760 	QString s=tr("Teacher max days per week");s+=", ";
5761 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
5762 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
5763 	s+=tr("MD:%1", "Max days (per week)").arg(this->maxDaysPerWeek);
5764 
5765 	return begin+s+end;
5766 }
5767 
getDetailedDescription(Rules & r)5768 QString ConstraintTeacherMaxDaysPerWeek::getDetailedDescription(Rules& r)
5769 {
5770 	Q_UNUSED(r);
5771 
5772 	QString s=tr("Time constraint");s+="\n";
5773 	s+=tr("A teacher must respect the maximum number of days per week");s+="\n";
5774 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
5775 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
5776 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
5777 
5778 	if(!active){
5779 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
5780 		s+="\n";
5781 	}
5782 	if(!comments.isEmpty()){
5783 		s+=tr("Comments=%1").arg(comments);
5784 		s+="\n";
5785 	}
5786 
5787 	return s;
5788 }
5789 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)5790 double ConstraintTeacherMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
5791 {
5792 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
5793 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
5794 		c.teachersMatrixReady=true;
5795 		c.subgroupsMatrixReady=true;
5796 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
5797 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
5798 
5799 		c.changedForMatrixCalculation=false;
5800 	}
5801 
5802 	int nbroken;
5803 
5804 	Matrix1D<int> nd;
5805 	nd.resize(r.nHoursPerDay+1);
5806 
5807 	//without logging
5808 	if(conflictsString==nullptr){
5809 		nbroken=0;
5810 		//count sort
5811 		int t=this->teacher_ID;
5812 		for(int h=0; h<=r.nHoursPerDay; h++)
5813 			nd[h]=0;
5814 		for(int d=0; d<r.nDaysPerWeek; d++){
5815 			int nh=0;
5816 			for(int h=0; h<r.nHoursPerDay; h++)
5817 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
5818 			nd[nh]++;
5819 		}
5820 		//return the minimum occupied days which do not respect this constraint
5821 		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
5822 		for(int k=0; k<=r.nHoursPerDay; k++){
5823 			if(nd[k]>0){
5824 				if(i>nd[k]){
5825 					i-=nd[k];
5826 					nbroken+=nd[k]*k;
5827 				}
5828 				else{
5829 					nbroken+=i*k;
5830 					break;
5831 				}
5832 			}
5833 		}
5834 	}
5835 	//with logging
5836 	else{
5837 		nbroken=0;
5838 		//count sort
5839 		int t=this->teacher_ID;
5840 		for(int h=0; h<=r.nHoursPerDay; h++)
5841 			nd[h]=0;
5842 		for(int d=0; d<r.nDaysPerWeek; d++){
5843 			int nh=0;
5844 			for(int h=0; h<r.nHoursPerDay; h++)
5845 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
5846 			nd[nh]++;
5847 		}
5848 		//return the minimum occupied days which do not respect this constraint
5849 		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
5850 		for(int k=0; k<=r.nHoursPerDay; k++){
5851 			if(nd[k]>0){
5852 				if(i>nd[k]){
5853 					i-=nd[k];
5854 					nbroken+=nd[k]*k;
5855 				}
5856 				else{
5857 					nbroken+=i*k;
5858 					break;
5859 				}
5860 			}
5861 		}
5862 
5863 		if(nbroken>0){
5864 			QString s= tr("Time constraint teacher max days per week broken for teacher: %1.")
5865 			 .arg(r.internalTeachersList[t]->name);
5866 			s += tr("This increases the conflicts total by %1")
5867 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
5868 
5869 			dl.append(s);
5870 			cl.append(nbroken*weightPercentage/100);
5871 
5872 			*conflictsString += s+"\n";
5873 		}
5874 	}
5875 
5876 	if(weightPercentage==100)
5877 		assert(nbroken==0);
5878 	return weightPercentage/100 * nbroken;
5879 }
5880 
isRelatedToActivity(Rules & r,Activity * a)5881 bool ConstraintTeacherMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
5882 {
5883 	Q_UNUSED(r);
5884 	Q_UNUSED(a);
5885 
5886 	return false;
5887 }
5888 
isRelatedToTeacher(Teacher * t)5889 bool ConstraintTeacherMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
5890 {
5891 	if(this->teacherName==t->name)
5892 		return true;
5893 	return false;
5894 }
5895 
isRelatedToSubject(Subject * s)5896 bool ConstraintTeacherMaxDaysPerWeek::isRelatedToSubject(Subject* s)
5897 {
5898 	Q_UNUSED(s);
5899 
5900 	return false;
5901 }
5902 
isRelatedToActivityTag(ActivityTag * s)5903 bool ConstraintTeacherMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
5904 {
5905 	Q_UNUSED(s);
5906 
5907 	return false;
5908 }
5909 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)5910 bool ConstraintTeacherMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
5911 {
5912 	Q_UNUSED(r);
5913 	Q_UNUSED(s);
5914 
5915 	return false;
5916 }
5917 
hasWrongDayOrHour(Rules & r)5918 bool ConstraintTeacherMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
5919 {
5920 	if(maxDaysPerWeek>r.nDaysPerWeek)
5921 		return true;
5922 
5923 	return false;
5924 }
5925 
canRepairWrongDayOrHour(Rules & r)5926 bool ConstraintTeacherMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
5927 {
5928 	assert(hasWrongDayOrHour(r));
5929 
5930 	return true;
5931 }
5932 
repairWrongDayOrHour(Rules & r)5933 bool ConstraintTeacherMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
5934 {
5935 	assert(hasWrongDayOrHour(r));
5936 
5937 	if(maxDaysPerWeek>r.nDaysPerWeek)
5938 		maxDaysPerWeek=r.nDaysPerWeek;
5939 
5940 	return true;
5941 }
5942 
5943 ///////////////////////////////////////////////////////////////////////////////////////////
5944 ///////////////////////////////////////////////////////////////////////////////////////////
5945 
ConstraintTeachersMaxDaysPerWeek()5946 ConstraintTeachersMaxDaysPerWeek::ConstraintTeachersMaxDaysPerWeek()
5947 	: TimeConstraint()
5948 {
5949 	this->type=CONSTRAINT_TEACHERS_MAX_DAYS_PER_WEEK;
5950 }
5951 
ConstraintTeachersMaxDaysPerWeek(double wp,int maxnd)5952 ConstraintTeachersMaxDaysPerWeek::ConstraintTeachersMaxDaysPerWeek(double wp, int maxnd)
5953 	 : TimeConstraint(wp)
5954 {
5955 	this->maxDaysPerWeek=maxnd;
5956 	this->type=CONSTRAINT_TEACHERS_MAX_DAYS_PER_WEEK;
5957 }
5958 
computeInternalStructure(QWidget * parent,Rules & r)5959 bool ConstraintTeachersMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
5960 {
5961 	Q_UNUSED(parent);
5962 	Q_UNUSED(r);
5963 
5964 	return true;
5965 }
5966 
hasInactiveActivities(Rules & r)5967 bool ConstraintTeachersMaxDaysPerWeek::hasInactiveActivities(Rules& r)
5968 {
5969 	Q_UNUSED(r);
5970 	return false;
5971 }
5972 
getXmlDescription(Rules & r)5973 QString ConstraintTeachersMaxDaysPerWeek::getXmlDescription(Rules& r)
5974 {
5975 	Q_UNUSED(r);
5976 
5977 	QString s="<ConstraintTeachersMaxDaysPerWeek>\n";
5978 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
5979 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
5980 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
5981 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
5982 	s+="</ConstraintTeachersMaxDaysPerWeek>\n";
5983 	return s;
5984 }
5985 
getDescription(Rules & r)5986 QString ConstraintTeachersMaxDaysPerWeek::getDescription(Rules& r)
5987 {
5988 	Q_UNUSED(r);
5989 
5990 	QString begin=QString("");
5991 	if(!active)
5992 		begin="X - ";
5993 
5994 	QString end=QString("");
5995 	if(!comments.isEmpty())
5996 		end=", "+tr("C: %1", "Comments").arg(comments);
5997 
5998 	QString s=tr("Teachers max days per week");s+=", ";
5999 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
6000 	s+=tr("MD:%1", "Max days (per week)").arg(this->maxDaysPerWeek);
6001 
6002 	return begin+s+end;
6003 }
6004 
getDetailedDescription(Rules & r)6005 QString ConstraintTeachersMaxDaysPerWeek::getDetailedDescription(Rules& r)
6006 {
6007 	Q_UNUSED(r);
6008 
6009 	QString s=tr("Time constraint");s+="\n";
6010 	s+=tr("All teachers must respect the maximum number of days per week");s+="\n";
6011 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
6012 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
6013 
6014 	if(!active){
6015 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
6016 		s+="\n";
6017 	}
6018 	if(!comments.isEmpty()){
6019 		s+=tr("Comments=%1").arg(comments);
6020 		s+="\n";
6021 	}
6022 
6023 	return s;
6024 }
6025 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)6026 double ConstraintTeachersMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
6027 {
6028 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
6029 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
6030 		c.teachersMatrixReady=true;
6031 		c.subgroupsMatrixReady=true;
6032 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
6033 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
6034 
6035 		c.changedForMatrixCalculation=false;
6036 	}
6037 
6038 	int nbroken;
6039 
6040 	Matrix1D<int> nd;
6041 	nd.resize(r.nHoursPerDay+1);
6042 
6043 	//without logging
6044 	if(conflictsString==nullptr){
6045 		nbroken=0;
6046 		//count sort
6047 
6048 		for(int t=0; t<r.nInternalTeachers; t++){
6049 			for(int h=0; h<=r.nHoursPerDay; h++)
6050 				nd[h]=0;
6051 			for(int d=0; d<r.nDaysPerWeek; d++){
6052 				int nh=0;
6053 				for(int h=0; h<r.nHoursPerDay; h++)
6054 					nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
6055 				nd[nh]++;
6056 			}
6057 			//return the minimum occupied days which do not respect this constraint
6058 			int i = r.nDaysPerWeek - this->maxDaysPerWeek;
6059 			for(int k=0; k<=r.nHoursPerDay; k++){
6060 				if(nd[k]>0){
6061 					if(i>nd[k]){
6062 						i-=nd[k];
6063 						nbroken+=nd[k]*k;
6064 					}
6065 					else{
6066 						nbroken+=i*k;
6067 						break;
6068 					}
6069 				}
6070 			}
6071 
6072 		}
6073 	}
6074 	//with logging
6075 	else{
6076 		nbroken=0;
6077 
6078 		for(int t=0; t<r.nInternalTeachers; t++){
6079 			int nbr=0;
6080 
6081 			//count sort
6082 			for(int h=0; h<=r.nHoursPerDay; h++)
6083 				nd[h]=0;
6084 			for(int d=0; d<r.nDaysPerWeek; d++){
6085 				int nh=0;
6086 				for(int h=0; h<r.nHoursPerDay; h++)
6087 					nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
6088 				nd[nh]++;
6089 			}
6090 			//return the minimum occupied days which do not respect this constraint
6091 			int i = r.nDaysPerWeek - this->maxDaysPerWeek;
6092 			for(int k=0; k<=r.nHoursPerDay; k++){
6093 				if(nd[k]>0){
6094 					if(i>nd[k]){
6095 						i-=nd[k];
6096 						nbroken+=nd[k]*k;
6097 						nbr+=nd[k]*k;
6098 					}
6099 					else{
6100 						nbroken+=i*k;
6101 						nbr+=i*k;
6102 						break;
6103 					}
6104 				}
6105 			}
6106 
6107 			if(nbr>0){
6108 				QString s= tr("Time constraint teachers max days per week broken for teacher: %1.")
6109 				.arg(r.internalTeachersList[t]->name);
6110 				s += tr("This increases the conflicts total by %1")
6111 				.arg(CustomFETString::numberPlusTwoDigitsPrecision(nbr*weightPercentage/100));
6112 
6113 				dl.append(s);
6114 				cl.append(nbr*weightPercentage/100);
6115 
6116 				*conflictsString += s+"\n";
6117 			}
6118 
6119 		}
6120 
6121 	}
6122 
6123 	if(weightPercentage==100)
6124 		assert(nbroken==0);
6125 	return weightPercentage/100 * nbroken;
6126 }
6127 
isRelatedToActivity(Rules & r,Activity * a)6128 bool ConstraintTeachersMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
6129 {
6130 	Q_UNUSED(r);
6131 	Q_UNUSED(a);
6132 
6133 	return false;
6134 }
6135 
isRelatedToTeacher(Teacher * t)6136 bool ConstraintTeachersMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
6137 {
6138 	Q_UNUSED(t);
6139 
6140 	return true;
6141 }
6142 
isRelatedToSubject(Subject * s)6143 bool ConstraintTeachersMaxDaysPerWeek::isRelatedToSubject(Subject* s)
6144 {
6145 	Q_UNUSED(s);
6146 
6147 	return false;
6148 }
6149 
isRelatedToActivityTag(ActivityTag * s)6150 bool ConstraintTeachersMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
6151 {
6152 	Q_UNUSED(s);
6153 
6154 	return false;
6155 }
6156 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)6157 bool ConstraintTeachersMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
6158 {
6159 	Q_UNUSED(r);
6160 	Q_UNUSED(s);
6161 
6162 	return false;
6163 }
6164 
hasWrongDayOrHour(Rules & r)6165 bool ConstraintTeachersMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
6166 {
6167 	if(maxDaysPerWeek>r.nDaysPerWeek)
6168 		return true;
6169 
6170 	return false;
6171 }
6172 
canRepairWrongDayOrHour(Rules & r)6173 bool ConstraintTeachersMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
6174 {
6175 	assert(hasWrongDayOrHour(r));
6176 
6177 	return true;
6178 }
6179 
repairWrongDayOrHour(Rules & r)6180 bool ConstraintTeachersMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
6181 {
6182 	assert(hasWrongDayOrHour(r));
6183 
6184 	if(maxDaysPerWeek>r.nDaysPerWeek)
6185 		maxDaysPerWeek=r.nDaysPerWeek;
6186 
6187 	return true;
6188 }
6189 
6190 ////////////////////////////////////////////////////////////////////////////////////////////
6191 ////////////////////////////////////////////////////////////////////////////////////////////
6192 
ConstraintTeachersMaxGapsPerWeek()6193 ConstraintTeachersMaxGapsPerWeek::ConstraintTeachersMaxGapsPerWeek()
6194 	: TimeConstraint()
6195 {
6196 	this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_WEEK;
6197 }
6198 
ConstraintTeachersMaxGapsPerWeek(double wp,int mg)6199 ConstraintTeachersMaxGapsPerWeek::ConstraintTeachersMaxGapsPerWeek(double wp, int mg)
6200 	: TimeConstraint(wp)
6201 {
6202 	this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_WEEK;
6203 	this->maxGaps=mg;
6204 }
6205 
computeInternalStructure(QWidget * parent,Rules & r)6206 bool ConstraintTeachersMaxGapsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
6207 {
6208 	Q_UNUSED(parent);
6209 	Q_UNUSED(r);
6210 
6211 	return true;
6212 }
6213 
hasInactiveActivities(Rules & r)6214 bool ConstraintTeachersMaxGapsPerWeek::hasInactiveActivities(Rules& r)
6215 {
6216 	Q_UNUSED(r);
6217 	return false;
6218 }
6219 
getXmlDescription(Rules & r)6220 QString ConstraintTeachersMaxGapsPerWeek::getXmlDescription(Rules& r)
6221 {
6222 	Q_UNUSED(r);
6223 
6224 	QString s="<ConstraintTeachersMaxGapsPerWeek>\n";
6225 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
6226 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
6227 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
6228 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
6229 	s+="</ConstraintTeachersMaxGapsPerWeek>\n";
6230 	return s;
6231 }
6232 
getDescription(Rules & r)6233 QString ConstraintTeachersMaxGapsPerWeek::getDescription(Rules& r)
6234 {
6235 	Q_UNUSED(r);
6236 
6237 	QString begin=QString("");
6238 	if(!active)
6239 		begin="X - ";
6240 
6241 	QString end=QString("");
6242 	if(!comments.isEmpty())
6243 		end=", "+tr("C: %1", "Comments").arg(comments);
6244 
6245 	QString s;
6246 	s+=tr("Teachers max gaps per week");s+=", ";
6247 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
6248 	s+=tr("MG:%1", "Max gaps (per week)").arg(this->maxGaps);
6249 
6250 	return begin+s+end;
6251 }
6252 
getDetailedDescription(Rules & r)6253 QString ConstraintTeachersMaxGapsPerWeek::getDetailedDescription(Rules& r)
6254 {
6255 	Q_UNUSED(r);
6256 
6257 	QString s=tr("Time constraint");s+="\n";
6258 	s+=tr("All teachers must respect the maximum number of gaps per week");s+="\n";
6259 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
6260 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
6261 	s+=tr("Maximum gaps per week=%1").arg(this->maxGaps); s+="\n";
6262 
6263 	if(!active){
6264 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
6265 		s+="\n";
6266 	}
6267 	if(!comments.isEmpty()){
6268 		s+=tr("Comments=%1").arg(comments);
6269 		s+="\n";
6270 	}
6271 
6272 	return s;
6273 }
6274 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)6275 double ConstraintTeachersMaxGapsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
6276 {
6277 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
6278 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
6279 		c.teachersMatrixReady=true;
6280 		c.subgroupsMatrixReady=true;
6281 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
6282 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
6283 
6284 		c.changedForMatrixCalculation=false;
6285 	}
6286 
6287 	int tg;
6288 	int i, j, k;
6289 	int totalGaps;
6290 
6291 	totalGaps=0;
6292 	for(i=0; i<r.nInternalTeachers; i++){
6293 		tg=0;
6294 		for(j=0; j<r.nDaysPerWeek; j++){
6295 			for(k=0; k<r.nHoursPerDay; k++)
6296 				if(teachersMatrix[i][j][k]>0){
6297 					assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
6298 					break;
6299 				}
6300 
6301 			int cnt=0;
6302 			for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
6303 				if(teachersMatrix[i][j][k]>0){
6304 					tg+=cnt;
6305 					cnt=0;
6306 				}
6307 				else
6308 					cnt++;
6309 			}
6310 		}
6311 		if(tg>this->maxGaps){
6312 			totalGaps+=tg-maxGaps;
6313 			//assert(this->weightPercentage<100); partial solutions might break this rule
6314 			if(conflictsString!=nullptr){
6315 				QString s=tr("Time constraint teachers max gaps per week broken for teacher: %1, conflicts factor increase=%2")
6316 					.arg(r.internalTeachersList[i]->name)
6317 					.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps)*weightPercentage/100));
6318 
6319 				*conflictsString+= s+"\n";
6320 
6321 				dl.append(s);
6322 				cl.append((tg-maxGaps)*weightPercentage/100);
6323 			}
6324 		}
6325 	}
6326 
6327 	if(c.nPlacedActivities==r.nInternalActivities)
6328 		if(weightPercentage==100){
6329 			assert(totalGaps==0); //for partial solutions this rule might be broken
6330 		}
6331 
6332 	return weightPercentage/100 * totalGaps;
6333 }
6334 
isRelatedToActivity(Rules & r,Activity * a)6335 bool ConstraintTeachersMaxGapsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
6336 {
6337 	Q_UNUSED(r);
6338 	Q_UNUSED(a);
6339 
6340 	return false;
6341 }
6342 
isRelatedToTeacher(Teacher * t)6343 bool ConstraintTeachersMaxGapsPerWeek::isRelatedToTeacher(Teacher* t)
6344 {
6345 	Q_UNUSED(t);
6346 
6347 	return true;
6348 }
6349 
isRelatedToSubject(Subject * s)6350 bool ConstraintTeachersMaxGapsPerWeek::isRelatedToSubject(Subject* s)
6351 {
6352 	Q_UNUSED(s);
6353 
6354 	return false;
6355 }
6356 
isRelatedToActivityTag(ActivityTag * s)6357 bool ConstraintTeachersMaxGapsPerWeek::isRelatedToActivityTag(ActivityTag* s)
6358 {
6359 	Q_UNUSED(s);
6360 
6361 	return false;
6362 }
6363 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)6364 bool ConstraintTeachersMaxGapsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
6365 {
6366 	Q_UNUSED(r);
6367 	Q_UNUSED(s);
6368 
6369 	return false;
6370 }
6371 
hasWrongDayOrHour(Rules & r)6372 bool ConstraintTeachersMaxGapsPerWeek::hasWrongDayOrHour(Rules& r)
6373 {
6374 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
6375 		return true;
6376 
6377 	return false;
6378 }
6379 
canRepairWrongDayOrHour(Rules & r)6380 bool ConstraintTeachersMaxGapsPerWeek::canRepairWrongDayOrHour(Rules& r)
6381 {
6382 	assert(hasWrongDayOrHour(r));
6383 
6384 	return true;
6385 }
6386 
repairWrongDayOrHour(Rules & r)6387 bool ConstraintTeachersMaxGapsPerWeek::repairWrongDayOrHour(Rules& r)
6388 {
6389 	assert(hasWrongDayOrHour(r));
6390 
6391 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
6392 		maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
6393 
6394 	return true;
6395 }
6396 
6397 ////////////////////////////////////////////////////////////////////////////////////////////
6398 ////////////////////////////////////////////////////////////////////////////////////////////
6399 
ConstraintTeacherMaxGapsPerWeek()6400 ConstraintTeacherMaxGapsPerWeek::ConstraintTeacherMaxGapsPerWeek()
6401 	: TimeConstraint()
6402 {
6403 	this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_WEEK;
6404 }
6405 
ConstraintTeacherMaxGapsPerWeek(double wp,const QString & tn,int mg)6406 ConstraintTeacherMaxGapsPerWeek::ConstraintTeacherMaxGapsPerWeek(double wp, const QString& tn, int mg)
6407 	: TimeConstraint(wp)
6408 {
6409 	this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_WEEK;
6410 	this->teacherName=tn;
6411 	this->maxGaps=mg;
6412 }
6413 
computeInternalStructure(QWidget * parent,Rules & r)6414 bool ConstraintTeacherMaxGapsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
6415 {
6416 	Q_UNUSED(parent);
6417 
6418 	//this->teacherIndex=r.searchTeacher(this->teacherName);
6419 	teacherIndex=r.teachersHash.value(teacherName, -1);
6420 	assert(this->teacherIndex>=0);
6421 	return true;
6422 }
6423 
hasInactiveActivities(Rules & r)6424 bool ConstraintTeacherMaxGapsPerWeek::hasInactiveActivities(Rules& r)
6425 {
6426 	Q_UNUSED(r);
6427 	return false;
6428 }
6429 
getXmlDescription(Rules & r)6430 QString ConstraintTeacherMaxGapsPerWeek::getXmlDescription(Rules& r)
6431 {
6432 	Q_UNUSED(r);
6433 
6434 	QString s="<ConstraintTeacherMaxGapsPerWeek>\n";
6435 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
6436 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
6437 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
6438 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
6439 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
6440 	s+="</ConstraintTeacherMaxGapsPerWeek>\n";
6441 	return s;
6442 }
6443 
getDescription(Rules & r)6444 QString ConstraintTeacherMaxGapsPerWeek::getDescription(Rules& r)
6445 {
6446 	Q_UNUSED(r);
6447 
6448 	QString begin=QString("");
6449 	if(!active)
6450 		begin="X - ";
6451 
6452 	QString end=QString("");
6453 	if(!comments.isEmpty())
6454 		end=", "+tr("C: %1", "Comments").arg(comments);
6455 
6456 	QString s;
6457 	s+=tr("Teacher max gaps per week");s+=", ";
6458 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
6459 	s+=tr("T:%1", "Teacher").arg(this->teacherName); s+=", ";
6460 	s+=tr("MG:%1", "Max gaps (per week").arg(this->maxGaps);
6461 
6462 	return begin+s+end;
6463 }
6464 
getDetailedDescription(Rules & r)6465 QString ConstraintTeacherMaxGapsPerWeek::getDetailedDescription(Rules& r)
6466 {
6467 	Q_UNUSED(r);
6468 
6469 	QString s=tr("Time constraint"); s+="\n";
6470 	s+=tr("A teacher must respect the maximum number of gaps per week"); s+="\n";
6471 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
6472 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
6473 	s+=tr("Teacher=%1").arg(this->teacherName); s+="\n";
6474 	s+=tr("Maximum gaps per week=%1").arg(this->maxGaps); s+="\n";
6475 
6476 	if(!active){
6477 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
6478 		s+="\n";
6479 	}
6480 	if(!comments.isEmpty()){
6481 		s+=tr("Comments=%1").arg(comments);
6482 		s+="\n";
6483 	}
6484 
6485 	return s;
6486 }
6487 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)6488 double ConstraintTeacherMaxGapsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
6489 {
6490 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
6491 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
6492 		c.teachersMatrixReady=true;
6493 		c.subgroupsMatrixReady=true;
6494 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
6495 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
6496 
6497 		c.changedForMatrixCalculation=false;
6498 	}
6499 
6500 	int tg;
6501 	int i, j, k;
6502 	int totalGaps;
6503 
6504 	totalGaps=0;
6505 
6506 	i=this->teacherIndex;
6507 
6508 	tg=0;
6509 	for(j=0; j<r.nDaysPerWeek; j++){
6510 		for(k=0; k<r.nHoursPerDay; k++)
6511 			if(teachersMatrix[i][j][k]>0){
6512 				assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
6513 				break;
6514 			}
6515 
6516 		int cnt=0;
6517 		for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
6518 			if(teachersMatrix[i][j][k]>0){
6519 				tg+=cnt;
6520 				cnt=0;
6521 			}
6522 			else
6523 				cnt++;
6524 		}
6525 	}
6526 	if(tg>this->maxGaps){
6527 		totalGaps+=tg-maxGaps;
6528 		//assert(this->weightPercentage<100); partial solutions might break this rule
6529 		if(conflictsString!=nullptr){
6530 			QString s=tr("Time constraint teacher max gaps per week broken for teacher: %1, conflicts factor increase=%2")
6531 				.arg(r.internalTeachersList[i]->name)
6532 				.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps)*weightPercentage/100));
6533 
6534 			*conflictsString+= s+"\n";
6535 
6536 			dl.append(s);
6537 			cl.append((tg-maxGaps)*weightPercentage/100);
6538 		}
6539 	}
6540 
6541 	if(c.nPlacedActivities==r.nInternalActivities)
6542 		if(weightPercentage==100)
6543 			assert(totalGaps==0); //for partial solutions this rule might be broken
6544 	return weightPercentage/100 * totalGaps;
6545 }
6546 
isRelatedToActivity(Rules & r,Activity * a)6547 bool ConstraintTeacherMaxGapsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
6548 {
6549 	Q_UNUSED(r);
6550 	Q_UNUSED(a);
6551 
6552 	return false;
6553 }
6554 
isRelatedToTeacher(Teacher * t)6555 bool ConstraintTeacherMaxGapsPerWeek::isRelatedToTeacher(Teacher* t)
6556 {
6557 	if(this->teacherName==t->name)
6558 		return true;
6559 	return false;
6560 }
6561 
isRelatedToSubject(Subject * s)6562 bool ConstraintTeacherMaxGapsPerWeek::isRelatedToSubject(Subject* s)
6563 {
6564 	Q_UNUSED(s);
6565 
6566 	return false;
6567 }
6568 
isRelatedToActivityTag(ActivityTag * s)6569 bool ConstraintTeacherMaxGapsPerWeek::isRelatedToActivityTag(ActivityTag* s)
6570 {
6571 	Q_UNUSED(s);
6572 
6573 	return false;
6574 }
6575 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)6576 bool ConstraintTeacherMaxGapsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
6577 {
6578 	Q_UNUSED(r);
6579 	Q_UNUSED(s);
6580 
6581 	return false;
6582 }
6583 
hasWrongDayOrHour(Rules & r)6584 bool ConstraintTeacherMaxGapsPerWeek::hasWrongDayOrHour(Rules& r)
6585 {
6586 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
6587 		return true;
6588 
6589 	return false;
6590 }
6591 
canRepairWrongDayOrHour(Rules & r)6592 bool ConstraintTeacherMaxGapsPerWeek::canRepairWrongDayOrHour(Rules& r)
6593 {
6594 	assert(hasWrongDayOrHour(r));
6595 
6596 	return true;
6597 }
6598 
repairWrongDayOrHour(Rules & r)6599 bool ConstraintTeacherMaxGapsPerWeek::repairWrongDayOrHour(Rules& r)
6600 {
6601 	assert(hasWrongDayOrHour(r));
6602 
6603 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
6604 		maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
6605 
6606 	return true;
6607 }
6608 
6609 ////////////////////////////////////////////////////////////////////////////////////////////
6610 ////////////////////////////////////////////////////////////////////////////////////////////
6611 
ConstraintTeachersMaxGapsPerDay()6612 ConstraintTeachersMaxGapsPerDay::ConstraintTeachersMaxGapsPerDay()
6613 	: TimeConstraint()
6614 {
6615 	this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_DAY;
6616 }
6617 
ConstraintTeachersMaxGapsPerDay(double wp,int mg)6618 ConstraintTeachersMaxGapsPerDay::ConstraintTeachersMaxGapsPerDay(double wp, int mg)
6619 	: TimeConstraint(wp)
6620 {
6621 	this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_DAY;
6622 	this->maxGaps=mg;
6623 }
6624 
computeInternalStructure(QWidget * parent,Rules & r)6625 bool ConstraintTeachersMaxGapsPerDay::computeInternalStructure(QWidget* parent, Rules& r)
6626 {
6627 	Q_UNUSED(parent);
6628 	Q_UNUSED(r);
6629 
6630 	return true;
6631 }
6632 
hasInactiveActivities(Rules & r)6633 bool ConstraintTeachersMaxGapsPerDay::hasInactiveActivities(Rules& r)
6634 {
6635 	Q_UNUSED(r);
6636 	return false;
6637 }
6638 
getXmlDescription(Rules & r)6639 QString ConstraintTeachersMaxGapsPerDay::getXmlDescription(Rules& r)
6640 {
6641 	Q_UNUSED(r);
6642 
6643 	QString s="<ConstraintTeachersMaxGapsPerDay>\n";
6644 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
6645 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
6646 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
6647 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
6648 	s+="</ConstraintTeachersMaxGapsPerDay>\n";
6649 	return s;
6650 }
6651 
getDescription(Rules & r)6652 QString ConstraintTeachersMaxGapsPerDay::getDescription(Rules& r)
6653 {
6654 	Q_UNUSED(r);
6655 
6656 	QString begin=QString("");
6657 	if(!active)
6658 		begin="X - ";
6659 
6660 	QString end=QString("");
6661 	if(!comments.isEmpty())
6662 		end=", "+tr("C: %1", "Comments").arg(comments);
6663 
6664 	QString s;
6665 	s+=tr("Teachers max gaps per day");s+=", ";
6666 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
6667 	s+=tr("MG:%1", "Max gaps (per day)").arg(this->maxGaps);
6668 
6669 	return begin+s+end;
6670 }
6671 
getDetailedDescription(Rules & r)6672 QString ConstraintTeachersMaxGapsPerDay::getDetailedDescription(Rules& r)
6673 {
6674 	Q_UNUSED(r);
6675 
6676 	QString s=tr("Time constraint");s+="\n";
6677 	s+=tr("All teachers must respect the maximum gaps per day");s+="\n";
6678 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
6679 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
6680 	s+=tr("Maximum gaps per day=%1").arg(this->maxGaps); s+="\n";
6681 
6682 	if(!active){
6683 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
6684 		s+="\n";
6685 	}
6686 	if(!comments.isEmpty()){
6687 		s+=tr("Comments=%1").arg(comments);
6688 		s+="\n";
6689 	}
6690 
6691 	return s;
6692 }
6693 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)6694 double ConstraintTeachersMaxGapsPerDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
6695 {
6696 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
6697 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
6698 		c.teachersMatrixReady=true;
6699 		c.subgroupsMatrixReady=true;
6700 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
6701 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
6702 
6703 		c.changedForMatrixCalculation=false;
6704 	}
6705 
6706 	int tg;
6707 	int i, j, k;
6708 	int totalGaps;
6709 
6710 	totalGaps=0;
6711 	for(i=0; i<r.nInternalTeachers; i++){
6712 		for(j=0; j<r.nDaysPerWeek; j++){
6713 			tg=0;
6714 			for(k=0; k<r.nHoursPerDay; k++)
6715 				if(teachersMatrix[i][j][k]>0){
6716 					assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
6717 					break;
6718 				}
6719 
6720 			int cnt=0;
6721 			for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
6722 				if(teachersMatrix[i][j][k]>0){
6723 					tg+=cnt;
6724 					cnt=0;
6725 				}
6726 				else
6727 					cnt++;
6728 			}
6729 			if(tg>this->maxGaps){
6730 				totalGaps+=tg-maxGaps;
6731 				//assert(this->weightPercentage<100); partial solutions might break this rule
6732 				if(conflictsString!=nullptr){
6733 					QString s=tr("Time constraint teachers max gaps per day broken for teacher: %1, day: %2, conflicts factor increase=%3")
6734 						.arg(r.internalTeachersList[i]->name)
6735 						.arg(r.daysOfTheWeek[j])
6736 						.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps)*weightPercentage/100));
6737 
6738 					*conflictsString+= s+"\n";
6739 
6740 					dl.append(s);
6741 					cl.append((tg-maxGaps)*weightPercentage/100);
6742 				}
6743 			}
6744 		}
6745 	}
6746 
6747 	if(c.nPlacedActivities==r.nInternalActivities)
6748 		if(weightPercentage==100)
6749 			assert(totalGaps==0); //for partial solutions this rule might be broken
6750 	return weightPercentage/100 * totalGaps;
6751 }
6752 
isRelatedToActivity(Rules & r,Activity * a)6753 bool ConstraintTeachersMaxGapsPerDay::isRelatedToActivity(Rules& r, Activity* a)
6754 {
6755 	Q_UNUSED(r);
6756 	Q_UNUSED(a);
6757 
6758 	return false;
6759 }
6760 
isRelatedToTeacher(Teacher * t)6761 bool ConstraintTeachersMaxGapsPerDay::isRelatedToTeacher(Teacher* t)
6762 {
6763 	Q_UNUSED(t);
6764 
6765 	return true;
6766 }
6767 
isRelatedToSubject(Subject * s)6768 bool ConstraintTeachersMaxGapsPerDay::isRelatedToSubject(Subject* s)
6769 {
6770 	Q_UNUSED(s);
6771 
6772 	return false;
6773 }
6774 
isRelatedToActivityTag(ActivityTag * s)6775 bool ConstraintTeachersMaxGapsPerDay::isRelatedToActivityTag(ActivityTag* s)
6776 {
6777 	Q_UNUSED(s);
6778 
6779 	return false;
6780 }
6781 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)6782 bool ConstraintTeachersMaxGapsPerDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
6783 {
6784 	Q_UNUSED(r);
6785 	Q_UNUSED(s);
6786 
6787 	return false;
6788 }
6789 
hasWrongDayOrHour(Rules & r)6790 bool ConstraintTeachersMaxGapsPerDay::hasWrongDayOrHour(Rules& r)
6791 {
6792 	if(maxGaps>r.nHoursPerDay)
6793 		return true;
6794 
6795 	return false;
6796 }
6797 
canRepairWrongDayOrHour(Rules & r)6798 bool ConstraintTeachersMaxGapsPerDay::canRepairWrongDayOrHour(Rules& r)
6799 {
6800 	assert(hasWrongDayOrHour(r));
6801 
6802 	return true;
6803 }
6804 
repairWrongDayOrHour(Rules & r)6805 bool ConstraintTeachersMaxGapsPerDay::repairWrongDayOrHour(Rules& r)
6806 {
6807 	assert(hasWrongDayOrHour(r));
6808 
6809 	if(maxGaps>r.nHoursPerDay)
6810 		maxGaps=r.nHoursPerDay;
6811 
6812 	return true;
6813 }
6814 
6815 ////////////////////////////////////////////////////////////////////////////////////////////
6816 ////////////////////////////////////////////////////////////////////////////////////////////
6817 
ConstraintTeacherMaxGapsPerDay()6818 ConstraintTeacherMaxGapsPerDay::ConstraintTeacherMaxGapsPerDay()
6819 	: TimeConstraint()
6820 {
6821 	this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_DAY;
6822 }
6823 
ConstraintTeacherMaxGapsPerDay(double wp,const QString & tn,int mg)6824 ConstraintTeacherMaxGapsPerDay::ConstraintTeacherMaxGapsPerDay(double wp, const QString& tn, int mg)
6825 	: TimeConstraint(wp)
6826 {
6827 	this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_DAY;
6828 	this->teacherName=tn;
6829 	this->maxGaps=mg;
6830 }
6831 
computeInternalStructure(QWidget * parent,Rules & r)6832 bool ConstraintTeacherMaxGapsPerDay::computeInternalStructure(QWidget* parent, Rules& r)
6833 {
6834 	Q_UNUSED(parent);
6835 
6836 	//this->teacherIndex=r.searchTeacher(this->teacherName);
6837 	teacherIndex=r.teachersHash.value(teacherName, -1);
6838 	assert(this->teacherIndex>=0);
6839 	return true;
6840 }
6841 
hasInactiveActivities(Rules & r)6842 bool ConstraintTeacherMaxGapsPerDay::hasInactiveActivities(Rules& r)
6843 {
6844 	Q_UNUSED(r);
6845 	return false;
6846 }
6847 
getXmlDescription(Rules & r)6848 QString ConstraintTeacherMaxGapsPerDay::getXmlDescription(Rules& r)
6849 {
6850 	Q_UNUSED(r);
6851 
6852 	QString s="<ConstraintTeacherMaxGapsPerDay>\n";
6853 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
6854 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
6855 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
6856 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
6857 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
6858 	s+="</ConstraintTeacherMaxGapsPerDay>\n";
6859 	return s;
6860 }
6861 
getDescription(Rules & r)6862 QString ConstraintTeacherMaxGapsPerDay::getDescription(Rules& r)
6863 {
6864 	Q_UNUSED(r);
6865 
6866 	QString begin=QString("");
6867 	if(!active)
6868 		begin="X - ";
6869 
6870 	QString end=QString("");
6871 	if(!comments.isEmpty())
6872 		end=", "+tr("C: %1", "Comments").arg(comments);
6873 
6874 	QString s;
6875 	s+=tr("Teacher max gaps per day");s+=", ";
6876 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
6877 	s+=tr("T:%1", "Teacher").arg(this->teacherName); s+=", ";
6878 	s+=tr("MG:%1", "Max gaps (per day)").arg(this->maxGaps);
6879 
6880 	return begin+s+end;
6881 }
6882 
getDetailedDescription(Rules & r)6883 QString ConstraintTeacherMaxGapsPerDay::getDetailedDescription(Rules& r)
6884 {
6885 	Q_UNUSED(r);
6886 
6887 	QString s=tr("Time constraint"); s+="\n";
6888 	s+=tr("A teacher must respect the maximum number of gaps per day"); s+="\n";
6889 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
6890 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
6891 	s+=tr("Teacher=%1").arg(this->teacherName); s+="\n";
6892 	s+=tr("Maximum gaps per day=%1").arg(this->maxGaps); s+="\n";
6893 
6894 	if(!active){
6895 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
6896 		s+="\n";
6897 	}
6898 	if(!comments.isEmpty()){
6899 		s+=tr("Comments=%1").arg(comments);
6900 		s+="\n";
6901 	}
6902 
6903 	return s;
6904 }
6905 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)6906 double ConstraintTeacherMaxGapsPerDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
6907 {
6908 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
6909 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
6910 		c.teachersMatrixReady=true;
6911 		c.subgroupsMatrixReady=true;
6912 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
6913 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
6914 
6915 		c.changedForMatrixCalculation=false;
6916 	}
6917 
6918 	int tg;
6919 	int i, j, k;
6920 	int totalGaps;
6921 
6922 	totalGaps=0;
6923 
6924 	i=this->teacherIndex;
6925 
6926 	for(j=0; j<r.nDaysPerWeek; j++){
6927 		tg=0;
6928 		for(k=0; k<r.nHoursPerDay; k++)
6929 			if(teachersMatrix[i][j][k]>0){
6930 				assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
6931 				break;
6932 			}
6933 
6934 		int cnt=0;
6935 		for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
6936 			if(teachersMatrix[i][j][k]>0){
6937 				tg+=cnt;
6938 				cnt=0;
6939 			}
6940 			else
6941 				cnt++;
6942 		}
6943 		if(tg>this->maxGaps){
6944 			totalGaps+=tg-maxGaps;
6945 			//assert(this->weightPercentage<100); partial solutions might break this rule
6946 			if(conflictsString!=nullptr){
6947 				QString s=tr("Time constraint teacher max gaps per day broken for teacher: %1, day: %2, conflicts factor increase=%3")
6948 					.arg(r.internalTeachersList[i]->name)
6949 					.arg(r.daysOfTheWeek[j])
6950 					.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps)*weightPercentage/100));
6951 
6952 				*conflictsString+= s+"\n";
6953 
6954 				dl.append(s);
6955 				cl.append((tg-maxGaps)*weightPercentage/100);
6956 			}
6957 		}
6958 	}
6959 
6960 	if(c.nPlacedActivities==r.nInternalActivities)
6961 		if(weightPercentage==100)
6962 			assert(totalGaps==0); //for partial solutions this rule might be broken
6963 	return weightPercentage/100 * totalGaps;
6964 }
6965 
isRelatedToActivity(Rules & r,Activity * a)6966 bool ConstraintTeacherMaxGapsPerDay::isRelatedToActivity(Rules& r, Activity* a)
6967 {
6968 	Q_UNUSED(r);
6969 	Q_UNUSED(a);
6970 
6971 	return false;
6972 }
6973 
isRelatedToTeacher(Teacher * t)6974 bool ConstraintTeacherMaxGapsPerDay::isRelatedToTeacher(Teacher* t)
6975 {
6976 	if(this->teacherName==t->name)
6977 		return true;
6978 	return false;
6979 }
6980 
isRelatedToSubject(Subject * s)6981 bool ConstraintTeacherMaxGapsPerDay::isRelatedToSubject(Subject* s)
6982 {
6983 	Q_UNUSED(s);
6984 
6985 	return false;
6986 }
6987 
isRelatedToActivityTag(ActivityTag * s)6988 bool ConstraintTeacherMaxGapsPerDay::isRelatedToActivityTag(ActivityTag* s)
6989 {
6990 	Q_UNUSED(s);
6991 
6992 	return false;
6993 }
6994 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)6995 bool ConstraintTeacherMaxGapsPerDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
6996 {
6997 	Q_UNUSED(r);
6998 	Q_UNUSED(s);
6999 
7000 	return false;
7001 }
7002 
hasWrongDayOrHour(Rules & r)7003 bool ConstraintTeacherMaxGapsPerDay::hasWrongDayOrHour(Rules& r)
7004 {
7005 	if(maxGaps>r.nHoursPerDay)
7006 		return true;
7007 
7008 	return false;
7009 }
7010 
canRepairWrongDayOrHour(Rules & r)7011 bool ConstraintTeacherMaxGapsPerDay::canRepairWrongDayOrHour(Rules& r)
7012 {
7013 	assert(hasWrongDayOrHour(r));
7014 
7015 	return true;
7016 }
7017 
repairWrongDayOrHour(Rules & r)7018 bool ConstraintTeacherMaxGapsPerDay::repairWrongDayOrHour(Rules& r)
7019 {
7020 	assert(hasWrongDayOrHour(r));
7021 
7022 	if(maxGaps>r.nHoursPerDay)
7023 		maxGaps=r.nHoursPerDay;
7024 
7025 	return true;
7026 }
7027 
7028 ////////////////////////////////////////////////////////////////////////////////////////////
7029 ////////////////////////////////////////////////////////////////////////////////////////////
7030 
ConstraintTeachersMaxGapsPerMorningAndAfternoon()7031 ConstraintTeachersMaxGapsPerMorningAndAfternoon::ConstraintTeachersMaxGapsPerMorningAndAfternoon()
7032 	: TimeConstraint()
7033 {
7034 	this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_MORNING_AND_AFTERNOON;
7035 }
7036 
ConstraintTeachersMaxGapsPerMorningAndAfternoon(double wp,int mg)7037 ConstraintTeachersMaxGapsPerMorningAndAfternoon::ConstraintTeachersMaxGapsPerMorningAndAfternoon(double wp, int mg)
7038 	: TimeConstraint(wp)
7039 {
7040 	this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_MORNING_AND_AFTERNOON;
7041 	this->maxGaps=mg;
7042 }
7043 
computeInternalStructure(QWidget * parent,Rules & r)7044 bool ConstraintTeachersMaxGapsPerMorningAndAfternoon::computeInternalStructure(QWidget* parent, Rules& r)
7045 {
7046 	Q_UNUSED(parent);
7047 	Q_UNUSED(r);
7048 
7049 	return true;
7050 }
7051 
hasInactiveActivities(Rules & r)7052 bool ConstraintTeachersMaxGapsPerMorningAndAfternoon::hasInactiveActivities(Rules& r)
7053 {
7054 	Q_UNUSED(r);
7055 	return false;
7056 }
7057 
getXmlDescription(Rules & r)7058 QString ConstraintTeachersMaxGapsPerMorningAndAfternoon::getXmlDescription(Rules& r)
7059 {
7060 	Q_UNUSED(r);
7061 
7062 	QString s="<ConstraintTeachersMaxGapsPerMorningAndAfternoon>\n";
7063 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
7064 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
7065 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
7066 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
7067 	s+="</ConstraintTeachersMaxGapsPerMorningAndAfternoon>\n";
7068 	return s;
7069 }
7070 
getDescription(Rules & r)7071 QString ConstraintTeachersMaxGapsPerMorningAndAfternoon::getDescription(Rules& r)
7072 {
7073 	Q_UNUSED(r);
7074 
7075 	QString begin=QString("");
7076 	if(!active)
7077 		begin="X - ";
7078 
7079 	QString end=QString("");
7080 	if(!comments.isEmpty())
7081 		end=", "+tr("C: %1", "Comments").arg(comments);
7082 
7083 	QString s;
7084 	s+=tr("Teachers max gaps per morning and afternoon");s+=", ";
7085 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
7086 	s+=tr("MG:%1", "Max gaps (per morning and afternoon)").arg(this->maxGaps);
7087 
7088 	return begin+s+end;
7089 }
7090 
getDetailedDescription(Rules & r)7091 QString ConstraintTeachersMaxGapsPerMorningAndAfternoon::getDetailedDescription(Rules& r)
7092 {
7093 	Q_UNUSED(r);
7094 
7095 	QString s=tr("Time constraint");s+="\n";
7096 	s+=tr("All teachers must respect the maximum gaps per morning and afternoon");s+="\n";
7097 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
7098 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
7099 	s+=tr("Maximum gaps per morning and afternoon=%1").arg(this->maxGaps); s+="\n";
7100 
7101 	if(!active){
7102 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
7103 		s+="\n";
7104 	}
7105 	if(!comments.isEmpty()){
7106 		s+=tr("Comments=%1").arg(comments);
7107 		s+="\n";
7108 	}
7109 
7110 	return s;
7111 }
7112 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)7113 double ConstraintTeachersMaxGapsPerMorningAndAfternoon::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
7114 {
7115 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
7116 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
7117 		c.teachersMatrixReady=true;
7118 		c.subgroupsMatrixReady=true;
7119 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
7120 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
7121 
7122 		c.changedForMatrixCalculation=false;
7123 	}
7124 
7125 	int tg;
7126 	int i, j, k;
7127 	int totalGaps;
7128 
7129 	totalGaps=0;
7130 	for(i=0; i<r.nInternalTeachers; i++){
7131 		for(j=0; j<r.nDaysPerWeek/2; j++){
7132 			tg=0;
7133 			for(k=0; k<r.nHoursPerDay; k++)
7134 				if(teachersMatrix[i][2*j][k]>0){
7135 					assert(!breakDayHour[2*j][k] && !teacherNotAvailableDayHour[i][2*j][k]);
7136 					break;
7137 				}
7138 
7139 			int cnt=0;
7140 			for(; k<r.nHoursPerDay; k++) if(!breakDayHour[2*j][k] && !teacherNotAvailableDayHour[i][2*j][k]){
7141 				if(teachersMatrix[i][2*j][k]>0){
7142 					tg+=cnt;
7143 					cnt=0;
7144 				}
7145 				else
7146 					cnt++;
7147 			}
7148 
7149 			for(k=0; k<r.nHoursPerDay; k++)
7150 				if(teachersMatrix[i][2*j+1][k]>0){
7151 					assert(!breakDayHour[2*j+1][k] && !teacherNotAvailableDayHour[i][2*j+1][k]);
7152 					break;
7153 				}
7154 
7155 			cnt=0;
7156 			for(; k<r.nHoursPerDay; k++) if(!breakDayHour[2*j+1][k] && !teacherNotAvailableDayHour[i][2*j+1][k]){
7157 				if(teachersMatrix[i][2*j+1][k]>0){
7158 					tg+=cnt;
7159 					cnt=0;
7160 				}
7161 				else
7162 					cnt++;
7163 			}
7164 
7165 			if(tg>this->maxGaps){
7166 				totalGaps+=tg-maxGaps;
7167 				//assert(this->weightPercentage<100); partial solutions might break this rule
7168 				if(conflictsString!=nullptr){
7169 					QString s=tr("Time constraint teachers max gaps per morning and afternoon broken for teacher: %1, real day number: %2, conflicts factor increase=%3")
7170 						.arg(r.internalTeachersList[i]->name)
7171 						.arg(j)
7172 						.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps)*weightPercentage/100));
7173 
7174 					*conflictsString+= s+"\n";
7175 
7176 					dl.append(s);
7177 					cl.append((tg-maxGaps)*weightPercentage/100);
7178 				}
7179 			}
7180 		}
7181 	}
7182 
7183 	if(c.nPlacedActivities==r.nInternalActivities)
7184 		if(weightPercentage==100)
7185 			assert(totalGaps==0); //for partial solutions this rule might be broken
7186 	return weightPercentage/100 * totalGaps;
7187 }
7188 
isRelatedToActivity(Rules & r,Activity * a)7189 bool ConstraintTeachersMaxGapsPerMorningAndAfternoon::isRelatedToActivity(Rules& r, Activity* a)
7190 {
7191 	Q_UNUSED(r);
7192 	Q_UNUSED(a);
7193 
7194 	return false;
7195 }
7196 
isRelatedToTeacher(Teacher * t)7197 bool ConstraintTeachersMaxGapsPerMorningAndAfternoon::isRelatedToTeacher(Teacher* t)
7198 {
7199 	Q_UNUSED(t);
7200 
7201 	return true;
7202 }
7203 
isRelatedToSubject(Subject * s)7204 bool ConstraintTeachersMaxGapsPerMorningAndAfternoon::isRelatedToSubject(Subject* s)
7205 {
7206 	Q_UNUSED(s);
7207 
7208 	return false;
7209 }
7210 
isRelatedToActivityTag(ActivityTag * s)7211 bool ConstraintTeachersMaxGapsPerMorningAndAfternoon::isRelatedToActivityTag(ActivityTag* s)
7212 {
7213 	Q_UNUSED(s);
7214 
7215 	return false;
7216 }
7217 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)7218 bool ConstraintTeachersMaxGapsPerMorningAndAfternoon::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
7219 {
7220 	Q_UNUSED(r);
7221 	Q_UNUSED(s);
7222 
7223 	return false;
7224 }
7225 
hasWrongDayOrHour(Rules & r)7226 bool ConstraintTeachersMaxGapsPerMorningAndAfternoon::hasWrongDayOrHour(Rules& r)
7227 {
7228 	if(maxGaps>2*r.nHoursPerDay)
7229 		return true;
7230 
7231 	return false;
7232 }
7233 
canRepairWrongDayOrHour(Rules & r)7234 bool ConstraintTeachersMaxGapsPerMorningAndAfternoon::canRepairWrongDayOrHour(Rules& r)
7235 {
7236 	assert(hasWrongDayOrHour(r));
7237 
7238 	return true;
7239 }
7240 
repairWrongDayOrHour(Rules & r)7241 bool ConstraintTeachersMaxGapsPerMorningAndAfternoon::repairWrongDayOrHour(Rules& r)
7242 {
7243 	assert(hasWrongDayOrHour(r));
7244 
7245 	if(maxGaps>2*r.nHoursPerDay)
7246 		maxGaps=2*r.nHoursPerDay;
7247 
7248 	return true;
7249 }
7250 
7251 ////////////////////////////////////////////////////////////////////////////////////////////
7252 ////////////////////////////////////////////////////////////////////////////////////////////
7253 
ConstraintTeacherMaxGapsPerMorningAndAfternoon()7254 ConstraintTeacherMaxGapsPerMorningAndAfternoon::ConstraintTeacherMaxGapsPerMorningAndAfternoon()
7255 	: TimeConstraint()
7256 {
7257 	this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_MORNING_AND_AFTERNOON;
7258 }
7259 
ConstraintTeacherMaxGapsPerMorningAndAfternoon(double wp,const QString & tn,int mg)7260 ConstraintTeacherMaxGapsPerMorningAndAfternoon::ConstraintTeacherMaxGapsPerMorningAndAfternoon(double wp, const QString& tn, int mg)
7261 	: TimeConstraint(wp)
7262 {
7263 	this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_MORNING_AND_AFTERNOON;
7264 	this->teacherName=tn;
7265 	this->maxGaps=mg;
7266 }
7267 
computeInternalStructure(QWidget * parent,Rules & r)7268 bool ConstraintTeacherMaxGapsPerMorningAndAfternoon::computeInternalStructure(QWidget* parent, Rules& r)
7269 {
7270 	Q_UNUSED(parent);
7271 
7272 	//this->teacherIndex=r.searchTeacher(this->teacherName);
7273 	teacherIndex=r.teachersHash.value(teacherName, -1);
7274 	assert(this->teacherIndex>=0);
7275 	return true;
7276 }
7277 
hasInactiveActivities(Rules & r)7278 bool ConstraintTeacherMaxGapsPerMorningAndAfternoon::hasInactiveActivities(Rules& r)
7279 {
7280 	Q_UNUSED(r);
7281 	return false;
7282 }
7283 
getXmlDescription(Rules & r)7284 QString ConstraintTeacherMaxGapsPerMorningAndAfternoon::getXmlDescription(Rules& r)
7285 {
7286 	Q_UNUSED(r);
7287 
7288 	QString s="<ConstraintTeacherMaxGapsPerMorningAndAfternoon>\n";
7289 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
7290 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
7291 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
7292 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
7293 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
7294 	s+="</ConstraintTeacherMaxGapsPerMorningAndAfternoon>\n";
7295 	return s;
7296 }
7297 
getDescription(Rules & r)7298 QString ConstraintTeacherMaxGapsPerMorningAndAfternoon::getDescription(Rules& r)
7299 {
7300 	Q_UNUSED(r);
7301 
7302 	QString begin=QString("");
7303 	if(!active)
7304 		begin="X - ";
7305 
7306 	QString end=QString("");
7307 	if(!comments.isEmpty())
7308 		end=", "+tr("C: %1", "Comments").arg(comments);
7309 
7310 	QString s;
7311 	s+=tr("Teacher max gaps per morning and afternoon");s+=", ";
7312 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
7313 	s+=tr("T:%1", "Teacher").arg(this->teacherName); s+=", ";
7314 	s+=tr("MG:%1", "Max gaps (per morning and afternoon)").arg(this->maxGaps);
7315 
7316 	return begin+s+end;
7317 }
7318 
getDetailedDescription(Rules & r)7319 QString ConstraintTeacherMaxGapsPerMorningAndAfternoon::getDetailedDescription(Rules& r)
7320 {
7321 	Q_UNUSED(r);
7322 
7323 	QString s=tr("Time constraint"); s+="\n";
7324 	s+=tr("A teacher must respect the maximum number of gaps per morning and afternoon"); s+="\n";
7325 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
7326 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
7327 	s+=tr("Teacher=%1").arg(this->teacherName); s+="\n";
7328 	s+=tr("Maximum gaps per morning and afternoon=%1").arg(this->maxGaps); s+="\n";
7329 
7330 	if(!active){
7331 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
7332 		s+="\n";
7333 	}
7334 	if(!comments.isEmpty()){
7335 		s+=tr("Comments=%1").arg(comments);
7336 		s+="\n";
7337 	}
7338 
7339 	return s;
7340 }
7341 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)7342 double ConstraintTeacherMaxGapsPerMorningAndAfternoon::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
7343 {
7344 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
7345 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
7346 		c.teachersMatrixReady=true;
7347 		c.subgroupsMatrixReady=true;
7348 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
7349 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
7350 
7351 		c.changedForMatrixCalculation=false;
7352 	}
7353 
7354 	int tg;
7355 	int i, j, k;
7356 	int totalGaps;
7357 
7358 	totalGaps=0;
7359 
7360 	i=this->teacherIndex;
7361 
7362 	for(j=0; j<r.nDaysPerWeek/2; j++){
7363 		tg=0;
7364 		for(k=0; k<r.nHoursPerDay; k++)
7365 			if(teachersMatrix[i][2*j][k]>0){
7366 				assert(!breakDayHour[2*j][k] && !teacherNotAvailableDayHour[i][2*j][k]);
7367 				break;
7368 			}
7369 
7370 		int cnt=0;
7371 		for(; k<r.nHoursPerDay; k++) if(!breakDayHour[2*j][k] && !teacherNotAvailableDayHour[i][2*j][k]){
7372 			if(teachersMatrix[i][2*j][k]>0){
7373 				tg+=cnt;
7374 				cnt=0;
7375 			}
7376 			else
7377 				cnt++;
7378 		}
7379 
7380 		for(k=0; k<r.nHoursPerDay; k++)
7381 			if(teachersMatrix[i][2*j+1][k]>0){
7382 				assert(!breakDayHour[2*j+1][k] && !teacherNotAvailableDayHour[i][2*j+1][k]);
7383 				break;
7384 			}
7385 
7386 		cnt=0;
7387 		for(; k<r.nHoursPerDay; k++) if(!breakDayHour[2*j+1][k] && !teacherNotAvailableDayHour[i][2*j+1][k]){
7388 			if(teachersMatrix[i][2*j+1][k]>0){
7389 				tg+=cnt;
7390 				cnt=0;
7391 			}
7392 			else
7393 				cnt++;
7394 		}
7395 
7396 		if(tg>this->maxGaps){
7397 			totalGaps+=tg-maxGaps;
7398 			//assert(this->weightPercentage<100); partial solutions might break this rule
7399 			if(conflictsString!=nullptr){
7400 				QString s=tr("Time constraint teacher max gaps per morning and afternoon broken for teacher: %1, real day number: %2, conflicts factor increase=%3")
7401 					.arg(r.internalTeachersList[i]->name)
7402 					.arg(j)
7403 					.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps)*weightPercentage/100));
7404 
7405 				*conflictsString+= s+"\n";
7406 
7407 				dl.append(s);
7408 				cl.append((tg-maxGaps)*weightPercentage/100);
7409 			}
7410 		}
7411 	}
7412 
7413 	if(c.nPlacedActivities==r.nInternalActivities)
7414 		if(weightPercentage==100)
7415 			assert(totalGaps==0); //for partial solutions this rule might be broken
7416 	return weightPercentage/100 * totalGaps;
7417 }
7418 
isRelatedToActivity(Rules & r,Activity * a)7419 bool ConstraintTeacherMaxGapsPerMorningAndAfternoon::isRelatedToActivity(Rules& r, Activity* a)
7420 {
7421 	Q_UNUSED(r);
7422 	Q_UNUSED(a);
7423 
7424 	return false;
7425 }
7426 
isRelatedToTeacher(Teacher * t)7427 bool ConstraintTeacherMaxGapsPerMorningAndAfternoon::isRelatedToTeacher(Teacher* t)
7428 {
7429 	if(this->teacherName==t->name)
7430 		return true;
7431 	return false;
7432 }
7433 
isRelatedToSubject(Subject * s)7434 bool ConstraintTeacherMaxGapsPerMorningAndAfternoon::isRelatedToSubject(Subject* s)
7435 {
7436 	Q_UNUSED(s);
7437 
7438 	return false;
7439 }
7440 
isRelatedToActivityTag(ActivityTag * s)7441 bool ConstraintTeacherMaxGapsPerMorningAndAfternoon::isRelatedToActivityTag(ActivityTag* s)
7442 {
7443 	Q_UNUSED(s);
7444 
7445 	return false;
7446 }
7447 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)7448 bool ConstraintTeacherMaxGapsPerMorningAndAfternoon::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
7449 {
7450 	Q_UNUSED(r);
7451 	Q_UNUSED(s);
7452 
7453 	return false;
7454 }
7455 
hasWrongDayOrHour(Rules & r)7456 bool ConstraintTeacherMaxGapsPerMorningAndAfternoon::hasWrongDayOrHour(Rules& r)
7457 {
7458 	if(maxGaps>2*r.nHoursPerDay)
7459 		return true;
7460 
7461 	return false;
7462 }
7463 
canRepairWrongDayOrHour(Rules & r)7464 bool ConstraintTeacherMaxGapsPerMorningAndAfternoon::canRepairWrongDayOrHour(Rules& r)
7465 {
7466 	assert(hasWrongDayOrHour(r));
7467 
7468 	return true;
7469 }
7470 
repairWrongDayOrHour(Rules & r)7471 bool ConstraintTeacherMaxGapsPerMorningAndAfternoon::repairWrongDayOrHour(Rules& r)
7472 {
7473 	assert(hasWrongDayOrHour(r));
7474 
7475 	if(maxGaps>2*r.nHoursPerDay)
7476 		maxGaps=2*r.nHoursPerDay;
7477 
7478 	return true;
7479 }
7480 
7481 ////////////////////////////////////////////////////////////////////////////////////////////
7482 ////////////////////////////////////////////////////////////////////////////////////////////
7483 
ConstraintBreakTimes()7484 ConstraintBreakTimes::ConstraintBreakTimes()
7485 	: TimeConstraint()
7486 {
7487 	this->type = CONSTRAINT_BREAK_TIMES;
7488 }
7489 
ConstraintBreakTimes(double wp,QList<int> d,QList<int> h)7490 ConstraintBreakTimes::ConstraintBreakTimes(double wp, QList<int> d, QList<int> h)
7491 	: TimeConstraint(wp)
7492 {
7493 	this->days = d;
7494 	this->hours = h;
7495 	this->type = CONSTRAINT_BREAK_TIMES;
7496 }
7497 
hasInactiveActivities(Rules & r)7498 bool ConstraintBreakTimes::hasInactiveActivities(Rules& r)
7499 {
7500 	Q_UNUSED(r);
7501 	return false;
7502 }
7503 
getXmlDescription(Rules & r)7504 QString ConstraintBreakTimes::getXmlDescription(Rules& r)
7505 {
7506 	QString s="<ConstraintBreakTimes>\n";
7507 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
7508 
7509 	s+="	<Number_of_Break_Times>"+QString::number(this->days.count())+"</Number_of_Break_Times>\n";
7510 	assert(days.count()==hours.count());
7511 	for(int i=0; i<days.count(); i++){
7512 		s+="	<Break_Time>\n";
7513 		if(this->days.at(i)>=0)
7514 			s+="		<Day>"+protect(r.daysOfTheWeek[this->days.at(i)])+"</Day>\n";
7515 		if(this->hours.at(i)>=0)
7516 			s+="		<Hour>"+protect(r.hoursOfTheDay[this->hours.at(i)])+"</Hour>\n";
7517 		s+="	</Break_Time>\n";
7518 	}
7519 
7520 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
7521 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
7522 	s+="</ConstraintBreakTimes>\n";
7523 	return s;
7524 }
7525 
getDescription(Rules & r)7526 QString ConstraintBreakTimes::getDescription(Rules& r)
7527 {
7528 	QString begin=QString("");
7529 	if(!active)
7530 		begin="X - ";
7531 
7532 	QString end=QString("");
7533 	if(!comments.isEmpty())
7534 		end=", "+tr("C: %1", "Comments").arg(comments);
7535 
7536 	QString s;
7537 	s+=tr("Break times");s+=", ";
7538 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
7539 
7540 	s+=tr("B at:", "Break at");
7541 	s+=" ";
7542 	assert(days.count()==hours.count());
7543 	for(int i=0; i<days.count(); i++){
7544 		if(this->days.at(i)>=0){
7545 			s+=r.daysOfTheWeek[this->days.at(i)];
7546 			s+=" ";
7547 		}
7548 		if(this->hours.at(i)>=0){
7549 			s+=r.hoursOfTheDay[this->hours.at(i)];
7550 		}
7551 		if(i<days.count()-1)
7552 			s+="; ";
7553 	}
7554 
7555 	return begin+s+end;
7556 }
7557 
getDetailedDescription(Rules & r)7558 QString ConstraintBreakTimes::getDetailedDescription(Rules& r)
7559 {
7560 	QString s=tr("Time constraint");s+="\n";
7561 	s+=tr("Break times");s+="\n";
7562 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
7563 
7564 	s+=tr("Break at:"); s+="\n";
7565 	assert(days.count()==hours.count());
7566 	for(int i=0; i<days.count(); i++){
7567 		if(this->days.at(i)>=0){
7568 			s+=r.daysOfTheWeek[this->days.at(i)];
7569 			s+=" ";
7570 		}
7571 		if(this->hours.at(i)>=0){
7572 			s+=r.hoursOfTheDay[this->hours.at(i)];
7573 		}
7574 		if(i<days.count()-1)
7575 			s+="; ";
7576 	}
7577 	s+="\n";
7578 
7579 	if(!active){
7580 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
7581 		s+="\n";
7582 	}
7583 	if(!comments.isEmpty()){
7584 		s+=tr("Comments=%1").arg(comments);
7585 		s+="\n";
7586 	}
7587 
7588 	return s;
7589 }
7590 
computeInternalStructure(QWidget * parent,Rules & r)7591 bool ConstraintBreakTimes::computeInternalStructure(QWidget* parent, Rules& r)
7592 {
7593 	Q_UNUSED(r);
7594 
7595 	assert(days.count()==hours.count());
7596 	for(int k=0; k<days.count(); k++){
7597 		if(this->days.at(k) >= r.nDaysPerWeek){
7598 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
7599 			 tr("Constraint break times is wrong because it refers to removed day. Please correct"
7600 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
7601 
7602 			return false;
7603 		}
7604 		if(this->hours.at(k) >= r.nHoursPerDay){
7605 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
7606 			 tr("Constraint break times is wrong because an hour is too late (after the last acceptable slot). Please correct"
7607 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
7608 
7609 			return false;
7610 		}
7611 	}
7612 
7613 	return true;
7614 }
7615 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)7616 double ConstraintBreakTimes::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
7617 {
7618 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
7619 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
7620 		c.teachersMatrixReady=true;
7621 		c.subgroupsMatrixReady=true;
7622 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
7623 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
7624 
7625 		c.changedForMatrixCalculation=false;
7626 	}
7627 
7628 	//DEPRECATED COMMENT
7629 	//For the moment, this function sums the number of hours each teacher
7630 	//is teaching in this break period.
7631 	//This function consideres all the hours, I mean if there are for example 5 weekly courses
7632 	//scheduled on that hour (which is already a broken hard restriction - we only
7633 	//are allowed 1 weekly course for a certain teacher at a certain hour) we calculate
7634 	//5 broken restrictions for this break period.
7635 	//TODO: decide if it is better to consider only 2 or 10 as a return value in this particular case
7636 	//(currently it is 10)
7637 
7638 	int nbroken;
7639 
7640 	nbroken=0;
7641 
7642 	for(int i=0; i<r.nInternalActivities; i++){
7643 		int dayact=c.times[i]%r.nDaysPerWeek;
7644 		int houract=c.times[i]/r.nDaysPerWeek;
7645 
7646 		assert(days.count()==hours.count());
7647 		for(int kk=0; kk<days.count(); kk++){
7648 			int d=days.at(kk);
7649 			int h=hours.at(kk);
7650 
7651 			int dur=r.internalActivitiesList[i].duration;
7652 			if(d==dayact && !(houract+dur<=h || houract>h))
7653 			{
7654 				nbroken++;
7655 
7656 				if(conflictsString!=nullptr){
7657 					QString s=tr("Time constraint break not respected for activity with id %1, on day %2, hour %3")
7658 						.arg(r.internalActivitiesList[i].id)
7659 						.arg(r.daysOfTheWeek[dayact])
7660 						.arg(r.hoursOfTheDay[houract]);
7661 					s+=". ";
7662 					s+=tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100));
7663 
7664 					dl.append(s);
7665 					cl.append(weightPercentage/100);
7666 
7667 					*conflictsString+= s+"\n";
7668 				}
7669 			}
7670 		}
7671 	}
7672 
7673 	if(weightPercentage==100)
7674 		assert(nbroken==0);
7675 	return weightPercentage/100 * nbroken;
7676 }
7677 
isRelatedToActivity(Rules & r,Activity * a)7678 bool ConstraintBreakTimes::isRelatedToActivity(Rules& r, Activity* a)
7679 {
7680 	Q_UNUSED(r);
7681 	Q_UNUSED(a);
7682 
7683 	return false;
7684 }
7685 
isRelatedToTeacher(Teacher * t)7686 bool ConstraintBreakTimes::isRelatedToTeacher(Teacher* t)
7687 {
7688 	Q_UNUSED(t);
7689 
7690 	return false;
7691 }
7692 
isRelatedToSubject(Subject * s)7693 bool ConstraintBreakTimes::isRelatedToSubject(Subject* s)
7694 {
7695 	Q_UNUSED(s);
7696 
7697 	return false;
7698 }
7699 
isRelatedToActivityTag(ActivityTag * s)7700 bool ConstraintBreakTimes::isRelatedToActivityTag(ActivityTag* s)
7701 {
7702 	Q_UNUSED(s);
7703 
7704 	return false;
7705 }
7706 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)7707 bool ConstraintBreakTimes::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
7708 {
7709 	Q_UNUSED(r);
7710 	Q_UNUSED(s);
7711 
7712 	return false;
7713 }
7714 
hasWrongDayOrHour(Rules & r)7715 bool ConstraintBreakTimes::hasWrongDayOrHour(Rules& r)
7716 {
7717 	assert(days.count()==hours.count());
7718 
7719 	for(int i=0; i<days.count(); i++)
7720 		if(days.at(i)<0 || days.at(i)>=r.nDaysPerWeek
7721 		 || hours.at(i)<0 || hours.at(i)>=r.nHoursPerDay)
7722 			return true;
7723 
7724 	return false;
7725 }
7726 
canRepairWrongDayOrHour(Rules & r)7727 bool ConstraintBreakTimes::canRepairWrongDayOrHour(Rules& r)
7728 {
7729 	assert(hasWrongDayOrHour(r));
7730 
7731 	return true;
7732 }
7733 
repairWrongDayOrHour(Rules & r)7734 bool ConstraintBreakTimes::repairWrongDayOrHour(Rules& r)
7735 {
7736 	assert(hasWrongDayOrHour(r));
7737 
7738 	assert(days.count()==hours.count());
7739 
7740 	QList<int> newDays;
7741 	QList<int> newHours;
7742 
7743 	for(int i=0; i<days.count(); i++)
7744 		if(days.at(i)>=0 && days.at(i)<r.nDaysPerWeek
7745 		 && hours.at(i)>=0 && hours.at(i)<r.nHoursPerDay){
7746 			newDays.append(days.at(i));
7747 			newHours.append(hours.at(i));
7748 		}
7749 
7750 	days=newDays;
7751 	hours=newHours;
7752 
7753 	r.internalStructureComputed=false;
7754 	setRulesModifiedAndOtherThings(&r);
7755 
7756 	return true;
7757 }
7758 
7759 ////////////////////////////////////////////////////////////////////////////////////////////
7760 ////////////////////////////////////////////////////////////////////////////////////////////
7761 
ConstraintStudentsMaxGapsPerWeek()7762 ConstraintStudentsMaxGapsPerWeek::ConstraintStudentsMaxGapsPerWeek()
7763 	: TimeConstraint()
7764 {
7765 	this->type = CONSTRAINT_STUDENTS_MAX_GAPS_PER_WEEK;
7766 }
7767 
ConstraintStudentsMaxGapsPerWeek(double wp,int mg)7768 ConstraintStudentsMaxGapsPerWeek::ConstraintStudentsMaxGapsPerWeek(double wp, int mg)
7769 	: TimeConstraint(wp)
7770 {
7771 	this->type = CONSTRAINT_STUDENTS_MAX_GAPS_PER_WEEK;
7772 	this->maxGaps=mg;
7773 }
7774 
computeInternalStructure(QWidget * parent,Rules & r)7775 bool ConstraintStudentsMaxGapsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
7776 {
7777 	Q_UNUSED(parent);
7778 	Q_UNUSED(r);
7779 
7780 	return true;
7781 }
7782 
hasInactiveActivities(Rules & r)7783 bool ConstraintStudentsMaxGapsPerWeek::hasInactiveActivities(Rules& r)
7784 {
7785 	Q_UNUSED(r);
7786 	return false;
7787 }
7788 
getXmlDescription(Rules & r)7789 QString ConstraintStudentsMaxGapsPerWeek::getXmlDescription(Rules& r)
7790 {
7791 	Q_UNUSED(r);
7792 
7793 	QString s="<ConstraintStudentsMaxGapsPerWeek>\n";
7794 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
7795 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
7796 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
7797 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
7798 	s+="</ConstraintStudentsMaxGapsPerWeek>\n";
7799 	return s;
7800 }
7801 
getDescription(Rules & r)7802 QString ConstraintStudentsMaxGapsPerWeek::getDescription(Rules& r)
7803 {
7804 	Q_UNUSED(r);
7805 
7806 	QString begin=QString("");
7807 	if(!active)
7808 		begin="X - ";
7809 
7810 	QString end=QString("");
7811 	if(!comments.isEmpty())
7812 		end=", "+tr("C: %1", "Comments").arg(comments);
7813 
7814 	QString s;
7815 	s+=tr("Students max gaps per week");s+=", ";
7816 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
7817 	s+=tr("MG:%1", "Max gaps (per week)").arg(this->maxGaps);
7818 
7819 	return begin+s+end;
7820 }
7821 
getDetailedDescription(Rules & r)7822 QString ConstraintStudentsMaxGapsPerWeek::getDetailedDescription(Rules& r)
7823 {
7824 	Q_UNUSED(r);
7825 
7826 	QString s=tr("Time constraint");s+="\n";
7827 	s+=tr("All students must respect the maximum number of gaps per week");s+="\n";
7828 	s+=tr("(breaks and students set not available not counted)");s+="\n";
7829 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
7830 	s+=tr("Maximum gaps per week=%1").arg(this->maxGaps);s+="\n";
7831 
7832 	if(!active){
7833 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
7834 		s+="\n";
7835 	}
7836 	if(!comments.isEmpty()){
7837 		s+=tr("Comments=%1").arg(comments);
7838 		s+="\n";
7839 	}
7840 
7841 	return s;
7842 }
7843 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)7844 double ConstraintStudentsMaxGapsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
7845 {
7846 	//returns a number equal to the number of gaps of the subgroups (in hours)
7847 
7848 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
7849 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
7850 		c.teachersMatrixReady=true;
7851 		c.subgroupsMatrixReady=true;
7852 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
7853 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
7854 
7855 		c.changedForMatrixCalculation=false;
7856 	}
7857 
7858 	int nGaps;
7859 	int tmp;
7860 	int i;
7861 
7862 	int tIllegalGaps=0;
7863 
7864 	for(i=0; i<r.nInternalSubgroups; i++){
7865 		nGaps=0;
7866 		for(int j=0; j<r.nDaysPerWeek; j++){
7867 			int k;
7868 			tmp=0;
7869 			for(k=0; k<r.nHoursPerDay; k++)
7870 				if(subgroupsMatrix[i][j][k]>0){
7871 					assert(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]);
7872 					break;
7873 				}
7874 			for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
7875 				if(subgroupsMatrix[i][j][k]>0){
7876 					nGaps+=tmp;
7877 					tmp=0;
7878 				}
7879 				else
7880 					tmp++;
7881 			}
7882 		}
7883 
7884 		int illegalGaps=nGaps-this->maxGaps;
7885 		if(illegalGaps<0)
7886 			illegalGaps=0;
7887 
7888 		if(illegalGaps>0 && conflictsString!=nullptr){
7889 			QString s=tr("Time constraint students max gaps per week broken for subgroup: %1, it has %2 extra gaps, conflicts increase=%3")
7890 			 .arg(r.internalSubgroupsList[i]->name)
7891 			 .arg(illegalGaps)
7892 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(illegalGaps*weightPercentage/100));
7893 
7894 			dl.append(s);
7895 			cl.append(illegalGaps*weightPercentage/100);
7896 
7897 			*conflictsString+= s+"\n";
7898 		}
7899 
7900 		tIllegalGaps+=illegalGaps;
7901 	}
7902 
7903 	if(c.nPlacedActivities==r.nInternalActivities)
7904 		if(weightPercentage==100)    //for partial solutions it might be broken
7905 			assert(tIllegalGaps==0);
7906 	return weightPercentage/100 * tIllegalGaps;
7907 }
7908 
isRelatedToActivity(Rules & r,Activity * a)7909 bool ConstraintStudentsMaxGapsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
7910 {
7911 	Q_UNUSED(r);
7912 	Q_UNUSED(a);
7913 
7914 	return false;
7915 }
7916 
isRelatedToTeacher(Teacher * t)7917 bool ConstraintStudentsMaxGapsPerWeek::isRelatedToTeacher(Teacher* t)
7918 {
7919 	Q_UNUSED(t);
7920 
7921 	return false;
7922 }
7923 
isRelatedToSubject(Subject * s)7924 bool ConstraintStudentsMaxGapsPerWeek::isRelatedToSubject(Subject* s)
7925 {
7926 	Q_UNUSED(s);
7927 
7928 	return false;
7929 }
7930 
isRelatedToActivityTag(ActivityTag * s)7931 bool ConstraintStudentsMaxGapsPerWeek::isRelatedToActivityTag(ActivityTag* s)
7932 {
7933 	Q_UNUSED(s);
7934 
7935 	return false;
7936 }
7937 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)7938 bool ConstraintStudentsMaxGapsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
7939 {
7940 	Q_UNUSED(r);
7941 	Q_UNUSED(s);
7942 
7943 	return true;
7944 }
7945 
hasWrongDayOrHour(Rules & r)7946 bool ConstraintStudentsMaxGapsPerWeek::hasWrongDayOrHour(Rules& r)
7947 {
7948 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
7949 		return true;
7950 
7951 	return false;
7952 }
7953 
canRepairWrongDayOrHour(Rules & r)7954 bool ConstraintStudentsMaxGapsPerWeek::canRepairWrongDayOrHour(Rules& r)
7955 {
7956 	assert(hasWrongDayOrHour(r));
7957 
7958 	return true;
7959 }
7960 
repairWrongDayOrHour(Rules & r)7961 bool ConstraintStudentsMaxGapsPerWeek::repairWrongDayOrHour(Rules& r)
7962 {
7963 	assert(hasWrongDayOrHour(r));
7964 
7965 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
7966 		maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
7967 
7968 	return true;
7969 }
7970 
7971 ////////////////////////////////////////////////////////////////////////////////////////////
7972 ////////////////////////////////////////////////////////////////////////////////////////////
7973 
ConstraintStudentsSetMaxGapsPerWeek()7974 ConstraintStudentsSetMaxGapsPerWeek::ConstraintStudentsSetMaxGapsPerWeek()
7975 	: TimeConstraint()
7976 {
7977 	this->type = CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_WEEK;
7978 }
7979 
ConstraintStudentsSetMaxGapsPerWeek(double wp,int mg,const QString & st)7980 ConstraintStudentsSetMaxGapsPerWeek::ConstraintStudentsSetMaxGapsPerWeek(double wp, int mg, const QString& st )
7981 	: TimeConstraint(wp)
7982 {
7983 	this->type = CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_WEEK;
7984 	this->maxGaps=mg;
7985 	this->students = st;
7986 }
7987 
computeInternalStructure(QWidget * parent,Rules & r)7988 bool ConstraintStudentsSetMaxGapsPerWeek::computeInternalStructure(QWidget* parent, Rules& r){
7989 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
7990 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
7991 
7992 	if(ss==nullptr){
7993 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
7994 		 tr("Constraint students set max gaps per week is wrong because it refers to inexistent students set."
7995 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
7996 
7997 		return false;
7998 	}
7999 
8000 	assert(ss!=nullptr);
8001 
8002 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
8003 	/*this->iSubgroupsList.clear();
8004 	if(ss->type==STUDENTS_SUBGROUP){
8005 		int tmp;
8006 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
8007 		assert(tmp>=0);
8008 		assert(tmp<r.nInternalSubgroups);
8009 		if(!this->iSubgroupsList.contains(tmp))
8010 			this->iSubgroupsList.append(tmp);
8011 	}
8012 	else if(ss->type==STUDENTS_GROUP){
8013 		StudentsGroup* stg=(StudentsGroup*)ss;
8014 		for(int i=0; i<stg->subgroupsList.size(); i++){
8015 			StudentsSubgroup* sts=stg->subgroupsList[i];
8016 			int tmp;
8017 			tmp=sts->indexInInternalSubgroupsList;
8018 			assert(tmp>=0);
8019 			assert(tmp<r.nInternalSubgroups);
8020 			if(!this->iSubgroupsList.contains(tmp))
8021 				this->iSubgroupsList.append(tmp);
8022 		}
8023 	}
8024 	else if(ss->type==STUDENTS_YEAR){
8025 		StudentsYear* sty=(StudentsYear*)ss;
8026 		for(int i=0; i<sty->groupsList.size(); i++){
8027 			StudentsGroup* stg=sty->groupsList[i];
8028 			for(int j=0; j<stg->subgroupsList.size(); j++){
8029 				StudentsSubgroup* sts=stg->subgroupsList[j];
8030 				int tmp;
8031 				tmp=sts->indexInInternalSubgroupsList;
8032 				assert(tmp>=0);
8033 				assert(tmp<r.nInternalSubgroups);
8034 				if(!this->iSubgroupsList.contains(tmp))
8035 					this->iSubgroupsList.append(tmp);
8036 			}
8037 		}
8038 	}
8039 	else
8040 		assert(0);*/
8041 
8042 	return true;
8043 }
8044 
hasInactiveActivities(Rules & r)8045 bool ConstraintStudentsSetMaxGapsPerWeek::hasInactiveActivities(Rules& r)
8046 {
8047 	Q_UNUSED(r);
8048 	return false;
8049 }
8050 
getXmlDescription(Rules & r)8051 QString ConstraintStudentsSetMaxGapsPerWeek::getXmlDescription(Rules& r)
8052 {
8053 	Q_UNUSED(r);
8054 
8055 	QString s="<ConstraintStudentsSetMaxGapsPerWeek>\n";
8056 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
8057 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
8058 	s+="	<Students>"; s+=protect(this->students); s+="</Students>\n";
8059 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
8060 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
8061 	s+="</ConstraintStudentsSetMaxGapsPerWeek>\n";
8062 	return s;
8063 }
8064 
getDescription(Rules & r)8065 QString ConstraintStudentsSetMaxGapsPerWeek::getDescription(Rules& r)
8066 {
8067 	Q_UNUSED(r);
8068 
8069 	QString begin=QString("");
8070 	if(!active)
8071 		begin="X - ";
8072 
8073 	QString end=QString("");
8074 	if(!comments.isEmpty())
8075 		end=", "+tr("C: %1", "Comments").arg(comments);
8076 
8077 	QString s;
8078 	s+=tr("Students set max gaps per week"); s+=", ";
8079 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage)); s+=", ";
8080 	s+=tr("MG:%1", "Max gaps (per week)").arg(this->maxGaps);s+=", ";
8081 	s+=tr("St:%1", "Students").arg(this->students);
8082 
8083 	return begin+s+end;
8084 }
8085 
getDetailedDescription(Rules & r)8086 QString ConstraintStudentsSetMaxGapsPerWeek::getDetailedDescription(Rules& r)
8087 {
8088 	Q_UNUSED(r);
8089 
8090 	QString s=tr("Time constraint");s+="\n";
8091 	s+=tr("A students set must respect the maximum number of gaps per week");s+="\n";
8092 	s+=tr("(breaks and students set not available not counted)");s+="\n";
8093 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
8094 	s+=tr("Maximum gaps per week=%1").arg(this->maxGaps);s+="\n";
8095 	s+=tr("Students=%1").arg(this->students); s+="\n";
8096 
8097 	if(!active){
8098 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
8099 		s+="\n";
8100 	}
8101 	if(!comments.isEmpty()){
8102 		s+=tr("Comments=%1").arg(comments);
8103 		s+="\n";
8104 	}
8105 
8106 	return s;
8107 }
8108 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)8109 double ConstraintStudentsSetMaxGapsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
8110 {
8111 	//OLD COMMENT
8112 	//returns a number equal to the number of gaps of the subgroups (in hours)
8113 
8114 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
8115 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
8116 		c.teachersMatrixReady=true;
8117 		c.subgroupsMatrixReady=true;
8118 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
8119 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
8120 
8121 		c.changedForMatrixCalculation=false;
8122 	}
8123 
8124 	int nGaps;
8125 	int tmp;
8126 
8127 	int tIllegalGaps=0;
8128 
8129 	for(int sg=0; sg<this->iSubgroupsList.count(); sg++){
8130 		nGaps=0;
8131 		int i=this->iSubgroupsList.at(sg);
8132 		for(int j=0; j<r.nDaysPerWeek; j++){
8133 			int k;
8134 			tmp=0;
8135 			for(k=0; k<r.nHoursPerDay; k++)
8136 				if(subgroupsMatrix[i][j][k]>0){
8137 					assert(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]);
8138 					break;
8139 				}
8140 			for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
8141 				if(subgroupsMatrix[i][j][k]>0){
8142 					nGaps+=tmp;
8143 					tmp=0;
8144 				}
8145 				else
8146 					tmp++;
8147 			}
8148 		}
8149 
8150 		int illegalGaps=nGaps-this->maxGaps;
8151 		if(illegalGaps<0)
8152 			illegalGaps=0;
8153 
8154 		if(illegalGaps>0 && conflictsString!=nullptr){
8155 			QString s=tr("Time constraint students set max gaps per week broken for subgroup: %1, extra gaps=%2, conflicts increase=%3")
8156 			 .arg(r.internalSubgroupsList[i]->name)
8157 			 .arg(illegalGaps)
8158 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*illegalGaps));
8159 
8160 			dl.append(s);
8161 			cl.append(weightPercentage/100*illegalGaps);
8162 
8163 			*conflictsString+= s+"\n";
8164 		}
8165 
8166 		tIllegalGaps+=illegalGaps;
8167 	}
8168 
8169 	if(c.nPlacedActivities==r.nInternalActivities)
8170 		if(weightPercentage==100)     //for partial solutions it might be broken
8171 			assert(tIllegalGaps==0);
8172 	return weightPercentage/100 * tIllegalGaps;
8173 }
8174 
isRelatedToActivity(Rules & r,Activity * a)8175 bool ConstraintStudentsSetMaxGapsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
8176 {
8177 	Q_UNUSED(r);
8178 	Q_UNUSED(a);
8179 
8180 	return false;
8181 }
8182 
isRelatedToTeacher(Teacher * t)8183 bool ConstraintStudentsSetMaxGapsPerWeek::isRelatedToTeacher(Teacher* t)
8184 {
8185 	Q_UNUSED(t);
8186 
8187 	return false;
8188 }
8189 
isRelatedToSubject(Subject * s)8190 bool ConstraintStudentsSetMaxGapsPerWeek::isRelatedToSubject(Subject* s)
8191 {
8192 	Q_UNUSED(s);
8193 
8194 	return false;
8195 }
8196 
isRelatedToActivityTag(ActivityTag * s)8197 bool ConstraintStudentsSetMaxGapsPerWeek::isRelatedToActivityTag(ActivityTag* s)
8198 {
8199 	Q_UNUSED(s);
8200 
8201 	return false;
8202 }
8203 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)8204 bool ConstraintStudentsSetMaxGapsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
8205 {
8206 	return r.setsShareStudents(this->students, s->name);
8207 }
8208 
hasWrongDayOrHour(Rules & r)8209 bool ConstraintStudentsSetMaxGapsPerWeek::hasWrongDayOrHour(Rules& r)
8210 {
8211 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
8212 		return true;
8213 
8214 	return false;
8215 }
8216 
canRepairWrongDayOrHour(Rules & r)8217 bool ConstraintStudentsSetMaxGapsPerWeek::canRepairWrongDayOrHour(Rules& r)
8218 {
8219 	assert(hasWrongDayOrHour(r));
8220 
8221 	return true;
8222 }
8223 
repairWrongDayOrHour(Rules & r)8224 bool ConstraintStudentsSetMaxGapsPerWeek::repairWrongDayOrHour(Rules& r)
8225 {
8226 	assert(hasWrongDayOrHour(r));
8227 
8228 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
8229 		maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
8230 
8231 	return true;
8232 }
8233 
8234 ////////////////////////////////////////////////////////////////////////////////////////////
8235 ////////////////////////////////////////////////////////////////////////////////////////////
8236 
ConstraintStudentsEarlyMaxBeginningsAtSecondHour()8237 ConstraintStudentsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsEarlyMaxBeginningsAtSecondHour()
8238 	: TimeConstraint()
8239 {
8240 	this->type = CONSTRAINT_STUDENTS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
8241 }
8242 
ConstraintStudentsEarlyMaxBeginningsAtSecondHour(double wp,int mBSH)8243 ConstraintStudentsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsEarlyMaxBeginningsAtSecondHour(double wp, int mBSH)
8244 	: TimeConstraint(wp)
8245 {
8246 	this->type = CONSTRAINT_STUDENTS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
8247 	this->maxBeginningsAtSecondHour=mBSH;
8248 }
8249 
computeInternalStructure(QWidget * parent,Rules & r)8250 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
8251 {
8252 	Q_UNUSED(parent);
8253 	Q_UNUSED(r);
8254 
8255 	return true;
8256 }
8257 
hasInactiveActivities(Rules & r)8258 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
8259 {
8260 	Q_UNUSED(r);
8261 	return false;
8262 }
8263 
getXmlDescription(Rules & r)8264 QString ConstraintStudentsEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
8265 {
8266 	Q_UNUSED(r);
8267 
8268 	QString s="<ConstraintStudentsEarlyMaxBeginningsAtSecondHour>\n";
8269 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
8270 	s+="	<Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
8271 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
8272 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
8273 	s+="</ConstraintStudentsEarlyMaxBeginningsAtSecondHour>\n";
8274 	return s;
8275 }
8276 
getDescription(Rules & r)8277 QString ConstraintStudentsEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
8278 {
8279 	Q_UNUSED(r);
8280 
8281 	QString begin=QString("");
8282 	if(!active)
8283 		begin="X - ";
8284 
8285 	QString end=QString("");
8286 	if(!comments.isEmpty())
8287 		end=", "+tr("C: %1", "Comments").arg(comments);
8288 
8289 	QString s;
8290 	s+=tr("Students must arrive early, respecting maximum %1 arrivals at second hour")
8291 	 .arg(this->maxBeginningsAtSecondHour);
8292 	s+=", ";
8293 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
8294 
8295 	return begin+s+end;
8296 }
8297 
getDetailedDescription(Rules & r)8298 QString ConstraintStudentsEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
8299 {
8300 	Q_UNUSED(r);
8301 
8302 	QString s=tr("Time constraint");s+="\n";
8303 	s+=tr("All students must begin their activities early, respecting maximum %1 later arrivals, at second hour")
8304 	 .arg(this->maxBeginningsAtSecondHour);s+="\n";
8305 	s+=tr("(breaks and students set not available not counted)");s+="\n";
8306 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
8307 
8308 	if(!active){
8309 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
8310 		s+="\n";
8311 	}
8312 	if(!comments.isEmpty()){
8313 		s+=tr("Comments=%1").arg(comments);
8314 		s+="\n";
8315 	}
8316 
8317 	return s;
8318 }
8319 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)8320 double ConstraintStudentsEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
8321 {
8322 	//considers the condition that the hours of subgroups begin as early as possible
8323 
8324 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
8325 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
8326 		c.teachersMatrixReady=true;
8327 		c.subgroupsMatrixReady=true;
8328 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
8329 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
8330 
8331 		c.changedForMatrixCalculation=false;
8332 	}
8333 
8334 	int conflTotal=0;
8335 
8336 	for(int i=0; i<r.nInternalSubgroups; i++){
8337 		int nGapsFirstHour=0;
8338 		for(int j=0; j<r.nDaysPerWeek; j++){
8339 			int k;
8340 			for(k=0; k<r.nHoursPerDay; k++)
8341 				if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k])
8342 					break;
8343 
8344 			bool firstHourOccupied=false;
8345 			if(k<r.nHoursPerDay)
8346 				if(subgroupsMatrix[i][j][k]>0)
8347 					firstHourOccupied=true;
8348 
8349 			bool dayOccupied=firstHourOccupied;
8350 
8351 			bool illegalGap=false;
8352 
8353 			if(!dayOccupied){
8354 				for(k++; k<r.nHoursPerDay; k++){
8355 					if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
8356 						if(subgroupsMatrix[i][j][k]>0){
8357 							dayOccupied=true;
8358 							break;
8359 						}
8360 						else{
8361 							illegalGap=true;
8362 						}
8363 					}
8364 				}
8365 			}
8366 
8367 			if(dayOccupied && illegalGap){
8368 				if(conflictsString!=nullptr){
8369 					QString s=tr("Constraint students early max %1 beginnings at second hour broken for subgroup %2, on day %3,"
8370 					 " because students have an illegal gap, increases conflicts total by %4")
8371 					 .arg(this->maxBeginningsAtSecondHour)
8372 					 .arg(r.internalSubgroupsList[i]->name)
8373 					 .arg(r.daysOfTheWeek[j])
8374 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
8375 
8376 					dl.append(s);
8377 					cl.append(1*weightPercentage/100);
8378 
8379 					*conflictsString+= s+"\n";
8380 
8381 					conflTotal+=1;
8382 				}
8383 
8384 				if(c.nPlacedActivities==r.nInternalActivities){
8385 					assert(0);
8386 				}
8387 			}
8388 
8389 			if(dayOccupied && !firstHourOccupied)
8390 				nGapsFirstHour++;
8391 		}
8392 
8393 		if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
8394 			if(conflictsString!=nullptr){
8395 				QString s=tr("Constraint students early max %1 beginnings at second hour broken for subgroup %2,"
8396 				 " because students have too many arrivals at second hour, increases conflicts total by %3")
8397 				 .arg(this->maxBeginningsAtSecondHour)
8398 				 .arg(r.internalSubgroupsList[i]->name)
8399 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
8400 
8401 				dl.append(s);
8402 				cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
8403 
8404 				*conflictsString+= s+"\n";
8405 
8406 				conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
8407 			}
8408 
8409 			if(c.nPlacedActivities==r.nInternalActivities){
8410 				assert(0);
8411 			}
8412 		}
8413 	}
8414 
8415 	if(c.nPlacedActivities==r.nInternalActivities)
8416 		if(weightPercentage==100)    //might be broken for partial solutions
8417 			assert(conflTotal==0);
8418 	return weightPercentage/100 * conflTotal;
8419 }
8420 
isRelatedToActivity(Rules & r,Activity * a)8421 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
8422 {
8423 	Q_UNUSED(r);
8424 	Q_UNUSED(a);
8425 
8426 	return false;
8427 }
8428 
isRelatedToTeacher(Teacher * t)8429 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
8430 {
8431 	Q_UNUSED(t);
8432 
8433 	return false;
8434 }
8435 
isRelatedToSubject(Subject * s)8436 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
8437 {
8438 	Q_UNUSED(s);
8439 
8440 	return false;
8441 }
8442 
isRelatedToActivityTag(ActivityTag * s)8443 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
8444 {
8445 	Q_UNUSED(s);
8446 
8447 	return false;
8448 }
8449 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)8450 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
8451 {
8452 	Q_UNUSED(r);
8453 	Q_UNUSED(s);
8454 
8455 	return true;
8456 }
8457 
hasWrongDayOrHour(Rules & r)8458 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
8459 {
8460 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek)
8461 		return true;
8462 
8463 	return false;
8464 }
8465 
canRepairWrongDayOrHour(Rules & r)8466 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
8467 {
8468 	assert(hasWrongDayOrHour(r));
8469 
8470 	return true;
8471 }
8472 
repairWrongDayOrHour(Rules & r)8473 bool ConstraintStudentsEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
8474 {
8475 	assert(hasWrongDayOrHour(r));
8476 
8477 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek)
8478 		maxBeginningsAtSecondHour=r.nDaysPerWeek;
8479 
8480 	return true;
8481 }
8482 
8483 ////////////////////////////////////////////////////////////////////////////////////////////
8484 ////////////////////////////////////////////////////////////////////////////////////////////
8485 
ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour()8486 ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour()
8487 	: TimeConstraint()
8488 {
8489 	this->type = CONSTRAINT_STUDENTS_SET_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
8490 }
8491 
ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour(double wp,int mBSH,const QString & students)8492 ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour(double wp, int mBSH, const QString& students)
8493 	: TimeConstraint(wp)
8494 {
8495 	this->type = CONSTRAINT_STUDENTS_SET_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
8496 	this->students=students;
8497 	this->maxBeginningsAtSecondHour=mBSH;
8498 }
8499 
computeInternalStructure(QWidget * parent,Rules & r)8500 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
8501 {
8502 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
8503 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
8504 
8505 	if(ss==nullptr){
8506 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
8507 		 tr("Constraint students set early is wrong because it refers to inexistent students set."
8508 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
8509 
8510 		return false;
8511 	}
8512 
8513 	assert(ss!=nullptr);
8514 
8515 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
8516 	/*this->iSubgroupsList.clear();
8517 	if(ss->type==STUDENTS_SUBGROUP){
8518 		int tmp;
8519 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
8520 		assert(tmp>=0);
8521 		assert(tmp<r.nInternalSubgroups);
8522 		if(!this->iSubgroupsList.contains(tmp))
8523 			this->iSubgroupsList.append(tmp);
8524 	}
8525 	else if(ss->type==STUDENTS_GROUP){
8526 		StudentsGroup* stg=(StudentsGroup*)ss;
8527 		for(int i=0; i<stg->subgroupsList.size(); i++){
8528 			StudentsSubgroup* sts=stg->subgroupsList[i];
8529 			int tmp;
8530 			tmp=sts->indexInInternalSubgroupsList;
8531 			assert(tmp>=0);
8532 			assert(tmp<r.nInternalSubgroups);
8533 			if(!this->iSubgroupsList.contains(tmp))
8534 				this->iSubgroupsList.append(tmp);
8535 		}
8536 	}
8537 	else if(ss->type==STUDENTS_YEAR){
8538 		StudentsYear* sty=(StudentsYear*)ss;
8539 		for(int i=0; i<sty->groupsList.size(); i++){
8540 			StudentsGroup* stg=sty->groupsList[i];
8541 			for(int j=0; j<stg->subgroupsList.size(); j++){
8542 				StudentsSubgroup* sts=stg->subgroupsList[j];
8543 				int tmp;
8544 				tmp=sts->indexInInternalSubgroupsList;
8545 				assert(tmp>=0);
8546 				assert(tmp<r.nInternalSubgroups);
8547 				if(!this->iSubgroupsList.contains(tmp))
8548 					this->iSubgroupsList.append(tmp);
8549 			}
8550 		}
8551 	}
8552 	else
8553 		assert(0);*/
8554 	return true;
8555 }
8556 
hasInactiveActivities(Rules & r)8557 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
8558 {
8559 	Q_UNUSED(r);
8560 	return false;
8561 }
8562 
getXmlDescription(Rules & r)8563 QString ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
8564 {
8565 	Q_UNUSED(r);
8566 
8567 	QString s="<ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour>\n";
8568 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
8569 	s+="	<Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
8570 	s+="	<Students>"+protect(this->students)+"</Students>\n";
8571 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
8572 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
8573 	s+="</ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour>\n";
8574 	return s;
8575 }
8576 
getDescription(Rules & r)8577 QString ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
8578 {
8579 	Q_UNUSED(r);
8580 
8581 	QString begin=QString("");
8582 	if(!active)
8583 		begin="X - ";
8584 
8585 	QString end=QString("");
8586 	if(!comments.isEmpty())
8587 		end=", "+tr("C: %1", "Comments").arg(comments);
8588 
8589 	QString s;
8590 
8591 	s+=tr("Students set must arrive early, respecting maximum %1 arrivals at second hour")
8592 	 .arg(this->maxBeginningsAtSecondHour); s+=", ";
8593 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
8594 	s+=tr("St:%1", "Students set").arg(this->students);
8595 
8596 	return begin+s+end;
8597 }
8598 
getDetailedDescription(Rules & r)8599 QString ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
8600 {
8601 	Q_UNUSED(r);
8602 
8603 	QString s=tr("Time constraint");s+="\n";
8604 
8605 	s+=tr("A students set must begin its activities early, respecting a maximum number of later arrivals, at second hour"); s+="\n";
8606 	s+=tr("(breaks and students set not available not counted)");s+="\n";
8607 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
8608 	s+=tr("Students set=%1").arg(this->students); s+="\n";
8609 	s+=tr("Maximum number of arrivals at the second hour=%1").arg(this->maxBeginningsAtSecondHour);s+="\n";
8610 
8611 	if(!active){
8612 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
8613 		s+="\n";
8614 	}
8615 	if(!comments.isEmpty()){
8616 		s+=tr("Comments=%1").arg(comments);
8617 		s+="\n";
8618 	}
8619 
8620 	return s;
8621 }
8622 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)8623 double ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
8624 {
8625 	//considers the condition that the hours of subgroups begin as early as possible
8626 
8627 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
8628 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
8629 		c.teachersMatrixReady=true;
8630 		c.subgroupsMatrixReady=true;
8631 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
8632 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
8633 
8634 		c.changedForMatrixCalculation=false;
8635 	}
8636 
8637 	int conflTotal=0;
8638 
8639 	for(int i : qAsConst(this->iSubgroupsList)){
8640 		int nGapsFirstHour=0;
8641 		for(int j=0; j<r.nDaysPerWeek; j++){
8642 			int k;
8643 			for(k=0; k<r.nHoursPerDay; k++)
8644 				if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k])
8645 					break;
8646 
8647 			bool firstHourOccupied=false;
8648 			if(k<r.nHoursPerDay)
8649 				if(subgroupsMatrix[i][j][k]>0)
8650 					firstHourOccupied=true;
8651 
8652 			bool dayOccupied=firstHourOccupied;
8653 
8654 			bool illegalGap=false;
8655 
8656 			if(!dayOccupied){
8657 				for(k++; k<r.nHoursPerDay; k++){
8658 					if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
8659 						if(subgroupsMatrix[i][j][k]>0){
8660 							dayOccupied=true;
8661 							break;
8662 						}
8663 						else{
8664 							illegalGap=true;
8665 						}
8666 					}
8667 				}
8668 			}
8669 
8670 			if(dayOccupied && illegalGap){
8671 				if(conflictsString!=nullptr){
8672 					QString s=tr("Constraint students set early max %1 beginnings at second hour broken for subgroup %2, on day %3,"
8673 					 " because students have an illegal gap, increases conflicts total by %4")
8674 					 .arg(this->maxBeginningsAtSecondHour)
8675 					 .arg(r.internalSubgroupsList[i]->name)
8676 					 .arg(r.daysOfTheWeek[j])
8677 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
8678 
8679 					dl.append(s);
8680 					cl.append(1*weightPercentage/100);
8681 
8682 					*conflictsString+= s+"\n";
8683 
8684 					conflTotal+=1;
8685 				}
8686 
8687 				if(c.nPlacedActivities==r.nInternalActivities)
8688 					assert(0);
8689 			}
8690 
8691 			if(dayOccupied && !firstHourOccupied)
8692 				nGapsFirstHour++;
8693 		}
8694 
8695 		if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
8696 			if(conflictsString!=nullptr){
8697 				QString s=tr("Constraint students set early max %1 beginnings at second hour broken for subgroup %2,"
8698 				 " because students have too many arrivals at second hour, increases conflicts total by %3")
8699 				 .arg(this->maxBeginningsAtSecondHour)
8700 				 .arg(r.internalSubgroupsList[i]->name)
8701 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
8702 
8703 				dl.append(s);
8704 				cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
8705 
8706 				*conflictsString+= s+"\n";
8707 
8708 				conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
8709 			}
8710 
8711 			if(c.nPlacedActivities==r.nInternalActivities)
8712 				assert(0);
8713 		}
8714 	}
8715 
8716 	if(c.nPlacedActivities==r.nInternalActivities)
8717 		if(weightPercentage==100)    //might be broken for partial solutions
8718 			assert(conflTotal==0);
8719 	return weightPercentage/100 * conflTotal;
8720 }
8721 
isRelatedToActivity(Rules & r,Activity * a)8722 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
8723 {
8724 	Q_UNUSED(r);
8725 	Q_UNUSED(a);
8726 
8727 	return false;
8728 }
8729 
isRelatedToTeacher(Teacher * t)8730 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
8731 {
8732 	Q_UNUSED(t);
8733 
8734 	return false;
8735 }
8736 
isRelatedToSubject(Subject * s)8737 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
8738 {
8739 	Q_UNUSED(s);
8740 
8741 	return false;
8742 }
8743 
isRelatedToActivityTag(ActivityTag * s)8744 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
8745 {
8746 	Q_UNUSED(s);
8747 
8748 	return false;
8749 }
8750 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)8751 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
8752 {
8753 	return r.setsShareStudents(this->students, s->name);
8754 }
8755 
hasWrongDayOrHour(Rules & r)8756 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
8757 {
8758 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek)
8759 		return true;
8760 
8761 	return false;
8762 }
8763 
canRepairWrongDayOrHour(Rules & r)8764 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
8765 {
8766 	assert(hasWrongDayOrHour(r));
8767 
8768 	return true;
8769 }
8770 
repairWrongDayOrHour(Rules & r)8771 bool ConstraintStudentsSetEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
8772 {
8773 	assert(hasWrongDayOrHour(r));
8774 
8775 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek)
8776 		maxBeginningsAtSecondHour=r.nDaysPerWeek;
8777 
8778 	return true;
8779 }
8780 
8781 ////////////////////////////////////////////////////////////////////////////////////////////
8782 ////////////////////////////////////////////////////////////////////////////////////////////
8783 
ConstraintStudentsMaxHoursDaily()8784 ConstraintStudentsMaxHoursDaily::ConstraintStudentsMaxHoursDaily()
8785 	: TimeConstraint()
8786 {
8787 	this->type = CONSTRAINT_STUDENTS_MAX_HOURS_DAILY;
8788 	this->maxHoursDaily = -1;
8789 }
8790 
ConstraintStudentsMaxHoursDaily(double wp,int maxnh)8791 ConstraintStudentsMaxHoursDaily::ConstraintStudentsMaxHoursDaily(double wp, int maxnh)
8792 	: TimeConstraint(wp)
8793 {
8794 	this->maxHoursDaily = maxnh;
8795 	this->type = CONSTRAINT_STUDENTS_MAX_HOURS_DAILY;
8796 }
8797 
computeInternalStructure(QWidget * parent,Rules & r)8798 bool ConstraintStudentsMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
8799 {
8800 	Q_UNUSED(parent);
8801 	Q_UNUSED(r);
8802 
8803 	return true;
8804 }
8805 
hasInactiveActivities(Rules & r)8806 bool ConstraintStudentsMaxHoursDaily::hasInactiveActivities(Rules& r)
8807 {
8808 	Q_UNUSED(r);
8809 	return false;
8810 }
8811 
getXmlDescription(Rules & r)8812 QString ConstraintStudentsMaxHoursDaily::getXmlDescription(Rules& r)
8813 {
8814 	Q_UNUSED(r);
8815 
8816 	QString s="<ConstraintStudentsMaxHoursDaily>\n";
8817 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
8818 	if(this->maxHoursDaily>=0)
8819 		s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
8820 	else
8821 		assert(0);
8822 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
8823 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
8824 	s+="</ConstraintStudentsMaxHoursDaily>\n";
8825 	return s;
8826 }
8827 
getDescription(Rules & r)8828 QString ConstraintStudentsMaxHoursDaily::getDescription(Rules& r)
8829 {
8830 	Q_UNUSED(r);
8831 
8832 	QString begin=QString("");
8833 	if(!active)
8834 		begin="X - ";
8835 
8836 	QString end=QString("");
8837 	if(!comments.isEmpty())
8838 		end=", "+tr("C: %1", "Comments").arg(comments);
8839 
8840 	QString s;
8841 	s+=tr("Students max hours daily");s+=", ";
8842 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
8843 	s+=tr("MH:%1", "Max hours (daily)").arg(this->maxHoursDaily);
8844 
8845 	return begin+s+end;
8846 }
8847 
getDetailedDescription(Rules & r)8848 QString ConstraintStudentsMaxHoursDaily::getDetailedDescription(Rules& r)
8849 {
8850 	Q_UNUSED(r);
8851 
8852 	QString s=tr("Time constraint");s+="\n";
8853 	s+=tr("All students must respect the maximum number of hours daily");s+="\n";
8854 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
8855 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
8856 
8857 	if(!active){
8858 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
8859 		s+="\n";
8860 	}
8861 	if(!comments.isEmpty()){
8862 		s+=tr("Comments=%1").arg(comments);
8863 		s+="\n";
8864 	}
8865 
8866 	return s;
8867 }
8868 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)8869 double ConstraintStudentsMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
8870 {
8871 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
8872 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
8873 		c.teachersMatrixReady=true;
8874 		c.subgroupsMatrixReady=true;
8875 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
8876 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
8877 
8878 		c.changedForMatrixCalculation=false;
8879 	}
8880 
8881 	int tmp;
8882 	int too_much;
8883 
8884 	assert(this->maxHoursDaily>=0);
8885 
8886 	if(1){
8887 		too_much=0;
8888 		for(int i=0; i<r.nInternalSubgroups; i++)
8889 			for(int j=0; j<r.nDaysPerWeek; j++){
8890 				tmp=0;
8891 				for(int k=0; k<r.nHoursPerDay; k++){
8892 					//OLD COMMENT
8893 					//Here we want to see if we have a weekly activity or a 2 weeks activity
8894 					//We don't do tmp+=subgroupsMatrix[i][j][k] because we already counted this as a hard hitness
8895 					if(subgroupsMatrix[i][j][k]>=1)
8896 						tmp++;
8897 				}
8898 				if(this->maxHoursDaily>=0 && tmp > this->maxHoursDaily){ //we would like no more than maxHoursDaily hours per day.
8899 					too_much += 1; //tmp - this->maxHoursDaily;
8900 
8901 					if(conflictsString!=nullptr){
8902 						QString s=tr("Time constraint students max hours daily broken for subgroup: %1, day: %2, length=%3, conflict increase=%4")
8903 						 .arg(r.internalSubgroupsList[i]->name)
8904 						 .arg(r.daysOfTheWeek[j])
8905 						 .arg(CustomFETString::number(tmp))
8906 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*1));
8907 
8908 						dl.append(s);
8909 						cl.append(weightPercentage/100*1);
8910 
8911 						*conflictsString+= s+"\n";
8912 					}
8913 				}
8914 			}
8915 	}
8916 
8917 	assert(too_much>=0);
8918 	if(weightPercentage==100)
8919 		assert(too_much==0);
8920 	return too_much * weightPercentage/100;
8921 }
8922 
isRelatedToActivity(Rules & r,Activity * a)8923 bool ConstraintStudentsMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
8924 {
8925 	Q_UNUSED(r);
8926 	Q_UNUSED(a);
8927 
8928 	return false;
8929 }
8930 
isRelatedToTeacher(Teacher * t)8931 bool ConstraintStudentsMaxHoursDaily::isRelatedToTeacher(Teacher* t)
8932 {
8933 	Q_UNUSED(t);
8934 
8935 	return false;
8936 }
8937 
isRelatedToSubject(Subject * s)8938 bool ConstraintStudentsMaxHoursDaily::isRelatedToSubject(Subject* s)
8939 {
8940 	Q_UNUSED(s);
8941 
8942 	return false;
8943 }
8944 
isRelatedToActivityTag(ActivityTag * s)8945 bool ConstraintStudentsMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
8946 {
8947 	Q_UNUSED(s);
8948 
8949 	return false;
8950 }
8951 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)8952 bool ConstraintStudentsMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
8953 {
8954 	Q_UNUSED(r);
8955 	Q_UNUSED(s);
8956 
8957 	return true;
8958 }
8959 
hasWrongDayOrHour(Rules & r)8960 bool ConstraintStudentsMaxHoursDaily::hasWrongDayOrHour(Rules& r)
8961 {
8962 	if(maxHoursDaily>r.nHoursPerDay)
8963 		return true;
8964 
8965 	return false;
8966 }
8967 
canRepairWrongDayOrHour(Rules & r)8968 bool ConstraintStudentsMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
8969 {
8970 	assert(hasWrongDayOrHour(r));
8971 
8972 	return true;
8973 }
8974 
repairWrongDayOrHour(Rules & r)8975 bool ConstraintStudentsMaxHoursDaily::repairWrongDayOrHour(Rules& r)
8976 {
8977 	assert(hasWrongDayOrHour(r));
8978 
8979 	if(maxHoursDaily>r.nHoursPerDay)
8980 		maxHoursDaily=r.nHoursPerDay;
8981 
8982 	return true;
8983 }
8984 
8985 ////////////////////////////////////////////////////////////////////////////////////////////
8986 ////////////////////////////////////////////////////////////////////////////////////////////
8987 
ConstraintStudentsSetMaxHoursDaily()8988 ConstraintStudentsSetMaxHoursDaily::ConstraintStudentsSetMaxHoursDaily()
8989 	: TimeConstraint()
8990 {
8991 	this->type = CONSTRAINT_STUDENTS_SET_MAX_HOURS_DAILY;
8992 	this->maxHoursDaily = -1;
8993 }
8994 
ConstraintStudentsSetMaxHoursDaily(double wp,int maxnh,const QString & s)8995 ConstraintStudentsSetMaxHoursDaily::ConstraintStudentsSetMaxHoursDaily(double wp, int maxnh, const QString& s)
8996 	: TimeConstraint(wp)
8997 {
8998 	this->maxHoursDaily = maxnh;
8999 	this->students = s;
9000 	this->type = CONSTRAINT_STUDENTS_SET_MAX_HOURS_DAILY;
9001 }
9002 
hasInactiveActivities(Rules & r)9003 bool ConstraintStudentsSetMaxHoursDaily::hasInactiveActivities(Rules& r)
9004 {
9005 	Q_UNUSED(r);
9006 	return false;
9007 }
9008 
getXmlDescription(Rules & r)9009 QString ConstraintStudentsSetMaxHoursDaily::getXmlDescription(Rules& r)
9010 {
9011 	Q_UNUSED(r);
9012 
9013 	QString s="<ConstraintStudentsSetMaxHoursDaily>\n";
9014 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
9015 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
9016 	s+="	<Students>"+protect(this->students)+"</Students>\n";
9017 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
9018 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
9019 	s+="</ConstraintStudentsSetMaxHoursDaily>\n";
9020 	return s;
9021 }
9022 
getDescription(Rules & r)9023 QString ConstraintStudentsSetMaxHoursDaily::getDescription(Rules& r)
9024 {
9025 	Q_UNUSED(r);
9026 
9027 	QString begin=QString("");
9028 	if(!active)
9029 		begin="X - ";
9030 
9031 	QString end=QString("");
9032 	if(!comments.isEmpty())
9033 		end=", "+tr("C: %1", "Comments").arg(comments);
9034 
9035 	QString s;
9036 	s+=tr("Students set max hours daily");s+=", ";
9037 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
9038 	s+=tr("St:%1", "Students (set)").arg(this->students); s+=", ";
9039 	s+=tr("MH:%1", "Max hours (daily)").arg(this->maxHoursDaily);
9040 
9041 	return begin+s+end;
9042 }
9043 
getDetailedDescription(Rules & r)9044 QString ConstraintStudentsSetMaxHoursDaily::getDetailedDescription(Rules& r)
9045 {
9046 	Q_UNUSED(r);
9047 
9048 	QString s=tr("Time constraint");s+="\n";
9049 	s+=tr("A students set must respect the maximum number of hours daily");s+="\n";
9050 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
9051 	s+=tr("Students set=%1").arg(this->students);s+="\n";
9052 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
9053 
9054 	if(!active){
9055 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
9056 		s+="\n";
9057 	}
9058 	if(!comments.isEmpty()){
9059 		s+=tr("Comments=%1").arg(comments);
9060 		s+="\n";
9061 	}
9062 
9063 	return s;
9064 }
9065 
computeInternalStructure(QWidget * parent,Rules & r)9066 bool ConstraintStudentsSetMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
9067 {
9068 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
9069 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
9070 
9071 	if(ss==nullptr){
9072 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
9073 		 tr("Constraint students set max hours daily is wrong because it refers to inexistent students set."
9074 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
9075 
9076 		return false;
9077 	}
9078 
9079 	assert(ss!=nullptr);
9080 
9081 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
9082 	/*this->iSubgroupsList.clear();
9083 	if(ss->type==STUDENTS_SUBGROUP){
9084 		int tmp;
9085 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
9086 		assert(tmp>=0);
9087 		assert(tmp<r.nInternalSubgroups);
9088 		if(!this->iSubgroupsList.contains(tmp))
9089 			this->iSubgroupsList.append(tmp);
9090 	}
9091 	else if(ss->type==STUDENTS_GROUP){
9092 		StudentsGroup* stg=(StudentsGroup*)ss;
9093 		for(int i=0; i<stg->subgroupsList.size(); i++){
9094 			StudentsSubgroup* sts=stg->subgroupsList[i];
9095 			int tmp;
9096 			tmp=sts->indexInInternalSubgroupsList;
9097 			assert(tmp>=0);
9098 			assert(tmp<r.nInternalSubgroups);
9099 			if(!this->iSubgroupsList.contains(tmp))
9100 				this->iSubgroupsList.append(tmp);
9101 		}
9102 	}
9103 	else if(ss->type==STUDENTS_YEAR){
9104 		StudentsYear* sty=(StudentsYear*)ss;
9105 		for(int i=0; i<sty->groupsList.size(); i++){
9106 			StudentsGroup* stg=sty->groupsList[i];
9107 			for(int j=0; j<stg->subgroupsList.size(); j++){
9108 				StudentsSubgroup* sts=stg->subgroupsList[j];
9109 				int tmp;
9110 				tmp=sts->indexInInternalSubgroupsList;
9111 				assert(tmp>=0);
9112 				assert(tmp<r.nInternalSubgroups);
9113 				if(!this->iSubgroupsList.contains(tmp))
9114 					this->iSubgroupsList.append(tmp);
9115 			}
9116 		}
9117 	}
9118 	else
9119 		assert(0);*/
9120 
9121 	return true;
9122 }
9123 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)9124 double ConstraintStudentsSetMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
9125 {
9126 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
9127 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
9128 		c.teachersMatrixReady=true;
9129 		c.subgroupsMatrixReady=true;
9130 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
9131 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
9132 
9133 		c.changedForMatrixCalculation=false;
9134 	}
9135 
9136 	int tmp;
9137 	int too_much;
9138 
9139 	assert(this->maxHoursDaily>=0);
9140 
9141 	if(1){
9142 		too_much=0;
9143 		for(int sg=0; sg<this->iSubgroupsList.count(); sg++){
9144 			int i=iSubgroupsList.at(sg);
9145 			for(int j=0; j<r.nDaysPerWeek; j++){
9146 				tmp=0;
9147 				for(int k=0; k<r.nHoursPerDay; k++){
9148 					//Here we want to see if we have a weekly activity or a 2 weeks activity
9149 					//We don't do tmp+=subgroupsMatrix[i][j][k] because we already counted this as a hard hitness
9150 					if(subgroupsMatrix[i][j][k]>=1)
9151 						tmp++;
9152 				}
9153 				if(this->maxHoursDaily>=0 && tmp > this->maxHoursDaily){ //we would like no more than max_hours_daily hours per day.
9154 					too_much += 1; //tmp - this->maxHoursDaily;
9155 
9156 					if(conflictsString!=nullptr){
9157 						QString s=tr("Time constraint students set max hours daily broken for subgroup: %1, day: %2, length=%3, conflicts increase=%4")
9158 						 .arg(r.internalSubgroupsList[i]->name)
9159 						 .arg(r.daysOfTheWeek[j])
9160 						 .arg(CustomFETString::number(tmp))
9161 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision( 1 *weightPercentage/100));
9162 
9163 						dl.append(s);
9164 						cl.append( 1 *weightPercentage/100);
9165 
9166 						*conflictsString+= s+"\n";
9167 					}
9168 				}
9169 			}
9170 		}
9171 	}
9172 
9173 	assert(too_much>=0);
9174 	if(weightPercentage==100)
9175 		assert(too_much==0);
9176 	return too_much * weightPercentage / 100.0;
9177 }
9178 
isRelatedToActivity(Rules & r,Activity * a)9179 bool ConstraintStudentsSetMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
9180 {
9181 	Q_UNUSED(r);
9182 	Q_UNUSED(a);
9183 
9184 	return false;
9185 }
9186 
isRelatedToTeacher(Teacher * t)9187 bool ConstraintStudentsSetMaxHoursDaily::isRelatedToTeacher(Teacher* t)
9188 {
9189 	Q_UNUSED(t);
9190 
9191 	return false;
9192 }
9193 
isRelatedToSubject(Subject * s)9194 bool ConstraintStudentsSetMaxHoursDaily::isRelatedToSubject(Subject* s)
9195 {
9196 	Q_UNUSED(s);
9197 
9198 	return false;
9199 }
9200 
isRelatedToActivityTag(ActivityTag * s)9201 bool ConstraintStudentsSetMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
9202 {
9203 	Q_UNUSED(s);
9204 
9205 	return false;
9206 }
9207 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)9208 bool ConstraintStudentsSetMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
9209 {
9210 	return r.setsShareStudents(this->students, s->name);
9211 }
9212 
hasWrongDayOrHour(Rules & r)9213 bool ConstraintStudentsSetMaxHoursDaily::hasWrongDayOrHour(Rules& r)
9214 {
9215 	if(maxHoursDaily>r.nHoursPerDay)
9216 		return true;
9217 
9218 	return false;
9219 }
9220 
canRepairWrongDayOrHour(Rules & r)9221 bool ConstraintStudentsSetMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
9222 {
9223 	assert(hasWrongDayOrHour(r));
9224 
9225 	return true;
9226 }
9227 
repairWrongDayOrHour(Rules & r)9228 bool ConstraintStudentsSetMaxHoursDaily::repairWrongDayOrHour(Rules& r)
9229 {
9230 	assert(hasWrongDayOrHour(r));
9231 
9232 	if(maxHoursDaily>r.nHoursPerDay)
9233 		maxHoursDaily=r.nHoursPerDay;
9234 
9235 	return true;
9236 }
9237 
9238 ////////////////////////////////////////////////////////////////////////////////////////////
9239 ////////////////////////////////////////////////////////////////////////////////////////////
9240 
ConstraintStudentsMaxHoursContinuously()9241 ConstraintStudentsMaxHoursContinuously::ConstraintStudentsMaxHoursContinuously()
9242 	: TimeConstraint()
9243 {
9244 	this->type = CONSTRAINT_STUDENTS_MAX_HOURS_CONTINUOUSLY;
9245 	this->maxHoursContinuously = -1;
9246 }
9247 
ConstraintStudentsMaxHoursContinuously(double wp,int maxnh)9248 ConstraintStudentsMaxHoursContinuously::ConstraintStudentsMaxHoursContinuously(double wp, int maxnh)
9249 	: TimeConstraint(wp)
9250 {
9251 	this->maxHoursContinuously = maxnh;
9252 	this->type = CONSTRAINT_STUDENTS_MAX_HOURS_CONTINUOUSLY;
9253 }
9254 
computeInternalStructure(QWidget * parent,Rules & r)9255 bool ConstraintStudentsMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
9256 {
9257 	Q_UNUSED(parent);
9258 	Q_UNUSED(r);
9259 
9260 	return true;
9261 }
9262 
hasInactiveActivities(Rules & r)9263 bool ConstraintStudentsMaxHoursContinuously::hasInactiveActivities(Rules& r)
9264 {
9265 	Q_UNUSED(r);
9266 	return false;
9267 }
9268 
getXmlDescription(Rules & r)9269 QString ConstraintStudentsMaxHoursContinuously::getXmlDescription(Rules& r)
9270 {
9271 	Q_UNUSED(r);
9272 
9273 	QString s="<ConstraintStudentsMaxHoursContinuously>\n";
9274 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
9275 	if(this->maxHoursContinuously>=0)
9276 		s+="	<Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
9277 	else
9278 		assert(0);
9279 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
9280 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
9281 	s+="</ConstraintStudentsMaxHoursContinuously>\n";
9282 	return s;
9283 }
9284 
getDescription(Rules & r)9285 QString ConstraintStudentsMaxHoursContinuously::getDescription(Rules& r)
9286 {
9287 	Q_UNUSED(r);
9288 
9289 	QString begin=QString("");
9290 	if(!active)
9291 		begin="X - ";
9292 
9293 	QString end=QString("");
9294 	if(!comments.isEmpty())
9295 		end=", "+tr("C: %1", "Comments").arg(comments);
9296 
9297 	QString s;
9298 	s+=tr("Students max hours continuously");s+=", ";
9299 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
9300 	s+=tr("MH:%1", "Max hours (continuously)").arg(this->maxHoursContinuously);
9301 
9302 	return begin+s+end;
9303 }
9304 
getDetailedDescription(Rules & r)9305 QString ConstraintStudentsMaxHoursContinuously::getDetailedDescription(Rules& r)
9306 {
9307 	Q_UNUSED(r);
9308 
9309 	QString s=tr("Time constraint");s+="\n";
9310 	s+=tr("All students must respect the maximum number of hours continuously");s+="\n";
9311 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
9312 	s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously);s+="\n";
9313 
9314 	if(!active){
9315 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
9316 		s+="\n";
9317 	}
9318 	if(!comments.isEmpty()){
9319 		s+=tr("Comments=%1").arg(comments);
9320 		s+="\n";
9321 	}
9322 
9323 	return s;
9324 }
9325 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)9326 double ConstraintStudentsMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
9327 {
9328 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
9329 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
9330 		c.teachersMatrixReady=true;
9331 		c.subgroupsMatrixReady=true;
9332 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
9333 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
9334 
9335 		c.changedForMatrixCalculation=false;
9336 	}
9337 
9338 	int nbroken;
9339 
9340 	nbroken=0;
9341 	for(int i=0; i<r.nInternalSubgroups; i++){
9342 		for(int d=0; d<r.nDaysPerWeek; d++){
9343 			int nc=0;
9344 			for(int h=0; h<r.nHoursPerDay; h++){
9345 				if(subgroupsMatrix[i][d][h]>0)
9346 					nc++;
9347 				else{
9348 					if(nc>this->maxHoursContinuously){
9349 						nbroken++;
9350 
9351 						if(conflictsString!=nullptr){
9352 							QString s=(tr(
9353 							 "Time constraint students max %1 hours continuously broken for subgroup %2, on day %3, length=%4.")
9354 							 .arg(CustomFETString::number(this->maxHoursContinuously))
9355 							 .arg(r.internalSubgroupsList[i]->name)
9356 							 .arg(r.daysOfTheWeek[d])
9357 							 .arg(nc)
9358 							 )
9359 							 +
9360 							 " "
9361 							 +
9362 							 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
9363 
9364 							dl.append(s);
9365 							cl.append(weightPercentage/100);
9366 
9367 							*conflictsString+= s+"\n";
9368 						}
9369 					}
9370 
9371 					nc=0;
9372 				}
9373 			}
9374 
9375 			if(nc>this->maxHoursContinuously){
9376 				nbroken++;
9377 
9378 				if(conflictsString!=nullptr){
9379 					QString s=(tr(
9380 					 "Time constraint students max %1 hours continuously broken for subgroup %2, on day %3, length=%4.")
9381 					 .arg(CustomFETString::number(this->maxHoursContinuously))
9382 					 .arg(r.internalSubgroupsList[i]->name)
9383 					 .arg(r.daysOfTheWeek[d])
9384 					 .arg(nc)
9385 					 )
9386 					 +
9387 					 " "
9388 					 +
9389 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
9390 
9391 					dl.append(s);
9392 					cl.append(weightPercentage/100);
9393 
9394 					*conflictsString+= s+"\n";
9395 				}
9396 			}
9397 		}
9398 	}
9399 
9400 	if(weightPercentage==100)
9401 		assert(nbroken==0);
9402 	return weightPercentage/100 * nbroken;
9403 }
9404 
isRelatedToActivity(Rules & r,Activity * a)9405 bool ConstraintStudentsMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
9406 {
9407 	Q_UNUSED(r);
9408 	Q_UNUSED(a);
9409 
9410 	return false;
9411 }
9412 
isRelatedToTeacher(Teacher * t)9413 bool ConstraintStudentsMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
9414 {
9415 	Q_UNUSED(t);
9416 
9417 	return false;
9418 }
9419 
isRelatedToSubject(Subject * s)9420 bool ConstraintStudentsMaxHoursContinuously::isRelatedToSubject(Subject* s)
9421 {
9422 	Q_UNUSED(s);
9423 
9424 	return false;
9425 }
9426 
isRelatedToActivityTag(ActivityTag * s)9427 bool ConstraintStudentsMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
9428 {
9429 	Q_UNUSED(s);
9430 
9431 	return false;
9432 }
9433 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)9434 bool ConstraintStudentsMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
9435 {
9436 	Q_UNUSED(r);
9437 	Q_UNUSED(s);
9438 
9439 	return true;
9440 }
9441 
hasWrongDayOrHour(Rules & r)9442 bool ConstraintStudentsMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
9443 {
9444 	if(maxHoursContinuously>r.nHoursPerDay)
9445 		return true;
9446 
9447 	return false;
9448 }
9449 
canRepairWrongDayOrHour(Rules & r)9450 bool ConstraintStudentsMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
9451 {
9452 	assert(hasWrongDayOrHour(r));
9453 
9454 	return true;
9455 }
9456 
repairWrongDayOrHour(Rules & r)9457 bool ConstraintStudentsMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
9458 {
9459 	assert(hasWrongDayOrHour(r));
9460 
9461 	if(maxHoursContinuously>r.nHoursPerDay)
9462 		maxHoursContinuously=r.nHoursPerDay;
9463 
9464 	return true;
9465 }
9466 
9467 ////////////////////////////////////////////////////////////////////////////////////////////
9468 ////////////////////////////////////////////////////////////////////////////////////////////
9469 
ConstraintStudentsSetMaxHoursContinuously()9470 ConstraintStudentsSetMaxHoursContinuously::ConstraintStudentsSetMaxHoursContinuously()
9471 	: TimeConstraint()
9472 {
9473 	this->type = CONSTRAINT_STUDENTS_SET_MAX_HOURS_CONTINUOUSLY;
9474 	this->maxHoursContinuously = -1;
9475 }
9476 
ConstraintStudentsSetMaxHoursContinuously(double wp,int maxnh,const QString & s)9477 ConstraintStudentsSetMaxHoursContinuously::ConstraintStudentsSetMaxHoursContinuously(double wp, int maxnh, const QString& s)
9478 	: TimeConstraint(wp)
9479 {
9480 	this->maxHoursContinuously = maxnh;
9481 	this->students = s;
9482 	this->type = CONSTRAINT_STUDENTS_SET_MAX_HOURS_CONTINUOUSLY;
9483 }
9484 
hasInactiveActivities(Rules & r)9485 bool ConstraintStudentsSetMaxHoursContinuously::hasInactiveActivities(Rules& r)
9486 {
9487 	Q_UNUSED(r);
9488 	return false;
9489 }
9490 
getXmlDescription(Rules & r)9491 QString ConstraintStudentsSetMaxHoursContinuously::getXmlDescription(Rules& r)
9492 {
9493 	Q_UNUSED(r);
9494 
9495 	QString s="<ConstraintStudentsSetMaxHoursContinuously>\n";
9496 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
9497 	s+="	<Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
9498 	s+="	<Students>"+protect(this->students)+"</Students>\n";
9499 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
9500 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
9501 	s+="</ConstraintStudentsSetMaxHoursContinuously>\n";
9502 	return s;
9503 }
9504 
getDescription(Rules & r)9505 QString ConstraintStudentsSetMaxHoursContinuously::getDescription(Rules& r)
9506 {
9507 	Q_UNUSED(r);
9508 
9509 	QString begin=QString("");
9510 	if(!active)
9511 		begin="X - ";
9512 
9513 	QString end=QString("");
9514 	if(!comments.isEmpty())
9515 		end=", "+tr("C: %1", "Comments").arg(comments);
9516 
9517 	QString s;
9518 	s+=tr("Students set max hours continuously");s+=", ";
9519 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
9520 	s+=tr("St:%1", "Students (set)").arg(this->students);s+=", ";
9521 	s+=tr("MH:%1", "Max hours (continuously)").arg(this->maxHoursContinuously);
9522 
9523 	return begin+s+end;
9524 }
9525 
getDetailedDescription(Rules & r)9526 QString ConstraintStudentsSetMaxHoursContinuously::getDetailedDescription(Rules& r)
9527 {
9528 	Q_UNUSED(r);
9529 
9530 	QString s=tr("Time constraint");s+="\n";
9531 	s+=tr("A students set must respect the maximum number of hours continuously");s+="\n";
9532 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
9533 	s+=tr("Students set=%1").arg(this->students);s+="\n";
9534 	s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously);s+="\n";
9535 
9536 	if(!active){
9537 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
9538 		s+="\n";
9539 	}
9540 	if(!comments.isEmpty()){
9541 		s+=tr("Comments=%1").arg(comments);
9542 		s+="\n";
9543 	}
9544 
9545 	return s;
9546 }
9547 
computeInternalStructure(QWidget * parent,Rules & r)9548 bool ConstraintStudentsSetMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
9549 {
9550 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
9551 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
9552 
9553 	if(ss==nullptr){
9554 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
9555 		 tr("Constraint students set max hours continuously is wrong because it refers to inexistent students set."
9556 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
9557 
9558 		return false;
9559 	}
9560 
9561 	assert(ss!=nullptr);
9562 
9563 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
9564 	/*this->iSubgroupsList.clear();
9565 	if(ss->type==STUDENTS_SUBGROUP){
9566 		int tmp;
9567 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
9568 		assert(tmp>=0);
9569 		assert(tmp<r.nInternalSubgroups);
9570 		if(!this->iSubgroupsList.contains(tmp))
9571 			this->iSubgroupsList.append(tmp);
9572 	}
9573 	else if(ss->type==STUDENTS_GROUP){
9574 		StudentsGroup* stg=(StudentsGroup*)ss;
9575 		for(int i=0; i<stg->subgroupsList.size(); i++){
9576 			StudentsSubgroup* sts=stg->subgroupsList[i];
9577 			int tmp;
9578 			tmp=sts->indexInInternalSubgroupsList;
9579 			assert(tmp>=0);
9580 			assert(tmp<r.nInternalSubgroups);
9581 			if(!this->iSubgroupsList.contains(tmp))
9582 				this->iSubgroupsList.append(tmp);
9583 		}
9584 	}
9585 	else if(ss->type==STUDENTS_YEAR){
9586 		StudentsYear* sty=(StudentsYear*)ss;
9587 		for(int i=0; i<sty->groupsList.size(); i++){
9588 			StudentsGroup* stg=sty->groupsList[i];
9589 			for(int j=0; j<stg->subgroupsList.size(); j++){
9590 				StudentsSubgroup* sts=stg->subgroupsList[j];
9591 				int tmp;
9592 				tmp=sts->indexInInternalSubgroupsList;
9593 				assert(tmp>=0);
9594 				assert(tmp<r.nInternalSubgroups);
9595 				if(!this->iSubgroupsList.contains(tmp))
9596 					this->iSubgroupsList.append(tmp);
9597 			}
9598 		}
9599 	}
9600 	else
9601 		assert(0);*/
9602 
9603 	return true;
9604 }
9605 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)9606 double ConstraintStudentsSetMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
9607 {
9608 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
9609 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
9610 		c.teachersMatrixReady=true;
9611 		c.subgroupsMatrixReady=true;
9612 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
9613 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
9614 
9615 		c.changedForMatrixCalculation=false;
9616 	}
9617 
9618 	int nbroken;
9619 
9620 	nbroken=0;
9621 	for(int i : qAsConst(this->iSubgroupsList)){
9622 		for(int d=0; d<r.nDaysPerWeek; d++){
9623 			int nc=0;
9624 			for(int h=0; h<r.nHoursPerDay; h++){
9625 				if(subgroupsMatrix[i][d][h]>0)
9626 					nc++;
9627 				else{
9628 					if(nc>this->maxHoursContinuously){
9629 						nbroken++;
9630 
9631 						if(conflictsString!=nullptr){
9632 							QString s=(tr(
9633 							 "Time constraint students set max %1 hours continuously broken for subgroup %2, on day %3, length=%4.")
9634 							 .arg(CustomFETString::number(this->maxHoursContinuously))
9635 							 .arg(r.internalSubgroupsList[i]->name)
9636 							 .arg(r.daysOfTheWeek[d])
9637 							 .arg(nc)
9638 							 )
9639 							 +
9640 							 " "
9641 							 +
9642 							 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
9643 
9644 							dl.append(s);
9645 							cl.append(weightPercentage/100);
9646 
9647 							*conflictsString+= s+"\n";
9648 						}
9649 					}
9650 
9651 					nc=0;
9652 				}
9653 			}
9654 
9655 			if(nc>this->maxHoursContinuously){
9656 				nbroken++;
9657 
9658 				if(conflictsString!=nullptr){
9659 					QString s=(tr(
9660 					 "Time constraint students set max %1 hours continuously broken for subgroup %2, on day %3, length=%4.")
9661 					 .arg(CustomFETString::number(this->maxHoursContinuously))
9662 					 .arg(r.internalSubgroupsList[i]->name)
9663 					 .arg(r.daysOfTheWeek[d])
9664 					 .arg(nc)
9665 					 )
9666 					 +
9667 					 " "
9668 					 +
9669 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
9670 
9671 					dl.append(s);
9672 					cl.append(weightPercentage/100);
9673 
9674 					*conflictsString+= s+"\n";
9675 				}
9676 			}
9677 		}
9678 	}
9679 
9680 	if(weightPercentage==100)
9681 		assert(nbroken==0);
9682 	return weightPercentage/100 * nbroken;
9683 }
9684 
isRelatedToActivity(Rules & r,Activity * a)9685 bool ConstraintStudentsSetMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
9686 {
9687 	Q_UNUSED(r);
9688 	Q_UNUSED(a);
9689 
9690 	return false;
9691 }
9692 
isRelatedToTeacher(Teacher * t)9693 bool ConstraintStudentsSetMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
9694 {
9695 	Q_UNUSED(t);
9696 
9697 	return false;
9698 }
9699 
isRelatedToSubject(Subject * s)9700 bool ConstraintStudentsSetMaxHoursContinuously::isRelatedToSubject(Subject* s)
9701 {
9702 	Q_UNUSED(s);
9703 
9704 	return false;
9705 }
9706 
isRelatedToActivityTag(ActivityTag * s)9707 bool ConstraintStudentsSetMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
9708 {
9709 	Q_UNUSED(s);
9710 
9711 	return false;
9712 }
9713 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)9714 bool ConstraintStudentsSetMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
9715 {
9716 	return r.setsShareStudents(this->students, s->name);
9717 }
9718 
hasWrongDayOrHour(Rules & r)9719 bool ConstraintStudentsSetMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
9720 {
9721 	if(maxHoursContinuously>r.nHoursPerDay)
9722 		return true;
9723 
9724 	return false;
9725 }
9726 
canRepairWrongDayOrHour(Rules & r)9727 bool ConstraintStudentsSetMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
9728 {
9729 	assert(hasWrongDayOrHour(r));
9730 
9731 	return true;
9732 }
9733 
repairWrongDayOrHour(Rules & r)9734 bool ConstraintStudentsSetMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
9735 {
9736 	assert(hasWrongDayOrHour(r));
9737 
9738 	if(maxHoursContinuously>r.nHoursPerDay)
9739 		maxHoursContinuously=r.nHoursPerDay;
9740 
9741 	return true;
9742 }
9743 
9744 ////////////////////////////////////////////////////////////////////////////////////////////
9745 ////////////////////////////////////////////////////////////////////////////////////////////
9746 
ConstraintStudentsActivityTagMaxHoursContinuously()9747 ConstraintStudentsActivityTagMaxHoursContinuously::ConstraintStudentsActivityTagMaxHoursContinuously()
9748 	: TimeConstraint()
9749 {
9750 	this->type = CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
9751 	this->maxHoursContinuously = -1;
9752 }
9753 
ConstraintStudentsActivityTagMaxHoursContinuously(double wp,int maxnh,const QString & activityTag)9754 ConstraintStudentsActivityTagMaxHoursContinuously::ConstraintStudentsActivityTagMaxHoursContinuously(double wp, int maxnh, const QString& activityTag)
9755 	: TimeConstraint(wp)
9756 {
9757 	this->maxHoursContinuously = maxnh;
9758 	this->activityTagName=activityTag;
9759 	this->type = CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
9760 }
9761 
computeInternalStructure(QWidget * parent,Rules & r)9762 bool ConstraintStudentsActivityTagMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
9763 {
9764 	Q_UNUSED(parent);
9765 
9766 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
9767 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
9768 	assert(this->activityTagIndex>=0);
9769 
9770 	this->canonicalSubgroupsList.clear();
9771 	for(int i=0; i<r.nInternalSubgroups; i++){
9772 		bool found=false;
9773 
9774 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
9775 		for(int actIndex : qAsConst(sbg->activitiesForSubgroup)){
9776 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
9777 				found=true;
9778 				break;
9779 			}
9780 		}
9781 
9782 		if(found)
9783 			this->canonicalSubgroupsList.append(i);
9784 	}
9785 
9786 	return true;
9787 }
9788 
hasInactiveActivities(Rules & r)9789 bool ConstraintStudentsActivityTagMaxHoursContinuously::hasInactiveActivities(Rules& r)
9790 {
9791 	Q_UNUSED(r);
9792 	return false;
9793 }
9794 
getXmlDescription(Rules & r)9795 QString ConstraintStudentsActivityTagMaxHoursContinuously::getXmlDescription(Rules& r)
9796 {
9797 	Q_UNUSED(r);
9798 
9799 	QString s="<ConstraintStudentsActivityTagMaxHoursContinuously>\n";
9800 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
9801 
9802 	s+="	<Activity_Tag>"+protect(this->activityTagName)+"</Activity_Tag>\n";
9803 
9804 	if(this->maxHoursContinuously>=0)
9805 		s+="	<Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
9806 	else
9807 		assert(0);
9808 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
9809 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
9810 	s+="</ConstraintStudentsActivityTagMaxHoursContinuously>\n";
9811 	return s;
9812 }
9813 
getDescription(Rules & r)9814 QString ConstraintStudentsActivityTagMaxHoursContinuously::getDescription(Rules& r)
9815 {
9816 	Q_UNUSED(r);
9817 
9818 	QString begin=QString("");
9819 	if(!active)
9820 		begin="X - ";
9821 
9822 	QString end=QString("");
9823 	if(!comments.isEmpty())
9824 		end=", "+tr("C: %1", "Comments").arg(comments);
9825 
9826 	QString s;
9827 	s+=tr("Students for activity tag %1 have max %2 hours continuously")
9828 		.arg(this->activityTagName).arg(this->maxHoursContinuously); s+=", ";
9829 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
9830 
9831 	return begin+s+end;
9832 }
9833 
getDetailedDescription(Rules & r)9834 QString ConstraintStudentsActivityTagMaxHoursContinuously::getDetailedDescription(Rules& r)
9835 {
9836 	Q_UNUSED(r);
9837 
9838 	QString s=tr("Time constraint");s+="\n";
9839 	s+=tr("All students, for an activity tag, must respect the maximum number of hours continuously"); s+="\n";
9840 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
9841 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
9842 	s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously);s+="\n";
9843 
9844 	if(!active){
9845 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
9846 		s+="\n";
9847 	}
9848 	if(!comments.isEmpty()){
9849 		s+=tr("Comments=%1").arg(comments);
9850 		s+="\n";
9851 	}
9852 
9853 	return s;
9854 }
9855 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)9856 double ConstraintStudentsActivityTagMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
9857 {
9858 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
9859 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
9860 		c.teachersMatrixReady=true;
9861 		c.subgroupsMatrixReady=true;
9862 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
9863 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
9864 
9865 		c.changedForMatrixCalculation=false;
9866 	}
9867 
9868 	int nbroken;
9869 
9870 	nbroken=0;
9871 
9872 	Matrix2D<int> crtSubgroupTimetableActivityTag;
9873 	crtSubgroupTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
9874 
9875 	for(int i : qAsConst(this->canonicalSubgroupsList)){
9876 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
9877 		for(int d=0; d<r.nDaysPerWeek; d++)
9878 			for(int h=0; h<r.nHoursPerDay; h++)
9879 				crtSubgroupTimetableActivityTag[d][h]=-1;
9880 		for(int ai : qAsConst(sbg->activitiesForSubgroup)) if(c.times[ai]!=UNALLOCATED_TIME){
9881 			int d=c.times[ai]%r.nDaysPerWeek;
9882 			int h=c.times[ai]/r.nDaysPerWeek;
9883 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
9884 				assert(h+dur<r.nHoursPerDay);
9885 				assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
9886 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
9887 					crtSubgroupTimetableActivityTag[d][h+dur]=this->activityTagIndex;
9888 			}
9889 		}
9890 
9891 		for(int d=0; d<r.nDaysPerWeek; d++){
9892 			int nc=0;
9893 			for(int h=0; h<r.nHoursPerDay; h++){
9894 				bool inc=false;
9895 
9896 				if(crtSubgroupTimetableActivityTag[d][h]==this->activityTagIndex)
9897 					inc=true;
9898 
9899 				if(inc)
9900 					nc++;
9901 				else{
9902 					if(nc>this->maxHoursContinuously){
9903 						nbroken++;
9904 
9905 						if(conflictsString!=nullptr){
9906 							QString s=(tr(
9907 							 "Time constraint students, activity tag %1, max %2 hours continuously, broken for subgroup %3, on day %4, length=%5.")
9908 							 .arg(this->activityTagName)
9909 							 .arg(CustomFETString::number(this->maxHoursContinuously))
9910 							 .arg(r.internalSubgroupsList[i]->name)
9911 							 .arg(r.daysOfTheWeek[d])
9912 							 .arg(nc)
9913 							 )
9914 							 +
9915 							 " "
9916 							 +
9917 							 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
9918 
9919 							dl.append(s);
9920 							cl.append(weightPercentage/100);
9921 
9922 							*conflictsString+= s+"\n";
9923 						}
9924 					}
9925 
9926 					nc=0;
9927 				}
9928 			}
9929 
9930 			if(nc>this->maxHoursContinuously){
9931 				nbroken++;
9932 
9933 				if(conflictsString!=nullptr){
9934 					QString s=(tr(
9935 					 "Time constraint students, activity tag %1, max %2 hours continuously, broken for subgroup %3, on day %4, length=%5.")
9936 					 .arg(this->activityTagName)
9937 					 .arg(CustomFETString::number(this->maxHoursContinuously))
9938 					 .arg(r.internalSubgroupsList[i]->name)
9939 					 .arg(r.daysOfTheWeek[d])
9940 					 .arg(nc)
9941 					 )
9942 					 +
9943 					 " "
9944 					 +
9945 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
9946 
9947 					dl.append(s);
9948 					cl.append(weightPercentage/100);
9949 
9950 					*conflictsString+= s+"\n";
9951 				}
9952 			}
9953 		}
9954 	}
9955 
9956 	if(weightPercentage==100)
9957 		assert(nbroken==0);
9958 	return weightPercentage/100 * nbroken;
9959 }
9960 
isRelatedToActivity(Rules & r,Activity * a)9961 bool ConstraintStudentsActivityTagMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
9962 {
9963 	Q_UNUSED(r);
9964 	Q_UNUSED(a);
9965 
9966 	return false;
9967 }
9968 
isRelatedToTeacher(Teacher * t)9969 bool ConstraintStudentsActivityTagMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
9970 {
9971 	Q_UNUSED(t);
9972 
9973 	return false;
9974 }
9975 
isRelatedToSubject(Subject * s)9976 bool ConstraintStudentsActivityTagMaxHoursContinuously::isRelatedToSubject(Subject* s)
9977 {
9978 	Q_UNUSED(s);
9979 
9980 	return false;
9981 }
9982 
isRelatedToActivityTag(ActivityTag * s)9983 bool ConstraintStudentsActivityTagMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
9984 {
9985 	return s->name==this->activityTagName;
9986 }
9987 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)9988 bool ConstraintStudentsActivityTagMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
9989 {
9990 	Q_UNUSED(r);
9991 	Q_UNUSED(s);
9992 
9993 	return true;
9994 }
9995 
hasWrongDayOrHour(Rules & r)9996 bool ConstraintStudentsActivityTagMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
9997 {
9998 	if(maxHoursContinuously>r.nHoursPerDay)
9999 		return true;
10000 
10001 	return false;
10002 }
10003 
canRepairWrongDayOrHour(Rules & r)10004 bool ConstraintStudentsActivityTagMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
10005 {
10006 	assert(hasWrongDayOrHour(r));
10007 
10008 	return true;
10009 }
10010 
repairWrongDayOrHour(Rules & r)10011 bool ConstraintStudentsActivityTagMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
10012 {
10013 	assert(hasWrongDayOrHour(r));
10014 
10015 	if(maxHoursContinuously>r.nHoursPerDay)
10016 		maxHoursContinuously=r.nHoursPerDay;
10017 
10018 	return true;
10019 }
10020 
10021 ////////////////////////////////////////////////////////////////////////////////////////////
10022 ////////////////////////////////////////////////////////////////////////////////////////////
10023 
ConstraintStudentsSetActivityTagMaxHoursContinuously()10024 ConstraintStudentsSetActivityTagMaxHoursContinuously::ConstraintStudentsSetActivityTagMaxHoursContinuously()
10025 	: TimeConstraint()
10026 {
10027 	this->type = CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
10028 	this->maxHoursContinuously = -1;
10029 }
10030 
ConstraintStudentsSetActivityTagMaxHoursContinuously(double wp,int maxnh,const QString & s,const QString & activityTag)10031 ConstraintStudentsSetActivityTagMaxHoursContinuously::ConstraintStudentsSetActivityTagMaxHoursContinuously(double wp, int maxnh, const QString& s, const QString& activityTag)
10032 	: TimeConstraint(wp)
10033 {
10034 	this->maxHoursContinuously = maxnh;
10035 	this->students = s;
10036 	this->activityTagName=activityTag;
10037 	this->type = CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_CONTINUOUSLY;
10038 }
10039 
hasInactiveActivities(Rules & r)10040 bool ConstraintStudentsSetActivityTagMaxHoursContinuously::hasInactiveActivities(Rules& r)
10041 {
10042 	Q_UNUSED(r);
10043 	return false;
10044 }
10045 
getXmlDescription(Rules & r)10046 QString ConstraintStudentsSetActivityTagMaxHoursContinuously::getXmlDescription(Rules& r)
10047 {
10048 	Q_UNUSED(r);
10049 
10050 	QString s="<ConstraintStudentsSetActivityTagMaxHoursContinuously>\n";
10051 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
10052 	s+="	<Maximum_Hours_Continuously>"+CustomFETString::number(this->maxHoursContinuously)+"</Maximum_Hours_Continuously>\n";
10053 	s+="	<Students>"+protect(this->students)+"</Students>\n";
10054 	s+="	<Activity_Tag>"+protect(this->activityTagName)+"</Activity_Tag>\n";
10055 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
10056 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
10057 	s+="</ConstraintStudentsSetActivityTagMaxHoursContinuously>\n";
10058 	return s;
10059 }
10060 
getDescription(Rules & r)10061 QString ConstraintStudentsSetActivityTagMaxHoursContinuously::getDescription(Rules& r)
10062 {
10063 	Q_UNUSED(r);
10064 
10065 	QString begin=QString("");
10066 	if(!active)
10067 		begin="X - ";
10068 
10069 	QString end=QString("");
10070 	if(!comments.isEmpty())
10071 		end=", "+tr("C: %1", "Comments").arg(comments);
10072 
10073 	QString s;
10074 	s+=tr("Students set %1 for activity tag %2 has max %3 hours continuously").arg(this->students).arg(this->activityTagName).arg(this->maxHoursContinuously);
10075 	s+=", ";
10076 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
10077 
10078 	return begin+s+end;
10079 }
10080 
getDetailedDescription(Rules & r)10081 QString ConstraintStudentsSetActivityTagMaxHoursContinuously::getDetailedDescription(Rules& r)
10082 {
10083 	Q_UNUSED(r);
10084 
10085 	QString s=tr("Time constraint");s+="\n";
10086 	s+=tr("A students set, for an activity tag, must respect the maximum number of hours continuously"); s+="\n";
10087 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
10088 	s+=tr("Students set=%1").arg(this->students);s+="\n";
10089 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
10090 	s+=tr("Maximum hours continuously=%1").arg(this->maxHoursContinuously);s+="\n";
10091 
10092 	if(!active){
10093 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
10094 		s+="\n";
10095 	}
10096 	if(!comments.isEmpty()){
10097 		s+=tr("Comments=%1").arg(comments);
10098 		s+="\n";
10099 	}
10100 
10101 	return s;
10102 }
10103 
computeInternalStructure(QWidget * parent,Rules & r)10104 bool ConstraintStudentsSetActivityTagMaxHoursContinuously::computeInternalStructure(QWidget* parent, Rules& r)
10105 {
10106 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
10107 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
10108 	assert(this->activityTagIndex>=0);
10109 
10110 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
10111 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
10112 
10113 	if(ss==nullptr){
10114 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
10115 		 tr("Constraint students set max hours continuously is wrong because it refers to inexistent students set."
10116 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
10117 
10118 		return false;
10119 	}
10120 
10121 	assert(ss!=nullptr);
10122 
10123 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
10124 	/*this->iSubgroupsList.clear();
10125 	if(ss->type==STUDENTS_SUBGROUP){
10126 		int tmp;
10127 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
10128 		assert(tmp>=0);
10129 		assert(tmp<r.nInternalSubgroups);
10130 		if(!this->iSubgroupsList.contains(tmp))
10131 			this->iSubgroupsList.append(tmp);
10132 	}
10133 	else if(ss->type==STUDENTS_GROUP){
10134 		StudentsGroup* stg=(StudentsGroup*)ss;
10135 		for(int i=0; i<stg->subgroupsList.size(); i++){
10136 			StudentsSubgroup* sts=stg->subgroupsList[i];
10137 			int tmp;
10138 			tmp=sts->indexInInternalSubgroupsList;
10139 			assert(tmp>=0);
10140 			assert(tmp<r.nInternalSubgroups);
10141 			if(!this->iSubgroupsList.contains(tmp))
10142 				this->iSubgroupsList.append(tmp);
10143 		}
10144 	}
10145 	else if(ss->type==STUDENTS_YEAR){
10146 		StudentsYear* sty=(StudentsYear*)ss;
10147 		for(int i=0; i<sty->groupsList.size(); i++){
10148 			StudentsGroup* stg=sty->groupsList[i];
10149 			for(int j=0; j<stg->subgroupsList.size(); j++){
10150 				StudentsSubgroup* sts=stg->subgroupsList[j];
10151 				int tmp;
10152 				tmp=sts->indexInInternalSubgroupsList;
10153 				assert(tmp>=0);
10154 				assert(tmp<r.nInternalSubgroups);
10155 				if(!this->iSubgroupsList.contains(tmp))
10156 					this->iSubgroupsList.append(tmp);
10157 			}
10158 		}
10159 	}
10160 	else
10161 		assert(0);*/
10162 
10163 	/////////////
10164 	this->canonicalSubgroupsList.clear();
10165 	for(int i : qAsConst(this->iSubgroupsList)){
10166 		bool found=false;
10167 
10168 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
10169 		for(int actIndex : qAsConst(sbg->activitiesForSubgroup)){
10170 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
10171 				found=true;
10172 				break;
10173 			}
10174 		}
10175 
10176 		if(found)
10177 			this->canonicalSubgroupsList.append(i);
10178 	}
10179 
10180 	return true;
10181 }
10182 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)10183 double ConstraintStudentsSetActivityTagMaxHoursContinuously::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
10184 {
10185 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
10186 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
10187 		c.teachersMatrixReady=true;
10188 		c.subgroupsMatrixReady=true;
10189 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
10190 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
10191 
10192 		c.changedForMatrixCalculation=false;
10193 	}
10194 
10195 	int nbroken;
10196 
10197 	nbroken=0;
10198 
10199 	Matrix2D<int> crtSubgroupTimetableActivityTag;
10200 	crtSubgroupTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
10201 
10202 	for(int i : qAsConst(this->canonicalSubgroupsList)){
10203 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
10204 		for(int d=0; d<r.nDaysPerWeek; d++)
10205 			for(int h=0; h<r.nHoursPerDay; h++)
10206 				crtSubgroupTimetableActivityTag[d][h]=-1;
10207 		for(int ai : qAsConst(sbg->activitiesForSubgroup)) if(c.times[ai]!=UNALLOCATED_TIME){
10208 			int d=c.times[ai]%r.nDaysPerWeek;
10209 			int h=c.times[ai]/r.nDaysPerWeek;
10210 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
10211 				assert(h+dur<r.nHoursPerDay);
10212 				assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
10213 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
10214 					crtSubgroupTimetableActivityTag[d][h+dur]=this->activityTagIndex;
10215 			}
10216 		}
10217 
10218 		for(int d=0; d<r.nDaysPerWeek; d++){
10219 			int nc=0;
10220 			for(int h=0; h<r.nHoursPerDay; h++){
10221 				bool inc=false;
10222 
10223 				if(crtSubgroupTimetableActivityTag[d][h]==this->activityTagIndex)
10224 					inc=true;
10225 
10226 				if(inc)
10227 					nc++;
10228 				else{
10229 					if(nc>this->maxHoursContinuously){
10230 						nbroken++;
10231 
10232 						if(conflictsString!=nullptr){
10233 							QString s=(tr(
10234 							 "Time constraint students set max %1 hours continuously broken for subgroup %2, on day %3, length=%4.")
10235 							 .arg(CustomFETString::number(this->maxHoursContinuously))
10236 							 .arg(r.internalSubgroupsList[i]->name)
10237 							 .arg(r.daysOfTheWeek[d])
10238 							 .arg(nc)
10239 							 )
10240 							 +
10241 							 " "
10242 							 +
10243 							 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
10244 
10245 							dl.append(s);
10246 							cl.append(weightPercentage/100);
10247 
10248 							*conflictsString+= s+"\n";
10249 						}
10250 					}
10251 
10252 					nc=0;
10253 				}
10254 			}
10255 
10256 			if(nc>this->maxHoursContinuously){
10257 				nbroken++;
10258 
10259 				if(conflictsString!=nullptr){
10260 					QString s=(tr(
10261 					 "Time constraint students set max %1 hours continuously broken for subgroup %2, on day %3, length=%4.")
10262 					 .arg(CustomFETString::number(this->maxHoursContinuously))
10263 					 .arg(r.internalSubgroupsList[i]->name)
10264 					 .arg(r.daysOfTheWeek[d])
10265 					 .arg(nc)
10266 					 )
10267 					 +
10268 					 " "
10269 					 +
10270 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
10271 
10272 					dl.append(s);
10273 					cl.append(weightPercentage/100);
10274 
10275 					*conflictsString+= s+"\n";
10276 				}
10277 			}
10278 		}
10279 	}
10280 
10281 	if(weightPercentage==100)
10282 		assert(nbroken==0);
10283 	return weightPercentage/100 * nbroken;
10284 }
10285 
isRelatedToActivity(Rules & r,Activity * a)10286 bool ConstraintStudentsSetActivityTagMaxHoursContinuously::isRelatedToActivity(Rules& r, Activity* a)
10287 {
10288 	Q_UNUSED(r);
10289 	Q_UNUSED(a);
10290 
10291 	return false;
10292 }
10293 
isRelatedToTeacher(Teacher * t)10294 bool ConstraintStudentsSetActivityTagMaxHoursContinuously::isRelatedToTeacher(Teacher* t)
10295 {
10296 	Q_UNUSED(t);
10297 
10298 	return false;
10299 }
10300 
isRelatedToSubject(Subject * s)10301 bool ConstraintStudentsSetActivityTagMaxHoursContinuously::isRelatedToSubject(Subject* s)
10302 {
10303 	Q_UNUSED(s);
10304 
10305 	return false;
10306 }
10307 
isRelatedToActivityTag(ActivityTag * s)10308 bool ConstraintStudentsSetActivityTagMaxHoursContinuously::isRelatedToActivityTag(ActivityTag* s)
10309 {
10310 	Q_UNUSED(s);
10311 
10312 	return false;
10313 }
10314 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)10315 bool ConstraintStudentsSetActivityTagMaxHoursContinuously::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
10316 {
10317 	return r.setsShareStudents(this->students, s->name);
10318 }
10319 
hasWrongDayOrHour(Rules & r)10320 bool ConstraintStudentsSetActivityTagMaxHoursContinuously::hasWrongDayOrHour(Rules& r)
10321 {
10322 	if(maxHoursContinuously>r.nHoursPerDay)
10323 		return true;
10324 
10325 	return false;
10326 }
10327 
canRepairWrongDayOrHour(Rules & r)10328 bool ConstraintStudentsSetActivityTagMaxHoursContinuously::canRepairWrongDayOrHour(Rules& r)
10329 {
10330 	assert(hasWrongDayOrHour(r));
10331 
10332 	return true;
10333 }
10334 
repairWrongDayOrHour(Rules & r)10335 bool ConstraintStudentsSetActivityTagMaxHoursContinuously::repairWrongDayOrHour(Rules& r)
10336 {
10337 	assert(hasWrongDayOrHour(r));
10338 
10339 	if(maxHoursContinuously>r.nHoursPerDay)
10340 		maxHoursContinuously=r.nHoursPerDay;
10341 
10342 	return true;
10343 }
10344 
10345 ////////////////////////////////////////////////////////////////////////////////////////////
10346 ////////////////////////////////////////////////////////////////////////////////////////////
10347 
ConstraintStudentsMinHoursDaily()10348 ConstraintStudentsMinHoursDaily::ConstraintStudentsMinHoursDaily()
10349 	: TimeConstraint()
10350 {
10351 	this->type = CONSTRAINT_STUDENTS_MIN_HOURS_DAILY;
10352 	this->minHoursDaily = -1;
10353 
10354 	this->allowEmptyDays=false;
10355 }
10356 
ConstraintStudentsMinHoursDaily(double wp,int minnh,bool _allowEmptyDays)10357 ConstraintStudentsMinHoursDaily::ConstraintStudentsMinHoursDaily(double wp, int minnh, bool _allowEmptyDays)
10358 	: TimeConstraint(wp)
10359 {
10360 	this->minHoursDaily = minnh;
10361 	this->type = CONSTRAINT_STUDENTS_MIN_HOURS_DAILY;
10362 
10363 	this->allowEmptyDays=_allowEmptyDays;
10364 }
10365 
computeInternalStructure(QWidget * parent,Rules & r)10366 bool ConstraintStudentsMinHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
10367 {
10368 	Q_UNUSED(parent);
10369 	Q_UNUSED(r);
10370 
10371 	return true;
10372 }
10373 
hasInactiveActivities(Rules & r)10374 bool ConstraintStudentsMinHoursDaily::hasInactiveActivities(Rules& r)
10375 {
10376 	Q_UNUSED(r);
10377 	return false;
10378 }
10379 
getXmlDescription(Rules & r)10380 QString ConstraintStudentsMinHoursDaily::getXmlDescription(Rules& r)
10381 {
10382 	Q_UNUSED(r);
10383 
10384 	QString s="<ConstraintStudentsMinHoursDaily>\n";
10385 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
10386 	if(this->minHoursDaily>=0)
10387 		s+="	<Minimum_Hours_Daily>"+CustomFETString::number(this->minHoursDaily)+"</Minimum_Hours_Daily>\n";
10388 	else
10389 		assert(0);
10390 	if(this->allowEmptyDays)
10391 		s+="	<Allow_Empty_Days>true</Allow_Empty_Days>\n";
10392 	else
10393 		s+="	<Allow_Empty_Days>false</Allow_Empty_Days>\n";
10394 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
10395 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
10396 	s+="</ConstraintStudentsMinHoursDaily>\n";
10397 	return s;
10398 }
10399 
getDescription(Rules & r)10400 QString ConstraintStudentsMinHoursDaily::getDescription(Rules& r)
10401 {
10402 	Q_UNUSED(r);
10403 
10404 	QString begin=QString("");
10405 	if(!active)
10406 		begin="X - ";
10407 
10408 	QString end=QString("");
10409 	if(!comments.isEmpty())
10410 		end=", "+tr("C: %1", "Comments").arg(comments);
10411 
10412 	QString s;
10413 
10414 	if(this->allowEmptyDays)
10415 		s+="! ";
10416 	s+=tr("Students min hours daily");s+=", ";
10417 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
10418 	s+=tr("mH:%1", "Min hours (daily)").arg(this->minHoursDaily);s+=", ";
10419 	s+=tr("AED:%1", "Allow empty days").arg(yesNoTranslated(this->allowEmptyDays));
10420 
10421 	return begin+s+end;
10422 }
10423 
getDetailedDescription(Rules & r)10424 QString ConstraintStudentsMinHoursDaily::getDetailedDescription(Rules& r)
10425 {
10426 	Q_UNUSED(r);
10427 
10428 	QString s=tr("Time constraint");s+="\n";
10429 	if(this->allowEmptyDays==true){
10430 		s+=tr("(nonstandard, students may have empty days)");
10431 		s+="\n";
10432 	}
10433 	s+=tr("All students must respect the minimum number of hours daily");s+="\n";
10434 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
10435 	s+=tr("Minimum hours daily=%1").arg(this->minHoursDaily);s+="\n";
10436 	s+=tr("Allow empty days=%1").arg(yesNoTranslated(this->allowEmptyDays));s+="\n";
10437 
10438 	if(!active){
10439 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
10440 		s+="\n";
10441 	}
10442 	if(!comments.isEmpty()){
10443 		s+=tr("Comments=%1").arg(comments);
10444 		s+="\n";
10445 	}
10446 
10447 	return s;
10448 }
10449 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)10450 double ConstraintStudentsMinHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
10451 {
10452 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
10453 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
10454 		c.teachersMatrixReady=true;
10455 		c.subgroupsMatrixReady=true;
10456 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
10457 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
10458 
10459 		c.changedForMatrixCalculation=false;
10460 	}
10461 
10462 	if(r.mode!=MORNINGS_AFTERNOONS){
10463 		int tmp;
10464 		int too_little;
10465 
10466 		assert(this->minHoursDaily>=0);
10467 
10468 		too_little=0;
10469 		for(int i=0; i<r.nInternalSubgroups; i++)
10470 			for(int j=0; j<r.nDaysPerWeek; j++){
10471 				tmp=0;
10472 				for(int k=0; k<r.nHoursPerDay; k++){
10473 					if(subgroupsMatrix[i][j][k]>=1)
10474 						tmp++;
10475 				}
10476 
10477 				bool searchDay;
10478 				if(this->allowEmptyDays==true)
10479 					searchDay=(tmp>0);
10480 				else
10481 					searchDay=true;
10482 
10483 				if(/*tmp>0*/ searchDay && this->minHoursDaily>=0 && tmp < this->minHoursDaily){ //we would like no less than minHoursDaily hours per day.
10484 					too_little += - tmp + this->minHoursDaily;
10485 
10486 					if(conflictsString!=nullptr){
10487 						QString s=tr("Time constraint students min hours daily broken for subgroup: %1, day: %2, length=%3, conflict increase=%4")
10488 						 .arg(r.internalSubgroupsList[i]->name)
10489 						 .arg(r.daysOfTheWeek[j])
10490 						 .arg(CustomFETString::number(tmp))
10491 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(-tmp+this->minHoursDaily)));
10492 
10493 						dl.append(s);
10494 						cl.append(weightPercentage/100*(-tmp+this->minHoursDaily));
10495 
10496 						*conflictsString+= s+"\n";
10497 					}
10498 				}
10499 			}
10500 
10501 		//should not consider for empty days
10502 
10503 		assert(too_little>=0);
10504 
10505 		if(c.nPlacedActivities==r.nInternalActivities)
10506 			if(weightPercentage==100) //does not work for partial solutions
10507 				assert(too_little==0);
10508 
10509 		return too_little * weightPercentage/100;
10510 	}
10511 	else{
10512 		int tmp1, tmp2;
10513 		int too_little;
10514 
10515 		assert(this->minHoursDaily>=0);
10516 
10517 		too_little=0;
10518 		for(int i=0; i<r.nInternalSubgroups; i++)
10519 			for(int j=0; j<r.nDaysPerWeek/2; j++){
10520 				tmp1=0;
10521 				for(int k=0; k<r.nHoursPerDay; k++){
10522 					if(subgroupsMatrix[i][2*j][k]>=1)
10523 						tmp1++;
10524 				}
10525 
10526 				if(tmp1>0 && tmp1<this->minHoursDaily){
10527 					too_little += - tmp1 + this->minHoursDaily;
10528 
10529 					if(conflictsString!=nullptr){
10530 						QString s=tr("Time constraint students min hours daily broken for subgroup: %1, day: %2, length=%3, conflict increase=%4")
10531 						 .arg(r.internalSubgroupsList[i]->name)
10532 						 .arg(r.daysOfTheWeek[2*j])
10533 						 .arg(CustomFETString::number(tmp1))
10534 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(-tmp1+this->minHoursDaily)));
10535 
10536 						dl.append(s);
10537 						cl.append(weightPercentage/100*(-tmp1+this->minHoursDaily));
10538 
10539 						*conflictsString+= s+"\n";
10540 					}
10541 				}
10542 
10543 				tmp2=0;
10544 				for(int k=0; k<r.nHoursPerDay; k++){
10545 					if(subgroupsMatrix[i][2*j+1][k]>=1)
10546 						tmp2++;
10547 				}
10548 
10549 				if(tmp2>0 && tmp2<this->minHoursDaily){
10550 					too_little += - tmp2 + this->minHoursDaily;
10551 
10552 					if(conflictsString!=nullptr){
10553 						QString s=tr("Time constraint students min hours daily broken for subgroup: %1, day: %2, length=%3, conflict increase=%4")
10554 						 .arg(r.internalSubgroupsList[i]->name)
10555 						 .arg(r.daysOfTheWeek[2*j+1])
10556 						 .arg(CustomFETString::number(tmp2))
10557 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(-tmp2+this->minHoursDaily)));
10558 
10559 						dl.append(s);
10560 						cl.append(weightPercentage/100*(-tmp2+this->minHoursDaily));
10561 
10562 						*conflictsString+= s+"\n";
10563 					}
10564 				}
10565 
10566 				if(!this->allowEmptyDays==true)
10567 					if(tmp1+tmp2==0){
10568 						too_little++;
10569 
10570 						if(conflictsString!=nullptr){
10571 							QString s=tr("Time constraint students min hours daily broken for subgroup: %1, real day: %2, empty real day, but"
10572 							 " the constraint does not allow empty real days, conflict increase=%3")
10573 							 .arg(r.internalSubgroupsList[i]->name)
10574 							 .arg(j)
10575 							 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(1)));
10576 
10577 							dl.append(s);
10578 							cl.append(weightPercentage/100*1);
10579 
10580 							*conflictsString+= s+"\n";
10581 						}
10582 					}
10583 			}
10584 
10585 		assert(too_little>=0);
10586 
10587 		if(c.nPlacedActivities==r.nInternalActivities)
10588 			if(weightPercentage==100) //does not work for partial solutions
10589 				assert(too_little==0);
10590 
10591 		return too_little * weightPercentage/100;
10592 	}
10593 }
10594 
isRelatedToActivity(Rules & r,Activity * a)10595 bool ConstraintStudentsMinHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
10596 {
10597 	Q_UNUSED(r);
10598 	Q_UNUSED(a);
10599 
10600 	return false;
10601 }
10602 
isRelatedToTeacher(Teacher * t)10603 bool ConstraintStudentsMinHoursDaily::isRelatedToTeacher(Teacher* t)
10604 {
10605 	Q_UNUSED(t);
10606 
10607 	return false;
10608 }
10609 
isRelatedToSubject(Subject * s)10610 bool ConstraintStudentsMinHoursDaily::isRelatedToSubject(Subject* s)
10611 {
10612 	Q_UNUSED(s);
10613 
10614 	return false;
10615 }
10616 
isRelatedToActivityTag(ActivityTag * s)10617 bool ConstraintStudentsMinHoursDaily::isRelatedToActivityTag(ActivityTag* s)
10618 {
10619 	Q_UNUSED(s);
10620 
10621 	return false;
10622 }
10623 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)10624 bool ConstraintStudentsMinHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
10625 {
10626 	Q_UNUSED(r);
10627 	Q_UNUSED(s);
10628 
10629 	return true;
10630 }
10631 
hasWrongDayOrHour(Rules & r)10632 bool ConstraintStudentsMinHoursDaily::hasWrongDayOrHour(Rules& r)
10633 {
10634 	if(minHoursDaily>r.nHoursPerDay)
10635 		return true;
10636 
10637 	return false;
10638 }
10639 
canRepairWrongDayOrHour(Rules & r)10640 bool ConstraintStudentsMinHoursDaily::canRepairWrongDayOrHour(Rules& r)
10641 {
10642 	assert(hasWrongDayOrHour(r));
10643 
10644 	return true;
10645 }
10646 
repairWrongDayOrHour(Rules & r)10647 bool ConstraintStudentsMinHoursDaily::repairWrongDayOrHour(Rules& r)
10648 {
10649 	assert(hasWrongDayOrHour(r));
10650 
10651 	if(minHoursDaily>r.nHoursPerDay)
10652 		minHoursDaily=r.nHoursPerDay;
10653 
10654 	return true;
10655 }
10656 
10657 ////////////////////////////////////////////////////////////////////////////////////////////
10658 ////////////////////////////////////////////////////////////////////////////////////////////
10659 
ConstraintStudentsSetMinHoursDaily()10660 ConstraintStudentsSetMinHoursDaily::ConstraintStudentsSetMinHoursDaily()
10661 	: TimeConstraint()
10662 {
10663 	this->type = CONSTRAINT_STUDENTS_SET_MIN_HOURS_DAILY;
10664 	this->minHoursDaily = -1;
10665 
10666 	this->allowEmptyDays=false;
10667 }
10668 
ConstraintStudentsSetMinHoursDaily(double wp,int minnh,const QString & s,bool _allowEmptyDays)10669 ConstraintStudentsSetMinHoursDaily::ConstraintStudentsSetMinHoursDaily(double wp, int minnh, const QString& s, bool _allowEmptyDays)
10670 	: TimeConstraint(wp)
10671 {
10672 	this->minHoursDaily = minnh;
10673 	this->students = s;
10674 	this->type = CONSTRAINT_STUDENTS_SET_MIN_HOURS_DAILY;
10675 
10676 	this->allowEmptyDays=_allowEmptyDays;
10677 }
10678 
hasInactiveActivities(Rules & r)10679 bool ConstraintStudentsSetMinHoursDaily::hasInactiveActivities(Rules& r)
10680 {
10681 	Q_UNUSED(r);
10682 	return false;
10683 }
10684 
getXmlDescription(Rules & r)10685 QString ConstraintStudentsSetMinHoursDaily::getXmlDescription(Rules& r)
10686 {
10687 	Q_UNUSED(r);
10688 
10689 	QString s="<ConstraintStudentsSetMinHoursDaily>\n";
10690 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
10691 	s+="	<Minimum_Hours_Daily>"+CustomFETString::number(this->minHoursDaily)+"</Minimum_Hours_Daily>\n";
10692 	s+="	<Students>"+protect(this->students)+"</Students>\n";
10693 	if(this->allowEmptyDays)
10694 		s+="	<Allow_Empty_Days>true</Allow_Empty_Days>\n";
10695 	else
10696 		s+="	<Allow_Empty_Days>false</Allow_Empty_Days>\n";
10697 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
10698 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
10699 	s+="</ConstraintStudentsSetMinHoursDaily>\n";
10700 	return s;
10701 }
10702 
getDescription(Rules & r)10703 QString ConstraintStudentsSetMinHoursDaily::getDescription(Rules& r)
10704 {
10705 	Q_UNUSED(r);
10706 
10707 	QString begin=QString("");
10708 	if(!active)
10709 		begin="X - ";
10710 
10711 	QString end=QString("");
10712 	if(!comments.isEmpty())
10713 		end=", "+tr("C: %1", "Comments").arg(comments);
10714 
10715 	QString s;
10716 
10717 	if(this->allowEmptyDays)
10718 		s+="! ";
10719 	s+=tr("Students set min hours daily");s+=", ";
10720 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
10721 	s+=tr("St:%1", "Students (set)").arg(this->students);s+=", ";
10722 	s+=tr("mH:%1", "Min hours (daily)").arg(this->minHoursDaily);s+=", ";
10723 	s+=tr("AED:%1", "Allow empty days").arg(yesNoTranslated(this->allowEmptyDays));
10724 
10725 	return begin+s+end;
10726 }
10727 
getDetailedDescription(Rules & r)10728 QString ConstraintStudentsSetMinHoursDaily::getDetailedDescription(Rules& r)
10729 {
10730 	Q_UNUSED(r);
10731 
10732 	QString s=tr("Time constraint");s+="\n";
10733 	if(this->allowEmptyDays==true){
10734 		s+=tr("(nonstandard, students may have empty days)");
10735 		s+="\n";
10736 	}
10737 	s+=tr("A students set must respect the minimum number of hours daily");s+="\n";
10738 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
10739 	s+=tr("Students set=%1").arg(this->students);s+="\n";
10740 	s+=tr("Minimum hours daily=%1").arg(this->minHoursDaily);s+="\n";
10741 	s+=tr("Allow empty days=%1").arg(yesNoTranslated(this->allowEmptyDays));s+="\n";
10742 
10743 	if(!active){
10744 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
10745 		s+="\n";
10746 	}
10747 	if(!comments.isEmpty()){
10748 		s+=tr("Comments=%1").arg(comments);
10749 		s+="\n";
10750 	}
10751 
10752 	return s;
10753 }
10754 
computeInternalStructure(QWidget * parent,Rules & r)10755 bool ConstraintStudentsSetMinHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
10756 {
10757 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
10758 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
10759 
10760 	if(ss==nullptr){
10761 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
10762 		 tr("Constraint students set min hours daily is wrong because it refers to inexistent students set."
10763 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
10764 
10765 		return false;
10766 	}
10767 
10768 	assert(ss!=nullptr);
10769 
10770 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
10771 	/*this->iSubgroupsList.clear();
10772 	if(ss->type==STUDENTS_SUBGROUP){
10773 		int tmp;
10774 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
10775 		assert(tmp>=0);
10776 		assert(tmp<r.nInternalSubgroups);
10777 		if(!this->iSubgroupsList.contains(tmp))
10778 			this->iSubgroupsList.append(tmp);
10779 	}
10780 	else if(ss->type==STUDENTS_GROUP){
10781 		StudentsGroup* stg=(StudentsGroup*)ss;
10782 		for(int i=0; i<stg->subgroupsList.size(); i++){
10783 			StudentsSubgroup* sts=stg->subgroupsList[i];
10784 			int tmp;
10785 			tmp=sts->indexInInternalSubgroupsList;
10786 			assert(tmp>=0);
10787 			assert(tmp<r.nInternalSubgroups);
10788 			if(!this->iSubgroupsList.contains(tmp))
10789 				this->iSubgroupsList.append(tmp);
10790 		}
10791 	}
10792 	else if(ss->type==STUDENTS_YEAR){
10793 		StudentsYear* sty=(StudentsYear*)ss;
10794 		for(int i=0; i<sty->groupsList.size(); i++){
10795 			StudentsGroup* stg=sty->groupsList[i];
10796 			for(int j=0; j<stg->subgroupsList.size(); j++){
10797 				StudentsSubgroup* sts=stg->subgroupsList[j];
10798 				int tmp;
10799 				tmp=sts->indexInInternalSubgroupsList;
10800 				assert(tmp>=0);
10801 				assert(tmp<r.nInternalSubgroups);
10802 				if(!this->iSubgroupsList.contains(tmp))
10803 					this->iSubgroupsList.append(tmp);
10804 			}
10805 		}
10806 	}
10807 	else
10808 		assert(0);*/
10809 
10810 	return true;
10811 }
10812 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)10813 double ConstraintStudentsSetMinHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
10814 {
10815 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
10816 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
10817 		c.teachersMatrixReady=true;
10818 		c.subgroupsMatrixReady=true;
10819 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
10820 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
10821 
10822 		c.changedForMatrixCalculation=false;
10823 	}
10824 
10825 	if(r.mode!=MORNINGS_AFTERNOONS){
10826 		int tmp;
10827 		int too_little;
10828 
10829 		assert(this->minHoursDaily>=0);
10830 
10831 		too_little=0;
10832 		for(int sg=0; sg<this->iSubgroupsList.count(); sg++){
10833 			int i=iSubgroupsList.at(sg);
10834 			for(int j=0; j<r.nDaysPerWeek; j++){
10835 				tmp=0;
10836 				for(int k=0; k<r.nHoursPerDay; k++){
10837 					if(subgroupsMatrix[i][j][k]>=1)
10838 						tmp++;
10839 				}
10840 
10841 				bool searchDay;
10842 				if(this->allowEmptyDays==true)
10843 					searchDay=(tmp>0);
10844 				else
10845 					searchDay=true;
10846 
10847 				if(/*tmp>0*/ searchDay && this->minHoursDaily>=0 && tmp < this->minHoursDaily){
10848 					too_little += - tmp + this->minHoursDaily;
10849 
10850 					if(conflictsString!=nullptr){
10851 						QString s=tr("Time constraint students set min hours daily broken for subgroup: %1, day: %2, length=%3, conflicts increase=%4")
10852 						 .arg(r.internalSubgroupsList[i]->name)
10853 						 .arg(r.daysOfTheWeek[j])
10854 						 .arg(CustomFETString::number(tmp))
10855 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision((-tmp+this->minHoursDaily)*weightPercentage/100));
10856 
10857 						dl.append(s);
10858 						cl.append((-tmp+this->minHoursDaily)*weightPercentage/100);
10859 
10860 						*conflictsString+= s+"\n";
10861 					}
10862 				}
10863 			}
10864 		}
10865 
10866 		assert(too_little>=0);
10867 
10868 		if(c.nPlacedActivities==r.nInternalActivities)
10869 			if(weightPercentage==100) //does not work for partial solutions
10870 				assert(too_little==0);
10871 
10872 		return too_little * weightPercentage / 100.0;
10873 	}
10874 	else{
10875 		int tmp1, tmp2;
10876 		int too_little;
10877 
10878 		assert(this->minHoursDaily>=0);
10879 
10880 		too_little=0;
10881 		for(int sg=0; sg<this->iSubgroupsList.count(); sg++){
10882 			int i=iSubgroupsList.at(sg);
10883 			for(int j=0; j<r.nDaysPerWeek/2; j++){
10884 				tmp1=0;
10885 				for(int k=0; k<r.nHoursPerDay; k++){
10886 					if(subgroupsMatrix[i][2*j][k]>=1)
10887 						tmp1++;
10888 				}
10889 
10890 				if(tmp1>0 && tmp1<this->minHoursDaily){
10891 					too_little += - tmp1 + this->minHoursDaily;
10892 
10893 					if(conflictsString!=nullptr){
10894 						QString s=tr("Time constraint students set min hours daily broken for subgroup: %1, day: %2, length=%3, conflict increase=%4")
10895 						 .arg(r.internalSubgroupsList[i]->name)
10896 						 .arg(r.daysOfTheWeek[2*j])
10897 						 .arg(CustomFETString::number(tmp1))
10898 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(-tmp1+this->minHoursDaily)));
10899 
10900 						dl.append(s);
10901 						cl.append(weightPercentage/100*(-tmp1+this->minHoursDaily));
10902 
10903 						*conflictsString+= s+"\n";
10904 					}
10905 				}
10906 
10907 				tmp2=0;
10908 				for(int k=0; k<r.nHoursPerDay; k++){
10909 					if(subgroupsMatrix[i][2*j+1][k]>=1)
10910 						tmp2++;
10911 				}
10912 
10913 				if(tmp2>0 && tmp2<this->minHoursDaily){
10914 					too_little += - tmp2 + this->minHoursDaily;
10915 
10916 					if(conflictsString!=nullptr){
10917 						QString s=tr("Time constraint students set min hours daily broken for subgroup: %1, day: %2, length=%3, conflict increase=%4")
10918 						 .arg(r.internalSubgroupsList[i]->name)
10919 						 .arg(r.daysOfTheWeek[2*j+1])
10920 						 .arg(CustomFETString::number(tmp2))
10921 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(-tmp2+this->minHoursDaily)));
10922 
10923 						dl.append(s);
10924 						cl.append(weightPercentage/100*(-tmp2+this->minHoursDaily));
10925 
10926 						*conflictsString+= s+"\n";
10927 					}
10928 				}
10929 
10930 				if(!this->allowEmptyDays==true)
10931 					if(tmp1+tmp2==0){
10932 						too_little++;
10933 
10934 						if(conflictsString!=nullptr){
10935 							QString s=tr("Time constraint students set min hours daily broken for subgroup: %1, real day: %2, empty real day, but"
10936 							 " the constraint does not allow empty real days, conflict increase=%3")
10937 							 .arg(r.internalSubgroupsList[i]->name)
10938 							 .arg(j)
10939 							 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(1)));
10940 
10941 							dl.append(s);
10942 							cl.append(weightPercentage/100*1);
10943 
10944 							*conflictsString+= s+"\n";
10945 						}
10946 					}
10947 			}
10948 		}
10949 
10950 		assert(too_little>=0);
10951 
10952 		if(c.nPlacedActivities==r.nInternalActivities)
10953 			if(weightPercentage==100) //does not work for partial solutions
10954 				assert(too_little==0);
10955 
10956 		return too_little * weightPercentage / 100.0;
10957 	}
10958 }
10959 
isRelatedToActivity(Rules & r,Activity * a)10960 bool ConstraintStudentsSetMinHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
10961 {
10962 	Q_UNUSED(r);
10963 	Q_UNUSED(a);
10964 
10965 	return false;
10966 }
10967 
isRelatedToTeacher(Teacher * t)10968 bool ConstraintStudentsSetMinHoursDaily::isRelatedToTeacher(Teacher* t)
10969 {
10970 	Q_UNUSED(t);
10971 
10972 	return false;
10973 }
10974 
isRelatedToSubject(Subject * s)10975 bool ConstraintStudentsSetMinHoursDaily::isRelatedToSubject(Subject* s)
10976 {
10977 	Q_UNUSED(s);
10978 
10979 	return false;
10980 }
10981 
isRelatedToActivityTag(ActivityTag * s)10982 bool ConstraintStudentsSetMinHoursDaily::isRelatedToActivityTag(ActivityTag* s)
10983 {
10984 	Q_UNUSED(s);
10985 
10986 	return false;
10987 }
10988 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)10989 bool ConstraintStudentsSetMinHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
10990 {
10991 	return r.setsShareStudents(this->students, s->name);
10992 }
10993 
hasWrongDayOrHour(Rules & r)10994 bool ConstraintStudentsSetMinHoursDaily::hasWrongDayOrHour(Rules& r)
10995 {
10996 	if(minHoursDaily>r.nHoursPerDay)
10997 		return true;
10998 
10999 	return false;
11000 }
11001 
canRepairWrongDayOrHour(Rules & r)11002 bool ConstraintStudentsSetMinHoursDaily::canRepairWrongDayOrHour(Rules& r)
11003 {
11004 	assert(hasWrongDayOrHour(r));
11005 
11006 	return true;
11007 }
11008 
repairWrongDayOrHour(Rules & r)11009 bool ConstraintStudentsSetMinHoursDaily::repairWrongDayOrHour(Rules& r)
11010 {
11011 	assert(hasWrongDayOrHour(r));
11012 
11013 	if(minHoursDaily>r.nHoursPerDay)
11014 		minHoursDaily=r.nHoursPerDay;
11015 
11016 	return true;
11017 }
11018 
11019 ////////////////////////////////////////////////////////////////////////////////////////////
11020 ////////////////////////////////////////////////////////////////////////////////////////////
11021 
ConstraintActivityPreferredStartingTime()11022 ConstraintActivityPreferredStartingTime::ConstraintActivityPreferredStartingTime()
11023 	: TimeConstraint()
11024 {
11025 	this->type = CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIME;
11026 }
11027 
ConstraintActivityPreferredStartingTime(double wp,int actId,int d,int h,bool perm)11028 ConstraintActivityPreferredStartingTime::ConstraintActivityPreferredStartingTime(double wp, int actId, int d, int h, bool perm)
11029 	: TimeConstraint(wp)
11030 {
11031 	this->activityId = actId;
11032 	this->day = d;
11033 	this->hour = h;
11034 	this->type = CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIME;
11035 	this->permanentlyLocked=perm;
11036 }
11037 
operator ==(const ConstraintActivityPreferredStartingTime & c)11038 bool ConstraintActivityPreferredStartingTime::operator==(const ConstraintActivityPreferredStartingTime& c){
11039 	if(this->day!=c.day)
11040 		return false;
11041 	if(this->hour!=c.hour)
11042 		return false;
11043 	if(this->activityId!=c.activityId)
11044 		return false;
11045 	if(this->weightPercentage!=c.weightPercentage)
11046 		return false;
11047 	if(this->active!=c.active)
11048 		return false;
11049 	//no need to care about permanently locked
11050 	return true;
11051 }
11052 
computeInternalStructure(QWidget * parent,Rules & r)11053 bool ConstraintActivityPreferredStartingTime::computeInternalStructure(QWidget* parent, Rules& r)
11054 {
11055 	/*Activity* act;
11056 	int i;
11057 	for(i=0; i<r.nInternalActivities; i++){
11058 		act=&r.internalActivitiesList[i];
11059 		if(act->id==this->activityId)
11060 			break;
11061 	}*/
11062 
11063 	int i=r.activitiesHash.value(activityId, r.nInternalActivities);
11064 
11065 	if(i==r.nInternalActivities){
11066 		//assert(0);
11067 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
11068 			tr("Following constraint is wrong (because it refers to invalid activity id). Please correct it (maybe removing it is a solution):\n%1").arg(this->getDetailedDescription(r)));
11069 		return false;
11070 	}
11071 
11072 	if(this->day >= r.nDaysPerWeek){
11073 		TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
11074 		 tr("Constraint activity preferred starting time is wrong because it refers to removed day. Please correct"
11075 		 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
11076 
11077 		return false;
11078 	}
11079 	if(this->hour == r.nHoursPerDay){
11080 		TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
11081 		 tr("Constraint activity preferred starting time is wrong because preferred hour is too late (after the last acceptable slot). Please correct"
11082 		 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
11083 
11084 		return false;
11085 	}
11086 	if(this->hour > r.nHoursPerDay){
11087 		TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
11088 		 tr("Constraint activity preferred starting time is wrong because it refers to removed hour. Please correct"
11089 		 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
11090 
11091 		return false;
11092 	}
11093 
11094 	this->activityIndex=i;
11095 	return true;
11096 }
11097 
hasInactiveActivities(Rules & r)11098 bool ConstraintActivityPreferredStartingTime::hasInactiveActivities(Rules& r)
11099 {
11100 	if(r.inactiveActivities.contains(this->activityId))
11101 		return true;
11102 	return false;
11103 }
11104 
getXmlDescription(Rules & r)11105 QString ConstraintActivityPreferredStartingTime::getXmlDescription(Rules& r)
11106 {
11107 	Q_UNUSED(r);
11108 
11109 	QString s="<ConstraintActivityPreferredStartingTime>\n";
11110 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
11111 	s+="	<Activity_Id>"+CustomFETString::number(this->activityId)+"</Activity_Id>\n";
11112 	if(this->day>=0)
11113 		s+="	<Preferred_Day>"+protect(r.daysOfTheWeek[this->day])+"</Preferred_Day>\n";
11114 	if(this->hour>=0)
11115 		s+="	<Preferred_Hour>"+protect(r.hoursOfTheDay[this->hour])+"</Preferred_Hour>\n";
11116 	s+="	<Permanently_Locked>";s+=trueFalse(this->permanentlyLocked);s+="</Permanently_Locked>\n";
11117 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
11118 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
11119 	s+="</ConstraintActivityPreferredStartingTime>\n";
11120 	return s;
11121 }
11122 
getDescription(Rules & r)11123 QString ConstraintActivityPreferredStartingTime::getDescription(Rules& r)
11124 {
11125 	Q_UNUSED(r);
11126 
11127 	QString begin=QString("");
11128 	if(!active)
11129 		begin="X - ";
11130 
11131 	QString end=QString("");
11132 	if(!comments.isEmpty())
11133 		end=", "+tr("C: %1", "Comments").arg(comments);
11134 
11135 	QString s;
11136 	s+=tr("Act. id: %1 (%2) has a preferred starting time: %3", "%1 is the id, %2 is the detailed description of the activity. %3 is time (day and hour)")
11137 	 .arg(this->activityId)
11138 	 .arg(getActivityDetailedDescription(r, this->activityId))
11139 	 .arg(r.daysOfTheWeek[this->day]+" "+r.hoursOfTheDay[this->hour]);
11140 
11141 	s+=", ";
11142 
11143 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
11144 	s+=", ";
11145 	s+=tr("PL:%1", "Abbreviation for permanently locked").arg(yesNoTranslated(this->permanentlyLocked));
11146 
11147 	return begin+s+end;
11148 }
11149 
getDetailedDescription(Rules & r)11150 QString ConstraintActivityPreferredStartingTime::getDetailedDescription(Rules& r)
11151 {
11152 	QString s=tr("Time constraint");s+="\n";
11153 	s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
11154 		.arg(this->activityId)
11155 		.arg(getActivityDetailedDescription(r, this->activityId));
11156 	s+="\n";
11157 
11158 	s+=tr("has a preferred starting time:");
11159 	s+="\n";
11160 	s+=tr("Day=%1").arg(r.daysOfTheWeek[this->day]);
11161 	s+="\n";
11162 	s+=tr("Hour=%1").arg(r.hoursOfTheDay[this->hour]);
11163 	s+="\n";
11164 
11165 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
11166 	if(this->permanentlyLocked){
11167 		s+=tr("This activity is permanently locked, which means you cannot unlock it from the 'Timetable' menu"
11168 		" (you can unlock this activity by removing the constraint from the constraints dialog or by setting the 'permanently"
11169 		" locked' attribute false when editing this constraint)");
11170 	}
11171 	else{
11172 		s+=tr("This activity is not permanently locked, which means you can unlock it from the 'Timetable' menu");
11173 	}
11174 	s+="\n";
11175 
11176 	if(!active){
11177 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
11178 		s+="\n";
11179 	}
11180 	if(!comments.isEmpty()){
11181 		s+=tr("Comments=%1").arg(comments);
11182 		s+="\n";
11183 	}
11184 
11185 	return s;
11186 }
11187 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)11188 double ConstraintActivityPreferredStartingTime::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
11189 {
11190 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
11191 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
11192 		c.teachersMatrixReady=true;
11193 		c.subgroupsMatrixReady=true;
11194 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
11195 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
11196 
11197 		c.changedForMatrixCalculation=false;
11198 	}
11199 
11200 	int nbroken;
11201 
11202 	assert(r.internalStructureComputed);
11203 
11204 	nbroken=0;
11205 	if(c.times[this->activityIndex]!=UNALLOCATED_TIME){
11206 		int d=c.times[this->activityIndex]%r.nDaysPerWeek; //the day when this activity was scheduled
11207 		int h=c.times[this->activityIndex]/r.nDaysPerWeek; //the hour
11208 		if(this->day>=0)
11209 			nbroken+=abs(this->day-d);
11210 		if(this->hour>=0)
11211 			nbroken+=abs(this->hour-h);
11212 	}
11213 	if(nbroken>0)
11214 		nbroken=1;
11215 
11216 	if(conflictsString!=nullptr && nbroken>0){
11217 		QString s=tr("Time constraint activity preferred starting time broken for activity with id=%1 (%2), increases conflicts total by %3",
11218 			"%1 is the id, %2 is the detailed description of the activity")
11219 			.arg(this->activityId)
11220 			.arg(getActivityDetailedDescription(r, this->activityId))
11221 			.arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*nbroken));
11222 
11223 		dl.append(s);
11224 		cl.append(weightPercentage/100*nbroken);
11225 
11226 		*conflictsString+= s+"\n";
11227 	}
11228 
11229 	if(weightPercentage==100)
11230 		assert(nbroken==0);
11231 	return nbroken * weightPercentage/100;
11232 }
11233 
isRelatedToActivity(Rules & r,Activity * a)11234 bool ConstraintActivityPreferredStartingTime::isRelatedToActivity(Rules& r, Activity* a)
11235 {
11236 	Q_UNUSED(r);
11237 
11238 	if(this->activityId==a->id)
11239 		return true;
11240 	return false;
11241 }
11242 
isRelatedToTeacher(Teacher * t)11243 bool ConstraintActivityPreferredStartingTime::isRelatedToTeacher(Teacher* t)
11244 {
11245 	Q_UNUSED(t);
11246 
11247 	return false;
11248 }
11249 
isRelatedToSubject(Subject * s)11250 bool ConstraintActivityPreferredStartingTime::isRelatedToSubject(Subject* s)
11251 {
11252 	Q_UNUSED(s);
11253 
11254 	return false;
11255 }
11256 
isRelatedToActivityTag(ActivityTag * s)11257 bool ConstraintActivityPreferredStartingTime::isRelatedToActivityTag(ActivityTag* s)
11258 {
11259 	Q_UNUSED(s);
11260 
11261 	return false;
11262 }
11263 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)11264 bool ConstraintActivityPreferredStartingTime::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
11265 {
11266 	Q_UNUSED(r);
11267 	Q_UNUSED(s);
11268 
11269 	return false;
11270 }
11271 
hasWrongDayOrHour(Rules & r)11272 bool ConstraintActivityPreferredStartingTime::hasWrongDayOrHour(Rules& r)
11273 {
11274 	if(day<0 || day>=r.nDaysPerWeek || hour<0 || hour>=r.nHoursPerDay)
11275 		return true;
11276 	return false;
11277 }
11278 
canRepairWrongDayOrHour(Rules & r)11279 bool ConstraintActivityPreferredStartingTime::canRepairWrongDayOrHour(Rules& r)
11280 {
11281 	assert(hasWrongDayOrHour(r));
11282 
11283 	return false;
11284 }
11285 
repairWrongDayOrHour(Rules & r)11286 bool ConstraintActivityPreferredStartingTime::repairWrongDayOrHour(Rules& r)
11287 {
11288 	assert(hasWrongDayOrHour(r));
11289 
11290 	return false;
11291 }
11292 
11293 ////////////////////////////////////////////////////////////////////////////////////////////
11294 ////////////////////////////////////////////////////////////////////////////////////////////
11295 
ConstraintActivityPreferredTimeSlots()11296 ConstraintActivityPreferredTimeSlots::ConstraintActivityPreferredTimeSlots()
11297 	: TimeConstraint()
11298 {
11299 	this->type = CONSTRAINT_ACTIVITY_PREFERRED_TIME_SLOTS;
11300 }
11301 
ConstraintActivityPreferredTimeSlots(double wp,int actId,int nPT_L,QList<int> d_L,QList<int> h_L)11302 ConstraintActivityPreferredTimeSlots::ConstraintActivityPreferredTimeSlots(double wp, int actId, int nPT_L, QList<int> d_L, QList<int> h_L)
11303 	: TimeConstraint(wp)
11304 {
11305 	assert(d_L.count()==nPT_L);
11306 	assert(h_L.count()==nPT_L);
11307 
11308 	this->p_activityId=actId;
11309 	this->p_nPreferredTimeSlots_L=nPT_L;
11310 	this->p_days_L=d_L;
11311 	this->p_hours_L=h_L;
11312 	this->type=CONSTRAINT_ACTIVITY_PREFERRED_TIME_SLOTS;
11313 }
11314 
computeInternalStructure(QWidget * parent,Rules & r)11315 bool ConstraintActivityPreferredTimeSlots::computeInternalStructure(QWidget* parent, Rules& r)
11316 {
11317 	/*Activity* act;
11318 	int i;
11319 	for(i=0; i<r.nInternalActivities; i++){
11320 		act=&r.internalActivitiesList[i];
11321 		if(act->id==this->p_activityId)
11322 			break;
11323 	}*/
11324 
11325 	int i=r.activitiesHash.value(p_activityId, r.nInternalActivities);
11326 
11327 	if(i==r.nInternalActivities){
11328 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
11329 			tr("Following constraint is wrong (because it refers to invalid activity id). Please correct it (maybe removing it is a solution):\n%1").arg(this->getDetailedDescription(r)));
11330 		//assert(0);
11331 		return false;
11332 	}
11333 
11334 	for(int k=0; k<p_nPreferredTimeSlots_L; k++){
11335 		if(this->p_days_L[k] >= r.nDaysPerWeek){
11336 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
11337 			 tr("Constraint activity preferred time slots is wrong because it refers to removed day. Please correct"
11338 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
11339 
11340 			return false;
11341 		}
11342 		if(this->p_hours_L[k] == r.nHoursPerDay){
11343 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
11344 			 tr("Constraint activity preferred time slots is wrong because a preferred hour is too late (after the last acceptable slot). Please correct"
11345 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
11346 
11347 			return false;
11348 		}
11349 		if(this->p_hours_L[k] > r.nHoursPerDay){
11350 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
11351 			 tr("Constraint activity preferred time slots is wrong because it refers to removed hour. Please correct"
11352 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
11353 
11354 			return false;
11355 		}
11356 
11357 		if(this->p_hours_L[k]<0 || this->p_days_L[k]<0){
11358 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
11359 			 tr("Constraint activity preferred time slots is wrong because it has hour or day not specified for a slot (-1). Please correct"
11360 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
11361 
11362 			return false;
11363 		}
11364 	}
11365 
11366 	this->p_activityIndex=i;
11367 	return true;
11368 }
11369 
hasInactiveActivities(Rules & r)11370 bool ConstraintActivityPreferredTimeSlots::hasInactiveActivities(Rules& r)
11371 {
11372 	if(r.inactiveActivities.contains(this->p_activityId))
11373 		return true;
11374 	return false;
11375 }
11376 
getXmlDescription(Rules & r)11377 QString ConstraintActivityPreferredTimeSlots::getXmlDescription(Rules& r)
11378 {
11379 	QString s="<ConstraintActivityPreferredTimeSlots>\n";
11380 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
11381 	s+="	<Activity_Id>"+CustomFETString::number(this->p_activityId)+"</Activity_Id>\n";
11382 	s+="	<Number_of_Preferred_Time_Slots>"+CustomFETString::number(this->p_nPreferredTimeSlots_L)+"</Number_of_Preferred_Time_Slots>\n";
11383 	for(int i=0; i<p_nPreferredTimeSlots_L; i++){
11384 		s+="	<Preferred_Time_Slot>\n";
11385 		if(this->p_days_L[i]>=0)
11386 			s+="		<Preferred_Day>"+protect(r.daysOfTheWeek[this->p_days_L[i]])+"</Preferred_Day>\n";
11387 		if(this->p_hours_L[i]>=0)
11388 			s+="		<Preferred_Hour>"+protect(r.hoursOfTheDay[this->p_hours_L[i]])+"</Preferred_Hour>\n";
11389 		s+="	</Preferred_Time_Slot>\n";
11390 	}
11391 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
11392 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
11393 	s+="</ConstraintActivityPreferredTimeSlots>\n";
11394 	return s;
11395 }
11396 
getDescription(Rules & r)11397 QString ConstraintActivityPreferredTimeSlots::getDescription(Rules& r)
11398 {
11399 	QString begin=QString("");
11400 	if(!active)
11401 		begin="X - ";
11402 
11403 	QString end=QString("");
11404 	if(!comments.isEmpty())
11405 		end=", "+tr("C: %1", "Comments").arg(comments);
11406 
11407 	QString s;
11408 	s+=tr("Act. id: %1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
11409 		.arg(this->p_activityId)
11410 		.arg(getActivityDetailedDescription(r, this->p_activityId));
11411 	s+=" ";
11412 
11413 	s+=tr("has a set of preferred time slots:");
11414 	s+=" ";
11415 	for(int i=0; i<this->p_nPreferredTimeSlots_L; i++){
11416 		if(this->p_days_L[i]>=0){
11417 			s+=r.daysOfTheWeek[this->p_days_L[i]];
11418 			s+=" ";
11419 		}
11420 		if(this->p_hours_L[i]>=0){
11421 			s+=r.hoursOfTheDay[this->p_hours_L[i]];
11422 		}
11423 		if(i<this->p_nPreferredTimeSlots_L-1)
11424 			s+="; ";
11425 	}
11426 	s+=", ";
11427 
11428 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
11429 
11430 	return begin+s+end;
11431 }
11432 
getDetailedDescription(Rules & r)11433 QString ConstraintActivityPreferredTimeSlots::getDetailedDescription(Rules& r)
11434 {
11435 	QString s=tr("Time constraint");s+="\n";
11436 	s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
11437 		.arg(this->p_activityId)
11438 		.arg(getActivityDetailedDescription(r, this->p_activityId));
11439 	s+="\n";
11440 
11441 	s+=tr("has a set of preferred time slots (all hours of the activity must be in the allowed slots):");
11442 	s+="\n";
11443 	for(int i=0; i<this->p_nPreferredTimeSlots_L; i++){
11444 		if(this->p_days_L[i]>=0){
11445 			s+=r.daysOfTheWeek[this->p_days_L[i]];
11446 			s+=" ";
11447 		}
11448 		if(this->p_hours_L[i]>=0){
11449 			s+=r.hoursOfTheDay[this->p_hours_L[i]];
11450 		}
11451 		if(i<this->p_nPreferredTimeSlots_L-1)
11452 			s+=";  ";
11453 	}
11454 	s+="\n";
11455 
11456 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
11457 
11458 	if(!active){
11459 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
11460 		s+="\n";
11461 	}
11462 	if(!comments.isEmpty()){
11463 		s+=tr("Comments=%1").arg(comments);
11464 		s+="\n";
11465 	}
11466 
11467 	return s;
11468 }
11469 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)11470 double ConstraintActivityPreferredTimeSlots::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
11471 {
11472 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
11473 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
11474 		c.teachersMatrixReady=true;
11475 		c.subgroupsMatrixReady=true;
11476 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
11477 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
11478 
11479 		c.changedForMatrixCalculation=false;
11480 	}
11481 
11482 	int nbroken;
11483 
11484 	assert(r.internalStructureComputed);
11485 
11486 	Matrix2D<bool> allowed;
11487 	allowed.resize(r.nDaysPerWeek, r.nHoursPerDay);
11488 	for(int d=0; d<r.nDaysPerWeek; d++)
11489 		for(int h=0; h<r.nHoursPerDay; h++)
11490 			allowed[d][h]=false;
11491 	for(int i=0; i<this->p_nPreferredTimeSlots_L; i++){
11492 		if(this->p_days_L[i]>=0 && this->p_hours_L[i]>=0)
11493 			allowed[this->p_days_L[i]][this->p_hours_L[i]]=true;
11494 		else
11495 			assert(0);
11496 	}
11497 
11498 	nbroken=0;
11499 	if(c.times[this->p_activityIndex]!=UNALLOCATED_TIME){
11500 		int d=c.times[this->p_activityIndex]%r.nDaysPerWeek; //the day when this activity was scheduled
11501 		int h=c.times[this->p_activityIndex]/r.nDaysPerWeek; //the hour
11502 		for(int dur=0; dur<r.internalActivitiesList[this->p_activityIndex].duration; dur++)
11503 			if(!allowed[d][h+dur])
11504 				nbroken++;
11505 	}
11506 
11507 	if(conflictsString!=nullptr && nbroken>0){
11508 		QString s=tr("Time constraint activity preferred time slots broken for activity with id=%1 (%2) on %3 hours, increases conflicts total by %4",
11509 		 "%1 is the id, %2 is the detailed description of the activity.")
11510 		 .arg(this->p_activityId)
11511 		 .arg(getActivityDetailedDescription(r, this->p_activityId))
11512 		 .arg(nbroken)
11513 		 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*nbroken));
11514 
11515 		dl.append(s);
11516 		cl.append(weightPercentage/100*nbroken);
11517 
11518 		*conflictsString+= s+"\n";
11519 	}
11520 
11521 	if(weightPercentage==100)
11522 		assert(nbroken==0);
11523 	return nbroken * weightPercentage/100;
11524 }
11525 
isRelatedToActivity(Rules & r,Activity * a)11526 bool ConstraintActivityPreferredTimeSlots::isRelatedToActivity(Rules& r, Activity* a)
11527 {
11528 	Q_UNUSED(r);
11529 
11530 	if(this->p_activityId==a->id)
11531 		return true;
11532 	return false;
11533 }
11534 
isRelatedToTeacher(Teacher * t)11535 bool ConstraintActivityPreferredTimeSlots::isRelatedToTeacher(Teacher* t)
11536 {
11537 	Q_UNUSED(t);
11538 
11539 	return false;
11540 }
11541 
isRelatedToSubject(Subject * s)11542 bool ConstraintActivityPreferredTimeSlots::isRelatedToSubject(Subject* s)
11543 {
11544 	Q_UNUSED(s);
11545 
11546 	return false;
11547 }
11548 
isRelatedToActivityTag(ActivityTag * s)11549 bool ConstraintActivityPreferredTimeSlots::isRelatedToActivityTag(ActivityTag* s)
11550 {
11551 	Q_UNUSED(s);
11552 
11553 	return false;
11554 }
11555 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)11556 bool ConstraintActivityPreferredTimeSlots::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
11557 {
11558 	Q_UNUSED(r);
11559 	Q_UNUSED(s);
11560 
11561 	return false;
11562 }
11563 
hasWrongDayOrHour(Rules & r)11564 bool ConstraintActivityPreferredTimeSlots::hasWrongDayOrHour(Rules& r)
11565 {
11566 	assert(p_nPreferredTimeSlots_L==p_days_L.count());
11567 	assert(p_nPreferredTimeSlots_L==p_hours_L.count());
11568 
11569 	for(int i=0; i<p_nPreferredTimeSlots_L; i++)
11570 		if(p_days_L.at(i)<0 || p_days_L.at(i)>=r.nDaysPerWeek
11571 		 || p_hours_L.at(i)<0 || p_hours_L.at(i)>=r.nHoursPerDay)
11572 			return true;
11573 
11574 	return false;
11575 }
11576 
canRepairWrongDayOrHour(Rules & r)11577 bool ConstraintActivityPreferredTimeSlots::canRepairWrongDayOrHour(Rules& r)
11578 {
11579 	assert(hasWrongDayOrHour(r));
11580 
11581 	return true;
11582 }
11583 
repairWrongDayOrHour(Rules & r)11584 bool ConstraintActivityPreferredTimeSlots::repairWrongDayOrHour(Rules& r)
11585 {
11586 	assert(hasWrongDayOrHour(r));
11587 
11588 	assert(p_nPreferredTimeSlots_L==p_days_L.count());
11589 	assert(p_nPreferredTimeSlots_L==p_hours_L.count());
11590 
11591 	QList<int> newDays;
11592 	QList<int> newHours;
11593 	int newNPref=0;
11594 
11595 	for(int i=0; i<p_nPreferredTimeSlots_L; i++)
11596 		if(p_days_L.at(i)>=0 && p_days_L.at(i)<r.nDaysPerWeek
11597 		 && p_hours_L.at(i)>=0 && p_hours_L.at(i)<r.nHoursPerDay){
11598 			newDays.append(p_days_L.at(i));
11599 			newHours.append(p_hours_L.at(i));
11600 			newNPref++;
11601 		}
11602 
11603 	p_nPreferredTimeSlots_L=newNPref;
11604 	p_days_L=newDays;
11605 	p_hours_L=newHours;
11606 
11607 	r.internalStructureComputed=false;
11608 	setRulesModifiedAndOtherThings(&r);
11609 
11610 	return true;
11611 }
11612 
11613 ////////////////////////////////////////////////////////////////////////////////////////////
11614 ////////////////////////////////////////////////////////////////////////////////////////////
11615 
ConstraintActivitiesPreferredTimeSlots()11616 ConstraintActivitiesPreferredTimeSlots::ConstraintActivitiesPreferredTimeSlots()
11617 	: TimeConstraint()
11618 {
11619 	this->type = CONSTRAINT_ACTIVITIES_PREFERRED_TIME_SLOTS;
11620 }
11621 
ConstraintActivitiesPreferredTimeSlots(double wp,const QString & te,const QString & st,const QString & su,const QString & sut,int dur,int nPT_L,QList<int> d_L,QList<int> h_L)11622 ConstraintActivitiesPreferredTimeSlots::ConstraintActivitiesPreferredTimeSlots(double wp, const QString& te,
11623 	const QString& st, const QString& su, const QString& sut, int dur, int nPT_L, QList<int> d_L, QList<int> h_L)
11624 	: TimeConstraint(wp)
11625 {
11626 	assert(dur==-1 || dur>=1);
11627 	duration=dur;
11628 
11629 	assert(d_L.count()==nPT_L);
11630 	assert(h_L.count()==nPT_L);
11631 
11632 	this->p_teacherName=te;
11633 	this->p_subjectName=su;
11634 	this->p_activityTagName=sut;
11635 	this->p_studentsName=st;
11636 	this->p_nPreferredTimeSlots_L=nPT_L;
11637 	this->p_days_L=d_L;
11638 	this->p_hours_L=h_L;
11639 	this->type=CONSTRAINT_ACTIVITIES_PREFERRED_TIME_SLOTS;
11640 }
11641 
computeInternalStructure(QWidget * parent,Rules & r)11642 bool ConstraintActivitiesPreferredTimeSlots::computeInternalStructure(QWidget* parent, Rules& r)
11643 {
11644 	this->p_nActivities=0;
11645 	this->p_activitiesIndices.clear();
11646 
11647 	int it;
11648 	Activity* act;
11649 	int i;
11650 	for(i=0; i<r.nInternalActivities; i++){
11651 		act=&r.internalActivitiesList[i];
11652 
11653 		//check if this activity has the corresponding teacher
11654 		if(this->p_teacherName!=""){
11655 			it = act->teachersNames.indexOf(this->p_teacherName);
11656 			if(it==-1)
11657 				continue;
11658 		}
11659 		//check if this activity has the corresponding students
11660 		if(this->p_studentsName!=""){
11661 			bool commonStudents=false;
11662 			for(const QString& st : qAsConst(act->studentsNames))
11663 				if(r.augmentedSetsShareStudentsFaster(st, p_studentsName)){
11664 					commonStudents=true;
11665 					break;
11666 				}
11667 
11668 			if(!commonStudents)
11669 				continue;
11670 		}
11671 		//check if this activity has the corresponding subject
11672 		if(this->p_subjectName!="" && act->subjectName!=this->p_subjectName){
11673 			continue;
11674 		}
11675 		//check if this activity has the corresponding activity tag
11676 		if(this->p_activityTagName!="" && !act->activityTagsNames.contains(this->p_activityTagName)){
11677 			continue;
11678 		}
11679 
11680 		if(duration>=1 && act->duration!=duration)
11681 			continue;
11682 
11683 		assert(this->p_nActivities < r.nInternalActivities);
11684 		this->p_nActivities++;
11685 		this->p_activitiesIndices.append(i);
11686 	}
11687 
11688 	assert(this->p_nActivities==this->p_activitiesIndices.count());
11689 
11690 	//////////////////////
11691 	for(int k=0; k<p_nPreferredTimeSlots_L; k++){
11692 		if(this->p_days_L[k] >= r.nDaysPerWeek){
11693 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
11694 			 tr("Constraint activities preferred time slots is wrong because it refers to removed day. Please correct"
11695 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
11696 
11697 			return false;
11698 		}
11699 		if(this->p_hours_L[k] == r.nHoursPerDay){
11700 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
11701 			 tr("Constraint activities preferred time slots is wrong because a preferred hour is too late (after the last acceptable slot). Please correct"
11702 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
11703 
11704 			return false;
11705 		}
11706 		if(this->p_hours_L[k] > r.nHoursPerDay){
11707 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
11708 			 tr("Constraint activities preferred time slots is wrong because it refers to removed hour. Please correct"
11709 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
11710 
11711 			return false;
11712 		}
11713 		if(this->p_hours_L[k]<0 || this->p_days_L[k]<0){
11714 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
11715 			 tr("Constraint activities preferred time slots is wrong because hour or day is not specified for a slot (-1). Please correct"
11716 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
11717 
11718 			return false;
11719 		}
11720 	}
11721 	///////////////////////
11722 
11723 	if(this->p_nActivities>0)
11724 		return true;
11725 	else{
11726 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
11727 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
11728 		return false;
11729 	}
11730 }
11731 
hasInactiveActivities(Rules & r)11732 bool ConstraintActivitiesPreferredTimeSlots::hasInactiveActivities(Rules& r)
11733 {
11734 	QList<int> localActiveActs;
11735 	QList<int> localAllActs;
11736 
11737 	//returns true if all activities are inactive
11738 	int it;
11739 	Activity* act;
11740 	int i;
11741 	for(i=0; i<r.activitiesList.count(); i++){
11742 		act=r.activitiesList.at(i);
11743 
11744 		//check if this activity has the corresponding teacher
11745 		if(this->p_teacherName!=""){
11746 			it = act->teachersNames.indexOf(this->p_teacherName);
11747 			if(it==-1)
11748 				continue;
11749 		}
11750 		//check if this activity has the corresponding students
11751 		if(this->p_studentsName!=""){
11752 			bool commonStudents=false;
11753 			for(const QString& st : qAsConst(act->studentsNames))
11754 				if(r.setsShareStudents(st, p_studentsName)){
11755 					commonStudents=true;
11756 					break;
11757 				}
11758 
11759 			if(!commonStudents)
11760 				continue;
11761 		}
11762 		//check if this activity has the corresponding subject
11763 		if(this->p_subjectName!="" && act->subjectName!=this->p_subjectName){
11764 				continue;
11765 		}
11766 		//check if this activity has the corresponding activity tag
11767 		if(this->p_activityTagName!="" && !act->activityTagsNames.contains(this->p_activityTagName)){
11768 				continue;
11769 		}
11770 
11771 		if(duration>=1 && act->duration!=duration)
11772 			continue;
11773 
11774 		if(!r.inactiveActivities.contains(act->id))
11775 			localActiveActs.append(act->id);
11776 
11777 		localAllActs.append(act->id);
11778 	}
11779 
11780 	if(localActiveActs.count()==0 && localAllActs.count()>0)
11781 	//because if this constraint does not refer to any activity,
11782 	//it should be reported as incorrect
11783 		return true;
11784 	else
11785 		return false;
11786 }
11787 
getXmlDescription(Rules & r)11788 QString ConstraintActivitiesPreferredTimeSlots::getXmlDescription(Rules& r)
11789 {
11790 	QString s="<ConstraintActivitiesPreferredTimeSlots>\n";
11791 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
11792 	s+="	<Teacher_Name>"+protect(this->p_teacherName)+"</Teacher_Name>\n";
11793 	s+="	<Students_Name>"+protect(this->p_studentsName)+"</Students_Name>\n";
11794 	s+="	<Subject_Name>"+protect(this->p_subjectName)+"</Subject_Name>\n";
11795 	s+="	<Activity_Tag_Name>"+protect(this->p_activityTagName)+"</Activity_Tag_Name>\n";
11796 	if(duration>=1)
11797 		s+="	<Duration>"+CustomFETString::number(duration)+"</Duration>\n";
11798 	else
11799 		s+="	<Duration></Duration>\n";
11800 	s+="	<Number_of_Preferred_Time_Slots>"+CustomFETString::number(this->p_nPreferredTimeSlots_L)+"</Number_of_Preferred_Time_Slots>\n";
11801 	for(int i=0; i<p_nPreferredTimeSlots_L; i++){
11802 		s+="	<Preferred_Time_Slot>\n";
11803 		if(this->p_days_L[i]>=0)
11804 			s+="		<Preferred_Day>"+protect(r.daysOfTheWeek[this->p_days_L[i]])+"</Preferred_Day>\n";
11805 		if(this->p_hours_L[i]>=0)
11806 			s+="		<Preferred_Hour>"+protect(r.hoursOfTheDay[this->p_hours_L[i]])+"</Preferred_Hour>\n";
11807 		s+="	</Preferred_Time_Slot>\n";
11808 	}
11809 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
11810 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
11811 	s+="</ConstraintActivitiesPreferredTimeSlots>\n";
11812 	return s;
11813 }
11814 
getDescription(Rules & r)11815 QString ConstraintActivitiesPreferredTimeSlots::getDescription(Rules& r)
11816 {
11817 	QString begin=QString("");
11818 	if(!active)
11819 		begin="X - ";
11820 
11821 	QString end=QString("");
11822 	if(!comments.isEmpty())
11823 		end=", "+tr("C: %1", "Comments").arg(comments);
11824 
11825 	QString s;
11826 
11827 	QString tc, st, su, at, dur;
11828 
11829 	if(this->p_teacherName!="")
11830 		tc=tr("teacher=%1").arg(this->p_teacherName);
11831 	else
11832 		tc=tr("all teachers");
11833 
11834 	if(this->p_studentsName!="")
11835 		st=tr("students=%1").arg(this->p_studentsName);
11836 	else
11837 		st=tr("all students");
11838 
11839 	if(this->p_subjectName!="")
11840 		su=tr("subject=%1").arg(this->p_subjectName);
11841 	else
11842 		su=tr("all subjects");
11843 
11844 	if(this->p_activityTagName!="")
11845 		at=tr("activity tag=%1").arg(this->p_activityTagName);
11846 	else
11847 		at=tr("all activity tags");
11848 
11849 	if(duration>=1)
11850 		dur=tr("duration=%1").arg(duration);
11851 	else
11852 		dur=tr("all durations");
11853 
11854 	s+=tr("Activities with %1, %2, %3, %4, %5, have a set of preferred time slots:", "%1...%5 are conditions for the activities").arg(tc).arg(st).arg(su).arg(at).arg(dur);
11855 	s+=" ";
11856 	for(int i=0; i<this->p_nPreferredTimeSlots_L; i++){
11857 		if(this->p_days_L[i]>=0){
11858 			s+=r.daysOfTheWeek[this->p_days_L[i]];
11859 			s+=" ";
11860 		}
11861 		if(this->p_hours_L[i]>=0){
11862 			s+=r.hoursOfTheDay[this->p_hours_L[i]];
11863 		}
11864 		if(i<this->p_nPreferredTimeSlots_L-1)
11865 			s+="; ";
11866 	}
11867 	s+=", ";
11868 
11869 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
11870 
11871 	return begin+s+end;
11872 }
11873 
getDetailedDescription(Rules & r)11874 QString ConstraintActivitiesPreferredTimeSlots::getDetailedDescription(Rules& r)
11875 {
11876 	QString s=tr("Time constraint");s+="\n";
11877 	s+=tr("Activities with:");s+="\n";
11878 
11879 	if(this->p_teacherName!="")
11880 		s+=tr("Teacher=%1").arg(this->p_teacherName);
11881 	else
11882 		s+=tr("All teachers");
11883 	s+="\n";
11884 	if(this->p_studentsName!="")
11885 		s+=tr("Students=%1").arg(this->p_studentsName);
11886 	else
11887 		s+=tr("All students");
11888 	s+="\n";
11889 	if(this->p_subjectName!="")
11890 		s+=tr("Subject=%1").arg(this->p_subjectName);
11891 	else
11892 		s+=tr("All subjects");
11893 	s+="\n";
11894 	if(this->p_activityTagName!="")
11895 		s+=tr("Activity tag=%1").arg(this->p_activityTagName);
11896 	else
11897 		s+=tr("All activity tags");
11898 	s+="\n";
11899 
11900 	if(duration>=1)
11901 		s+=tr("Duration=%1").arg(duration);
11902 	else
11903 		s+=tr("All durations");
11904 	s+="\n";
11905 
11906 	s+=tr("have a set of preferred time slots (all hours of each affected activity must be in the allowed slots):");
11907 	s+="\n";
11908 	for(int i=0; i<this->p_nPreferredTimeSlots_L; i++){
11909 		if(this->p_days_L[i]>=0){
11910 			s+=r.daysOfTheWeek[this->p_days_L[i]];
11911 			s+=" ";
11912 		}
11913 		if(this->p_hours_L[i]>=0){
11914 			s+=r.hoursOfTheDay[this->p_hours_L[i]];
11915 		}
11916 		if(i<this->p_nPreferredTimeSlots_L-1)
11917 			s+=";  ";
11918 	}
11919 	s+="\n";
11920 
11921 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
11922 
11923 	if(!active){
11924 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
11925 		s+="\n";
11926 	}
11927 	if(!comments.isEmpty()){
11928 		s+=tr("Comments=%1").arg(comments);
11929 		s+="\n";
11930 	}
11931 
11932 	return s;
11933 }
11934 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)11935 double ConstraintActivitiesPreferredTimeSlots::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
11936 {
11937 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
11938 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
11939 		c.teachersMatrixReady=true;
11940 		c.subgroupsMatrixReady=true;
11941 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
11942 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
11943 
11944 		c.changedForMatrixCalculation=false;
11945 	}
11946 
11947 	int nbroken;
11948 
11949 	assert(r.internalStructureComputed);
11950 
11951 ///////////////////
11952 	Matrix2D<bool> allowed;
11953 	allowed.resize(r.nDaysPerWeek, r.nHoursPerDay);
11954 	for(int d=0; d<r.nDaysPerWeek; d++)
11955 		for(int h=0; h<r.nHoursPerDay; h++)
11956 			allowed[d][h]=false;
11957 	for(int i=0; i<this->p_nPreferredTimeSlots_L; i++){
11958 		if(this->p_days_L[i]>=0 && this->p_hours_L[i]>=0)
11959 			allowed[this->p_days_L[i]][this->p_hours_L[i]]=true;
11960 		else
11961 			assert(0);
11962 	}
11963 ////////////////////
11964 
11965 	nbroken=0;
11966 	int tmp;
11967 
11968 	for(int i=0; i<this->p_nActivities; i++){
11969 		tmp=0;
11970 		int ai=this->p_activitiesIndices[i];
11971 		if(c.times[ai]!=UNALLOCATED_TIME){
11972 			int d=c.times[ai]%r.nDaysPerWeek; //the day when this activity was scheduled
11973 			int h=c.times[ai]/r.nDaysPerWeek; //the hour
11974 
11975 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++)
11976 				if(!allowed[d][h+dur])
11977 					tmp++;
11978 		}
11979 		nbroken+=tmp;
11980 		if(conflictsString!=nullptr && tmp>0){
11981 			QString s=tr("Time constraint activities preferred time slots broken"
11982 			 " for activity with id=%1 (%2) on %3 hours,"
11983 			 " increases conflicts total by %4", "%1 is the id, %2 is the detailed description of the activity.")
11984 			 .arg(r.internalActivitiesList[ai].id)
11985 			 .arg(getActivityDetailedDescription(r, r.internalActivitiesList[ai].id))
11986 			 .arg(tmp)
11987 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*tmp));
11988 
11989 			dl.append(s);
11990 			cl.append(weightPercentage/100*tmp);
11991 
11992 			*conflictsString+= s+"\n";
11993 		}
11994 	}
11995 
11996 	if(weightPercentage==100)
11997 		assert(nbroken==0);
11998 	return nbroken * weightPercentage / 100.0;
11999 }
12000 
isRelatedToActivity(Rules & r,Activity * a)12001 bool ConstraintActivitiesPreferredTimeSlots::isRelatedToActivity(Rules& r, Activity* a)
12002 {
12003 	int it;
12004 
12005 	//check if this activity has the corresponding teacher
12006 	if(this->p_teacherName!=""){
12007 		it = a->teachersNames.indexOf(this->p_teacherName);
12008 		if(it==-1)
12009 			return false;
12010 	}
12011 	//check if this activity has the corresponding students
12012 	if(this->p_studentsName!=""){
12013 		bool commonStudents=false;
12014 		for(const QString& st : qAsConst(a->studentsNames)){
12015 			if(r.setsShareStudents(st, this->p_studentsName)){
12016 				commonStudents=true;
12017 				break;
12018 			}
12019 		}
12020 		if(!commonStudents)
12021 			return false;
12022 	}
12023 	//check if this activity has the corresponding subject
12024 	if(this->p_subjectName!="" && a->subjectName!=this->p_subjectName)
12025 		return false;
12026 	//check if this activity has the corresponding activity tag
12027 	if(this->p_activityTagName!="" && !a->activityTagsNames.contains(this->p_activityTagName))
12028 		return false;
12029 
12030 	if(duration>=1 && a->duration!=duration)
12031 		return false;
12032 
12033 	return true;
12034 }
12035 
isRelatedToTeacher(Teacher * t)12036 bool ConstraintActivitiesPreferredTimeSlots::isRelatedToTeacher(Teacher* t)
12037 {
12038 	Q_UNUSED(t);
12039 
12040 	return false;
12041 }
12042 
isRelatedToSubject(Subject * s)12043 bool ConstraintActivitiesPreferredTimeSlots::isRelatedToSubject(Subject* s)
12044 {
12045 	Q_UNUSED(s);
12046 
12047 	return false;
12048 }
12049 
isRelatedToActivityTag(ActivityTag * s)12050 bool ConstraintActivitiesPreferredTimeSlots::isRelatedToActivityTag(ActivityTag* s)
12051 {
12052 	Q_UNUSED(s);
12053 
12054 	return false;
12055 }
12056 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)12057 bool ConstraintActivitiesPreferredTimeSlots::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
12058 {
12059 	Q_UNUSED(r);
12060 	Q_UNUSED(s);
12061 
12062 	return false;
12063 }
12064 
hasWrongDayOrHour(Rules & r)12065 bool ConstraintActivitiesPreferredTimeSlots::hasWrongDayOrHour(Rules& r)
12066 {
12067 	assert(p_nPreferredTimeSlots_L==p_days_L.count());
12068 	assert(p_nPreferredTimeSlots_L==p_hours_L.count());
12069 
12070 	for(int i=0; i<p_nPreferredTimeSlots_L; i++)
12071 		if(p_days_L.at(i)<0 || p_days_L.at(i)>=r.nDaysPerWeek
12072 		 || p_hours_L.at(i)<0 || p_hours_L.at(i)>=r.nHoursPerDay)
12073 			return true;
12074 
12075 	return false;
12076 }
12077 
canRepairWrongDayOrHour(Rules & r)12078 bool ConstraintActivitiesPreferredTimeSlots::canRepairWrongDayOrHour(Rules& r)
12079 {
12080 	assert(hasWrongDayOrHour(r));
12081 
12082 	return true;
12083 }
12084 
repairWrongDayOrHour(Rules & r)12085 bool ConstraintActivitiesPreferredTimeSlots::repairWrongDayOrHour(Rules& r)
12086 {
12087 	assert(hasWrongDayOrHour(r));
12088 
12089 	assert(p_nPreferredTimeSlots_L==p_days_L.count());
12090 	assert(p_nPreferredTimeSlots_L==p_hours_L.count());
12091 
12092 	QList<int> newDays;
12093 	QList<int> newHours;
12094 	int newNPref=0;
12095 
12096 	for(int i=0; i<p_nPreferredTimeSlots_L; i++)
12097 		if(p_days_L.at(i)>=0 && p_days_L.at(i)<r.nDaysPerWeek
12098 		 && p_hours_L.at(i)>=0 && p_hours_L.at(i)<r.nHoursPerDay){
12099 			newDays.append(p_days_L.at(i));
12100 			newHours.append(p_hours_L.at(i));
12101 			newNPref++;
12102 		}
12103 
12104 	p_nPreferredTimeSlots_L=newNPref;
12105 	p_days_L=newDays;
12106 	p_hours_L=newHours;
12107 
12108 	r.internalStructureComputed=false;
12109 	setRulesModifiedAndOtherThings(&r);
12110 
12111 	return true;
12112 }
12113 
12114 ////////////////////////////////////////////////////////////////////////////////////////////
12115 ////////////////////////////////////////////////////////////////////////////////////////////
12116 
ConstraintSubactivitiesPreferredTimeSlots()12117 ConstraintSubactivitiesPreferredTimeSlots::ConstraintSubactivitiesPreferredTimeSlots()
12118 	: TimeConstraint()
12119 {
12120 	this->type = CONSTRAINT_SUBACTIVITIES_PREFERRED_TIME_SLOTS;
12121 }
12122 
ConstraintSubactivitiesPreferredTimeSlots(double wp,int compNo,const QString & te,const QString & st,const QString & su,const QString & sut,int dur,int nPT_L,QList<int> d_L,QList<int> h_L)12123 ConstraintSubactivitiesPreferredTimeSlots::ConstraintSubactivitiesPreferredTimeSlots(double wp, int compNo, const QString& te,
12124 	const QString& st, const QString& su, const QString& sut, int dur, int nPT_L, QList<int> d_L, QList<int> h_L)
12125 	: TimeConstraint(wp)
12126 {
12127 	assert(dur==-1 || dur>=1);
12128 	duration=dur;
12129 
12130 	assert(d_L.count()==nPT_L);
12131 	assert(h_L.count()==nPT_L);
12132 
12133 	this->componentNumber=compNo;
12134 	this->p_teacherName=te;
12135 	this->p_subjectName=su;
12136 	this->p_activityTagName=sut;
12137 	this->p_studentsName=st;
12138 	this->p_nPreferredTimeSlots_L=nPT_L;
12139 	this->p_days_L=d_L;
12140 	this->p_hours_L=h_L;
12141 	this->type=CONSTRAINT_SUBACTIVITIES_PREFERRED_TIME_SLOTS;
12142 }
12143 
computeInternalStructure(QWidget * parent,Rules & r)12144 bool ConstraintSubactivitiesPreferredTimeSlots::computeInternalStructure(QWidget* parent, Rules& r)
12145 {
12146 	this->p_nActivities=0;
12147 	this->p_activitiesIndices.clear();
12148 
12149 	int it;
12150 	Activity* act;
12151 	int i;
12152 	for(i=0; i<r.nInternalActivities; i++){
12153 		act=&r.internalActivitiesList[i];
12154 
12155 		if(!act->representsComponentNumber(this->componentNumber))
12156 			continue;
12157 
12158 		//check if this activity has the corresponding teacher
12159 		if(this->p_teacherName!=""){
12160 			it = act->teachersNames.indexOf(this->p_teacherName);
12161 			if(it==-1)
12162 				continue;
12163 		}
12164 		//check if this activity has the corresponding students
12165 		if(this->p_studentsName!=""){
12166 			bool commonStudents=false;
12167 			for(const QString& st : qAsConst(act->studentsNames))
12168 				if(r.augmentedSetsShareStudentsFaster(st, p_studentsName)){
12169 					commonStudents=true;
12170 					break;
12171 				}
12172 
12173 			if(!commonStudents)
12174 				continue;
12175 		}
12176 		//check if this activity has the corresponding subject
12177 		if(this->p_subjectName!="" && act->subjectName!=this->p_subjectName){
12178 			continue;
12179 		}
12180 		//check if this activity has the corresponding activity tag
12181 		if(this->p_activityTagName!="" && !act->activityTagsNames.contains(this->p_activityTagName)){
12182 			continue;
12183 		}
12184 
12185 		if(duration>=1 && act->duration!=duration)
12186 			continue;
12187 
12188 		assert(this->p_nActivities < r.nInternalActivities);
12189 		this->p_nActivities++;
12190 		this->p_activitiesIndices.append(i);
12191 	}
12192 
12193 	assert(this->p_nActivities==this->p_activitiesIndices.count());
12194 
12195 	//////////////////////
12196 	for(int k=0; k<p_nPreferredTimeSlots_L; k++){
12197 		if(this->p_days_L[k] >= r.nDaysPerWeek){
12198 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
12199 			 tr("Constraint subactivities preferred time slots is wrong because it refers to removed day. Please correct"
12200 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
12201 
12202 			return false;
12203 		}
12204 		if(this->p_hours_L[k] == r.nHoursPerDay){
12205 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
12206 			 tr("Constraint subactivities preferred time slots is wrong because a preferred hour is too late (after the last acceptable slot). Please correct"
12207 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
12208 
12209 			return false;
12210 		}
12211 		if(this->p_hours_L[k] > r.nHoursPerDay){
12212 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
12213 			 tr("Constraint subactivities preferred time slots is wrong because it refers to removed hour. Please correct"
12214 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
12215 
12216 			return false;
12217 		}
12218 		if(this->p_hours_L[k]<0 || this->p_days_L[k]<0){
12219 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
12220 			 tr("Constraint subactivities preferred time slots is wrong because hour or day is not specified for a slot (-1). Please correct"
12221 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
12222 
12223 			return false;
12224 		}
12225 	}
12226 	///////////////////////
12227 
12228 	if(this->p_nActivities>0)
12229 		return true;
12230 	else{
12231 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
12232 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
12233 		return false;
12234 	}
12235 }
12236 
hasInactiveActivities(Rules & r)12237 bool ConstraintSubactivitiesPreferredTimeSlots::hasInactiveActivities(Rules& r)
12238 {
12239 	QList<int> localActiveActs;
12240 	QList<int> localAllActs;
12241 
12242 	//returns true if all activities are inactive
12243 	int it;
12244 	Activity* act;
12245 	int i;
12246 	for(i=0; i<r.activitiesList.count(); i++){
12247 		act=r.activitiesList.at(i);
12248 
12249 		if(!act->representsComponentNumber(this->componentNumber))
12250 			continue;
12251 
12252 		//check if this activity has the corresponding teacher
12253 		if(this->p_teacherName!=""){
12254 			it = act->teachersNames.indexOf(this->p_teacherName);
12255 			if(it==-1)
12256 				continue;
12257 		}
12258 		//check if this activity has the corresponding students
12259 		if(this->p_studentsName!=""){
12260 			bool commonStudents=false;
12261 			for(const QString& st : qAsConst(act->studentsNames))
12262 				if(r.setsShareStudents(st, p_studentsName)){
12263 					commonStudents=true;
12264 					break;
12265 				}
12266 
12267 			if(!commonStudents)
12268 				continue;
12269 		}
12270 		//check if this activity has the corresponding subject
12271 		if(this->p_subjectName!="" && act->subjectName!=this->p_subjectName){
12272 				continue;
12273 		}
12274 		//check if this activity has the corresponding activity tag
12275 		if(this->p_activityTagName!="" && !act->activityTagsNames.contains(this->p_activityTagName)){
12276 				continue;
12277 		}
12278 
12279 		if(duration>=1 && act->duration!=duration)
12280 			continue;
12281 
12282 		if(!r.inactiveActivities.contains(act->id))
12283 			localActiveActs.append(act->id);
12284 
12285 		localAllActs.append(act->id);
12286 	}
12287 
12288 	if(localActiveActs.count()==0 && localAllActs.count()>0)
12289 		return true;
12290 	else
12291 		return false;
12292 }
12293 
getXmlDescription(Rules & r)12294 QString ConstraintSubactivitiesPreferredTimeSlots::getXmlDescription(Rules& r)
12295 {
12296 	QString s="<ConstraintSubactivitiesPreferredTimeSlots>\n";
12297 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
12298 	s+="	<Component_Number>"+CustomFETString::number(this->componentNumber)+"</Component_Number>\n";
12299 	s+="	<Teacher_Name>"+protect(this->p_teacherName)+"</Teacher_Name>\n";
12300 	s+="	<Students_Name>"+protect(this->p_studentsName)+"</Students_Name>\n";
12301 	s+="	<Subject_Name>"+protect(this->p_subjectName)+"</Subject_Name>\n";
12302 	s+="	<Activity_Tag_Name>"+protect(this->p_activityTagName)+"</Activity_Tag_Name>\n";
12303 	if(duration>=1)
12304 		s+="	<Duration>"+CustomFETString::number(duration)+"</Duration>\n";
12305 	else
12306 		s+="	<Duration></Duration>\n";
12307 	s+="	<Number_of_Preferred_Time_Slots>"+CustomFETString::number(this->p_nPreferredTimeSlots_L)+"</Number_of_Preferred_Time_Slots>\n";
12308 	for(int i=0; i<p_nPreferredTimeSlots_L; i++){
12309 		s+="	<Preferred_Time_Slot>\n";
12310 		if(this->p_days_L[i]>=0)
12311 			s+="		<Preferred_Day>"+protect(r.daysOfTheWeek[this->p_days_L[i]])+"</Preferred_Day>\n";
12312 		if(this->p_hours_L[i]>=0)
12313 			s+="		<Preferred_Hour>"+protect(r.hoursOfTheDay[this->p_hours_L[i]])+"</Preferred_Hour>\n";
12314 		s+="	</Preferred_Time_Slot>\n";
12315 	}
12316 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
12317 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
12318 	s+="</ConstraintSubactivitiesPreferredTimeSlots>\n";
12319 	return s;
12320 }
12321 
getDescription(Rules & r)12322 QString ConstraintSubactivitiesPreferredTimeSlots::getDescription(Rules& r)
12323 {
12324 	QString begin=QString("");
12325 	if(!active)
12326 		begin="X - ";
12327 
12328 	QString end=QString("");
12329 	if(!comments.isEmpty())
12330 		end=", "+tr("C: %1", "Comments").arg(comments);
12331 
12332 	QString s;
12333 
12334 	QString tc, st, su, at, dur;
12335 
12336 	if(this->p_teacherName!="")
12337 		tc=tr("teacher=%1").arg(this->p_teacherName);
12338 	else
12339 		tc=tr("all teachers");
12340 
12341 	if(this->p_studentsName!="")
12342 		st=tr("students=%1").arg(this->p_studentsName);
12343 	else
12344 		st=tr("all students");
12345 
12346 	if(this->p_subjectName!="")
12347 		su=tr("subject=%1").arg(this->p_subjectName);
12348 	else
12349 		su=tr("all subjects");
12350 
12351 	if(this->p_activityTagName!="")
12352 		at=tr("activity tag=%1").arg(this->p_activityTagName);
12353 	else
12354 		at=tr("all activity tags");
12355 
12356 	if(duration>=1)
12357 		dur=tr("duration=%1").arg(duration);
12358 	else
12359 		dur=tr("all durations");
12360 
12361 	s+=tr("Subactivities with %1, %2, %3, %4, %5, %6, have a set of preferred time slots:", "%1...%6 are conditions for the subactivities")
12362 		.arg(tr("component number=%1").arg(this->componentNumber)).arg(tc).arg(st).arg(su).arg(at).arg(dur);
12363 
12364 	s+=" ";
12365 
12366 	for(int i=0; i<this->p_nPreferredTimeSlots_L; i++){
12367 		if(this->p_days_L[i]>=0){
12368 			s+=r.daysOfTheWeek[this->p_days_L[i]];
12369 			s+=" ";
12370 		}
12371 		if(this->p_hours_L[i]>=0){
12372 			s+=r.hoursOfTheDay[this->p_hours_L[i]];
12373 		}
12374 		if(i<this->p_nPreferredTimeSlots_L-1)
12375 			s+="; ";
12376 	}
12377 	s+=", ";
12378 
12379 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
12380 
12381 	return begin+s+end;
12382 }
12383 
getDetailedDescription(Rules & r)12384 QString ConstraintSubactivitiesPreferredTimeSlots::getDetailedDescription(Rules& r)
12385 {
12386 	QString s=tr("Time constraint");s+="\n";
12387 	s+=tr("Subactivities with:");s+="\n";
12388 
12389 	s+=tr("Component number=%1").arg(this->componentNumber);
12390 	s+="\n";
12391 
12392 	if(this->p_teacherName!="")
12393 		s+=tr("Teacher=%1").arg(this->p_teacherName);
12394 	else
12395 		s+=tr("All teachers");
12396 	s+="\n";
12397 
12398 	if(this->p_studentsName!="")
12399 		s+=tr("Students=%1").arg(this->p_studentsName);
12400 	else
12401 		s+=tr("All students");
12402 	s+="\n";
12403 
12404 	if(this->p_subjectName!="")
12405 		s+=tr("Subject=%1").arg(this->p_subjectName);
12406 	else
12407 		s+=tr("All subjects");
12408 	s+="\n";
12409 
12410 	if(this->p_activityTagName!="")
12411 		s+=tr("Activity tag=%1").arg(this->p_activityTagName);
12412 	else
12413 		s+=tr("All activity tags");
12414 	s+="\n";
12415 
12416 	if(duration>=1)
12417 		s+=tr("Duration=%1").arg(duration);
12418 	else
12419 		s+=tr("All durations");
12420 	s+="\n";
12421 
12422 	s+=tr("have a set of preferred time slots (all hours of each affected subactivity must be in the allowed slots):");
12423 	s+="\n";
12424 	for(int i=0; i<this->p_nPreferredTimeSlots_L; i++){
12425 		if(this->p_days_L[i]>=0){
12426 			s+=r.daysOfTheWeek[this->p_days_L[i]];
12427 			s+=" ";
12428 		}
12429 		if(this->p_hours_L[i]>=0){
12430 			s+=r.hoursOfTheDay[this->p_hours_L[i]];
12431 		}
12432 		if(i<this->p_nPreferredTimeSlots_L-1)
12433 			s+=";  ";
12434 	}
12435 	s+="\n";
12436 
12437 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
12438 
12439 	if(!active){
12440 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
12441 		s+="\n";
12442 	}
12443 	if(!comments.isEmpty()){
12444 		s+=tr("Comments=%1").arg(comments);
12445 		s+="\n";
12446 	}
12447 
12448 	return s;
12449 }
12450 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)12451 double ConstraintSubactivitiesPreferredTimeSlots::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
12452 {
12453 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
12454 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
12455 		c.teachersMatrixReady=true;
12456 		c.subgroupsMatrixReady=true;
12457 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
12458 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
12459 
12460 		c.changedForMatrixCalculation=false;
12461 	}
12462 
12463 	int nbroken;
12464 
12465 	assert(r.internalStructureComputed);
12466 
12467 ///////////////////
12468 	Matrix2D<bool> allowed;
12469 	allowed.resize(r.nDaysPerWeek, r.nHoursPerDay);
12470 	for(int d=0; d<r.nDaysPerWeek; d++)
12471 		for(int h=0; h<r.nHoursPerDay; h++)
12472 			allowed[d][h]=false;
12473 	for(int i=0; i<this->p_nPreferredTimeSlots_L; i++){
12474 		if(this->p_days_L[i]>=0 && this->p_hours_L[i]>=0)
12475 			allowed[this->p_days_L[i]][this->p_hours_L[i]]=true;
12476 		else
12477 			assert(0);
12478 	}
12479 ////////////////////
12480 
12481 	nbroken=0;
12482 	int tmp;
12483 
12484 	for(int i=0; i<this->p_nActivities; i++){
12485 		tmp=0;
12486 		int ai=this->p_activitiesIndices[i];
12487 		if(c.times[ai]!=UNALLOCATED_TIME){
12488 			int d=c.times[ai]%r.nDaysPerWeek; //the day when this activity was scheduled
12489 			int h=c.times[ai]/r.nDaysPerWeek; //the hour
12490 
12491 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++)
12492 				if(!allowed[d][h+dur])
12493 					tmp++;
12494 		}
12495 		nbroken+=tmp;
12496 		if(conflictsString!=nullptr && tmp>0){
12497 			QString s=tr("Time constraint subactivities preferred time slots broken"
12498 			 " for activity with id=%1 (%2), component number %3, on %4 hours,"
12499 			 " increases conflicts total by %5", "%1 is the id, %2 is the detailed description of the activity.")
12500 			 .arg(r.internalActivitiesList[ai].id)
12501 			 .arg(getActivityDetailedDescription(r, r.internalActivitiesList[ai].id))
12502 			 .arg(componentNumber)
12503 			 .arg(tmp)
12504 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*tmp));
12505 
12506 			dl.append(s);
12507 			cl.append(weightPercentage/100*tmp);
12508 
12509 			*conflictsString+= s+"\n";
12510 		}
12511 	}
12512 
12513 	if(weightPercentage==100)
12514 		assert(nbroken==0);
12515 	return nbroken * weightPercentage / 100.0;
12516 }
12517 
isRelatedToActivity(Rules & r,Activity * a)12518 bool ConstraintSubactivitiesPreferredTimeSlots::isRelatedToActivity(Rules& r, Activity* a)
12519 {
12520 	if(!a->representsComponentNumber(this->componentNumber))
12521 		return false;
12522 
12523 	int it;
12524 
12525 	//check if this activity has the corresponding teacher
12526 	if(this->p_teacherName!=""){
12527 		it = a->teachersNames.indexOf(this->p_teacherName);
12528 		if(it==-1)
12529 			return false;
12530 	}
12531 	//check if this activity has the corresponding students
12532 	if(this->p_studentsName!=""){
12533 		bool commonStudents=false;
12534 		for(const QString& st : qAsConst(a->studentsNames)){
12535 			if(r.setsShareStudents(st, this->p_studentsName)){
12536 				commonStudents=true;
12537 				break;
12538 			}
12539 		}
12540 		if(!commonStudents)
12541 			return false;
12542 	}
12543 	//check if this activity has the corresponding subject
12544 	if(this->p_subjectName!="" && a->subjectName!=this->p_subjectName)
12545 		return false;
12546 	//check if this activity has the corresponding activity tag
12547 	if(this->p_activityTagName!="" && !a->activityTagsNames.contains(this->p_activityTagName))
12548 		return false;
12549 
12550 	if(duration>=1 && a->duration!=duration)
12551 		return false;
12552 
12553 	return true;
12554 }
12555 
isRelatedToTeacher(Teacher * t)12556 bool ConstraintSubactivitiesPreferredTimeSlots::isRelatedToTeacher(Teacher* t)
12557 {
12558 	Q_UNUSED(t);
12559 
12560 	return false;
12561 }
12562 
isRelatedToSubject(Subject * s)12563 bool ConstraintSubactivitiesPreferredTimeSlots::isRelatedToSubject(Subject* s)
12564 {
12565 	Q_UNUSED(s);
12566 
12567 	return false;
12568 }
12569 
isRelatedToActivityTag(ActivityTag * s)12570 bool ConstraintSubactivitiesPreferredTimeSlots::isRelatedToActivityTag(ActivityTag* s)
12571 {
12572 	Q_UNUSED(s);
12573 
12574 	return false;
12575 }
12576 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)12577 bool ConstraintSubactivitiesPreferredTimeSlots::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
12578 {
12579 	Q_UNUSED(r);
12580 	Q_UNUSED(s);
12581 
12582 	return false;
12583 }
12584 
hasWrongDayOrHour(Rules & r)12585 bool ConstraintSubactivitiesPreferredTimeSlots::hasWrongDayOrHour(Rules& r)
12586 {
12587 	assert(p_nPreferredTimeSlots_L==p_days_L.count());
12588 	assert(p_nPreferredTimeSlots_L==p_hours_L.count());
12589 
12590 	for(int i=0; i<p_nPreferredTimeSlots_L; i++)
12591 		if(p_days_L.at(i)<0 || p_days_L.at(i)>=r.nDaysPerWeek
12592 		 || p_hours_L.at(i)<0 || p_hours_L.at(i)>=r.nHoursPerDay)
12593 			return true;
12594 
12595 	return false;
12596 }
12597 
canRepairWrongDayOrHour(Rules & r)12598 bool ConstraintSubactivitiesPreferredTimeSlots::canRepairWrongDayOrHour(Rules& r)
12599 {
12600 	assert(hasWrongDayOrHour(r));
12601 
12602 	return true;
12603 }
12604 
repairWrongDayOrHour(Rules & r)12605 bool ConstraintSubactivitiesPreferredTimeSlots::repairWrongDayOrHour(Rules& r)
12606 {
12607 	assert(hasWrongDayOrHour(r));
12608 
12609 	assert(p_nPreferredTimeSlots_L==p_days_L.count());
12610 	assert(p_nPreferredTimeSlots_L==p_hours_L.count());
12611 
12612 	QList<int> newDays;
12613 	QList<int> newHours;
12614 	int newNPref=0;
12615 
12616 	for(int i=0; i<p_nPreferredTimeSlots_L; i++)
12617 		if(p_days_L.at(i)>=0 && p_days_L.at(i)<r.nDaysPerWeek
12618 		 && p_hours_L.at(i)>=0 && p_hours_L.at(i)<r.nHoursPerDay){
12619 			newDays.append(p_days_L.at(i));
12620 			newHours.append(p_hours_L.at(i));
12621 			newNPref++;
12622 		}
12623 
12624 	p_nPreferredTimeSlots_L=newNPref;
12625 	p_days_L=newDays;
12626 	p_hours_L=newHours;
12627 
12628 	r.internalStructureComputed=false;
12629 	setRulesModifiedAndOtherThings(&r);
12630 
12631 	return true;
12632 }
12633 
12634 ////////////////////////////////////////////////////////////////////////////////////////////
12635 ////////////////////////////////////////////////////////////////////////////////////////////
12636 
ConstraintActivityPreferredStartingTimes()12637 ConstraintActivityPreferredStartingTimes::ConstraintActivityPreferredStartingTimes()
12638 	: TimeConstraint()
12639 {
12640 	this->type = CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIMES;
12641 }
12642 
ConstraintActivityPreferredStartingTimes(double wp,int actId,int nPT_L,QList<int> d_L,QList<int> h_L)12643 ConstraintActivityPreferredStartingTimes::ConstraintActivityPreferredStartingTimes(double wp, int actId, int nPT_L, QList<int> d_L, QList<int> h_L)
12644 	: TimeConstraint(wp)
12645 {
12646 	assert(d_L.count()==nPT_L);
12647 	assert(h_L.count()==nPT_L);
12648 
12649 	this->activityId=actId;
12650 	this->nPreferredStartingTimes_L=nPT_L;
12651 	this->days_L=d_L;
12652 	this->hours_L=h_L;
12653 	this->type=CONSTRAINT_ACTIVITY_PREFERRED_STARTING_TIMES;
12654 }
12655 
computeInternalStructure(QWidget * parent,Rules & r)12656 bool ConstraintActivityPreferredStartingTimes::computeInternalStructure(QWidget* parent, Rules& r)
12657 {
12658 	/*Activity* act;
12659 	int i;
12660 	for(i=0; i<r.nInternalActivities; i++){
12661 		act=&r.internalActivitiesList[i];
12662 		if(act->id==this->activityId)
12663 			break;
12664 	}*/
12665 
12666 	int i=r.activitiesHash.value(activityId, r.nInternalActivities);
12667 
12668 	if(i==r.nInternalActivities){
12669 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
12670 			tr("Following constraint is wrong (because it refers to invalid activity id). Please correct it (maybe removing it is a solution):\n%1").arg(this->getDetailedDescription(r)));
12671 		return false;
12672 	}
12673 
12674 	for(int k=0; k<nPreferredStartingTimes_L; k++){
12675 		if(this->days_L[k] >= r.nDaysPerWeek){
12676 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
12677 			 tr("Constraint activity preferred starting times is wrong because it refers to removed day. Please correct"
12678 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
12679 
12680 			return false;
12681 		}
12682 		if(this->hours_L[k] == r.nHoursPerDay){
12683 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
12684 			 tr("Constraint activity preferred starting times is wrong because a preferred hour is too late (after the last acceptable slot). Please correct"
12685 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
12686 
12687 			return false;
12688 		}
12689 		if(this->hours_L[k] > r.nHoursPerDay){
12690 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
12691 			 tr("Constraint activity preferred starting times is wrong because it refers to removed hour. Please correct"
12692 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
12693 
12694 			return false;
12695 		}
12696 	}
12697 
12698 	this->activityIndex=i;
12699 	return true;
12700 }
12701 
hasInactiveActivities(Rules & r)12702 bool ConstraintActivityPreferredStartingTimes::hasInactiveActivities(Rules& r)
12703 {
12704 	if(r.inactiveActivities.contains(this->activityId))
12705 		return true;
12706 	return false;
12707 }
12708 
getXmlDescription(Rules & r)12709 QString ConstraintActivityPreferredStartingTimes::getXmlDescription(Rules& r)
12710 {
12711 	QString s="<ConstraintActivityPreferredStartingTimes>\n";
12712 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
12713 	s+="	<Activity_Id>"+CustomFETString::number(this->activityId)+"</Activity_Id>\n";
12714 	s+="	<Number_of_Preferred_Starting_Times>"+CustomFETString::number(this->nPreferredStartingTimes_L)+"</Number_of_Preferred_Starting_Times>\n";
12715 	for(int i=0; i<nPreferredStartingTimes_L; i++){
12716 		s+="	<Preferred_Starting_Time>\n";
12717 		if(this->days_L[i]>=0)
12718 			s+="		<Preferred_Starting_Day>"+protect(r.daysOfTheWeek[this->days_L[i]])+"</Preferred_Starting_Day>\n";
12719 		if(this->hours_L[i]>=0)
12720 			s+="		<Preferred_Starting_Hour>"+protect(r.hoursOfTheDay[this->hours_L[i]])+"</Preferred_Starting_Hour>\n";
12721 		s+="	</Preferred_Starting_Time>\n";
12722 	}
12723 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
12724 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
12725 	s+="</ConstraintActivityPreferredStartingTimes>\n";
12726 	return s;
12727 }
12728 
getDescription(Rules & r)12729 QString ConstraintActivityPreferredStartingTimes::getDescription(Rules& r)
12730 {
12731 	QString begin=QString("");
12732 	if(!active)
12733 		begin="X - ";
12734 
12735 	QString end=QString("");
12736 	if(!comments.isEmpty())
12737 		end=", "+tr("C: %1", "Comments").arg(comments);
12738 
12739 	QString s;
12740 	s+=tr("Act. id: %1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
12741 		.arg(this->activityId)
12742 		.arg(getActivityDetailedDescription(r, this->activityId));
12743 
12744 	s+=" ";
12745 	s+=tr("has a set of preferred starting times:");
12746 	s+=" ";
12747 	for(int i=0; i<this->nPreferredStartingTimes_L; i++){
12748 		if(this->days_L[i]>=0){
12749 			s+=r.daysOfTheWeek[this->days_L[i]];
12750 			s+=" ";
12751 		}
12752 		if(this->hours_L[i]>=0){
12753 			s+=r.hoursOfTheDay[this->hours_L[i]];
12754 		}
12755 		if(i<nPreferredStartingTimes_L-1)
12756 			s+="; ";
12757 	}
12758 	s+=", ";
12759 
12760 	s+=tr("WP:%1%", "Weight Percentage").arg(CustomFETString::number(this->weightPercentage));
12761 
12762 	return begin+s+end;
12763 }
12764 
getDetailedDescription(Rules & r)12765 QString ConstraintActivityPreferredStartingTimes::getDetailedDescription(Rules& r)
12766 {
12767 	QString s=tr("Time constraint");s+="\n";
12768 	s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
12769 		.arg(this->activityId)
12770 		.arg(getActivityDetailedDescription(r, this->activityId));
12771 
12772 	s+="\n";
12773 	s+=tr("has a set of preferred starting times:");
12774 	s+="\n";
12775 	for(int i=0; i<this->nPreferredStartingTimes_L; i++){
12776 		if(this->days_L[i]>=0){
12777 			s+=r.daysOfTheWeek[this->days_L[i]];
12778 			s+=" ";
12779 		}
12780 		if(this->hours_L[i]>=0){
12781 			s+=r.hoursOfTheDay[this->hours_L[i]];
12782 		}
12783 		if(i<this->nPreferredStartingTimes_L-1)
12784 			s+=";  ";
12785 	}
12786 	s+="\n";
12787 
12788 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
12789 
12790 	if(!active){
12791 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
12792 		s+="\n";
12793 	}
12794 	if(!comments.isEmpty()){
12795 		s+=tr("Comments=%1").arg(comments);
12796 		s+="\n";
12797 	}
12798 
12799 	return s;
12800 }
12801 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)12802 double ConstraintActivityPreferredStartingTimes::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
12803 {
12804 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
12805 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
12806 		c.teachersMatrixReady=true;
12807 		c.subgroupsMatrixReady=true;
12808 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
12809 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
12810 
12811 		c.changedForMatrixCalculation=false;
12812 	}
12813 
12814 	int nbroken;
12815 
12816 	assert(r.internalStructureComputed);
12817 
12818 	nbroken=0;
12819 	if(c.times[this->activityIndex]!=UNALLOCATED_TIME){
12820 		int d=c.times[this->activityIndex]%r.nDaysPerWeek; //the day when this activity was scheduled
12821 		int h=c.times[this->activityIndex]/r.nDaysPerWeek; //the hour
12822 		int i;
12823 		for(i=0; i<this->nPreferredStartingTimes_L; i++){
12824 			if(this->days_L[i]>=0 && this->days_L[i]!=d)
12825 				continue;
12826 			if(this->hours_L[i]>=0 && this->hours_L[i]!=h)
12827 				continue;
12828 			break;
12829 		}
12830 		if(i==this->nPreferredStartingTimes_L){
12831 			nbroken=1;
12832 		}
12833 	}
12834 
12835 	if(conflictsString!=nullptr && nbroken>0){
12836 		QString s=tr("Time constraint activity preferred starting times broken for activity with id=%1 (%2), increases conflicts total by %3",
12837 		 "%1 is the id, %2 is the detailed description of the activity")
12838 		 .arg(this->activityId)
12839 		 .arg(getActivityDetailedDescription(r, this->activityId))
12840 		 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*nbroken));
12841 
12842 		dl.append(s);
12843 		cl.append(weightPercentage/100*nbroken);
12844 
12845 		*conflictsString+= s+"\n";
12846 	}
12847 
12848 	if(weightPercentage==100)
12849 		assert(nbroken==0);
12850 	return nbroken * weightPercentage/100;
12851 }
12852 
isRelatedToActivity(Rules & r,Activity * a)12853 bool ConstraintActivityPreferredStartingTimes::isRelatedToActivity(Rules& r, Activity* a)
12854 {
12855 	Q_UNUSED(r);
12856 
12857 	if(this->activityId==a->id)
12858 		return true;
12859 	return false;
12860 }
12861 
isRelatedToTeacher(Teacher * t)12862 bool ConstraintActivityPreferredStartingTimes::isRelatedToTeacher(Teacher* t)
12863 {
12864 	Q_UNUSED(t);
12865 
12866 	return false;
12867 }
12868 
isRelatedToSubject(Subject * s)12869 bool ConstraintActivityPreferredStartingTimes::isRelatedToSubject(Subject* s)
12870 {
12871 	Q_UNUSED(s);
12872 
12873 	return false;
12874 }
12875 
isRelatedToActivityTag(ActivityTag * s)12876 bool ConstraintActivityPreferredStartingTimes::isRelatedToActivityTag(ActivityTag* s)
12877 {
12878 	Q_UNUSED(s);
12879 
12880 	return false;
12881 }
12882 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)12883 bool ConstraintActivityPreferredStartingTimes::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
12884 {
12885 	Q_UNUSED(r);
12886 	Q_UNUSED(s);
12887 
12888 	return false;
12889 }
12890 
hasWrongDayOrHour(Rules & r)12891 bool ConstraintActivityPreferredStartingTimes::hasWrongDayOrHour(Rules& r)
12892 {
12893 	assert(nPreferredStartingTimes_L==days_L.count());
12894 	assert(nPreferredStartingTimes_L==hours_L.count());
12895 
12896 	for(int i=0; i<nPreferredStartingTimes_L; i++)
12897 		if(days_L.at(i)<0 || days_L.at(i)>=r.nDaysPerWeek
12898 		 || hours_L.at(i)<0 || hours_L.at(i)>=r.nHoursPerDay)
12899 			return true;
12900 
12901 	return false;
12902 }
12903 
canRepairWrongDayOrHour(Rules & r)12904 bool ConstraintActivityPreferredStartingTimes::canRepairWrongDayOrHour(Rules& r)
12905 {
12906 	assert(hasWrongDayOrHour(r));
12907 
12908 	return true;
12909 }
12910 
repairWrongDayOrHour(Rules & r)12911 bool ConstraintActivityPreferredStartingTimes::repairWrongDayOrHour(Rules& r)
12912 {
12913 	assert(hasWrongDayOrHour(r));
12914 
12915 	assert(nPreferredStartingTimes_L==days_L.count());
12916 	assert(nPreferredStartingTimes_L==hours_L.count());
12917 
12918 	QList<int> newDays;
12919 	QList<int> newHours;
12920 	int newNPref=0;
12921 
12922 	for(int i=0; i<nPreferredStartingTimes_L; i++)
12923 		if(days_L.at(i)>=0 && days_L.at(i)<r.nDaysPerWeek
12924 		 && hours_L.at(i)>=0 && hours_L.at(i)<r.nHoursPerDay){
12925 			newDays.append(days_L.at(i));
12926 			newHours.append(hours_L.at(i));
12927 			newNPref++;
12928 		}
12929 
12930 	nPreferredStartingTimes_L=newNPref;
12931 	days_L=newDays;
12932 	hours_L=newHours;
12933 
12934 	r.internalStructureComputed=false;
12935 	setRulesModifiedAndOtherThings(&r);
12936 
12937 	return true;
12938 }
12939 
12940 ////////////////////////////////////////////////////////////////////////////////////////////
12941 ////////////////////////////////////////////////////////////////////////////////////////////
12942 
ConstraintActivitiesPreferredStartingTimes()12943 ConstraintActivitiesPreferredStartingTimes::ConstraintActivitiesPreferredStartingTimes()
12944 	: TimeConstraint()
12945 {
12946 	this->type = CONSTRAINT_ACTIVITIES_PREFERRED_STARTING_TIMES;
12947 }
12948 
ConstraintActivitiesPreferredStartingTimes(double wp,const QString & te,const QString & st,const QString & su,const QString & sut,int dur,int nPT_L,QList<int> d_L,QList<int> h_L)12949 ConstraintActivitiesPreferredStartingTimes::ConstraintActivitiesPreferredStartingTimes(double wp, const QString& te,
12950 	const QString& st, const QString& su, const QString& sut, int dur, int nPT_L, QList<int> d_L, QList<int> h_L)
12951 	: TimeConstraint(wp)
12952 {
12953 	assert(dur==-1 || dur>=1);
12954 	duration=dur;
12955 
12956 	assert(d_L.count()==nPT_L);
12957 	assert(h_L.count()==nPT_L);
12958 
12959 	this->teacherName=te;
12960 	this->subjectName=su;
12961 	this->activityTagName=sut;
12962 	this->studentsName=st;
12963 	this->nPreferredStartingTimes_L=nPT_L;
12964 	this->days_L=d_L;
12965 	this->hours_L=h_L;
12966 	this->type=CONSTRAINT_ACTIVITIES_PREFERRED_STARTING_TIMES;
12967 }
12968 
computeInternalStructure(QWidget * parent,Rules & r)12969 bool ConstraintActivitiesPreferredStartingTimes::computeInternalStructure(QWidget* parent, Rules& r)
12970 {
12971 	this->nActivities=0;
12972 	this->activitiesIndices.clear();
12973 
12974 	int it;
12975 	Activity* act;
12976 	int i;
12977 	for(i=0; i<r.nInternalActivities; i++){
12978 		act=&r.internalActivitiesList[i];
12979 
12980 		//check if this activity has the corresponding teacher
12981 		if(this->teacherName!=""){
12982 			it = act->teachersNames.indexOf(this->teacherName);
12983 			if(it==-1)
12984 				continue;
12985 		}
12986 		//check if this activity has the corresponding students
12987 		if(this->studentsName!=""){
12988 			bool commonStudents=false;
12989 			for(const QString& st : qAsConst(act->studentsNames))
12990 				if(r.augmentedSetsShareStudentsFaster(st, studentsName)){
12991 					commonStudents=true;
12992 					break;
12993 				}
12994 
12995 			if(!commonStudents)
12996 				continue;
12997 		}
12998 		//check if this activity has the corresponding subject
12999 		if(this->subjectName!="" && act->subjectName!=this->subjectName){
13000 				continue;
13001 		}
13002 		//check if this activity has the corresponding activity tag
13003 		if(this->activityTagName!="" && !act->activityTagsNames.contains(this->activityTagName)){
13004 				continue;
13005 		}
13006 
13007 		if(duration>=1 && act->duration!=duration)
13008 			continue;
13009 
13010 		assert(this->nActivities < r.nInternalActivities);
13011 		this->activitiesIndices.append(i);
13012 		this->nActivities++;
13013 	}
13014 
13015 	assert(this->activitiesIndices.count()==this->nActivities);
13016 
13017 	//////////////////////
13018 	for(int k=0; k<nPreferredStartingTimes_L; k++){
13019 		if(this->days_L[k] >= r.nDaysPerWeek){
13020 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
13021 			 tr("Constraint activities preferred starting times is wrong because it refers to removed day. Please correct"
13022 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
13023 
13024 			return false;
13025 		}
13026 		if(this->hours_L[k] == r.nHoursPerDay){
13027 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
13028 			 tr("Constraint activities preferred starting times is wrong because a preferred hour is too late (after the last acceptable slot). Please correct"
13029 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
13030 
13031 			return false;
13032 		}
13033 		if(this->hours_L[k] > r.nHoursPerDay){
13034 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
13035 			 tr("Constraint activities preferred starting times is wrong because it refers to removed hour. Please correct"
13036 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
13037 
13038 			return false;
13039 		}
13040 	}
13041 	///////////////////////
13042 
13043 	if(this->nActivities>0)
13044 		return true;
13045 	else{
13046 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
13047 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
13048 		return false;
13049 	}
13050 }
13051 
hasInactiveActivities(Rules & r)13052 bool ConstraintActivitiesPreferredStartingTimes::hasInactiveActivities(Rules& r)
13053 {
13054 	QList<int> localActiveActs;
13055 	QList<int> localAllActs;
13056 
13057 	//returns true if all activities are inactive
13058 	int it;
13059 	Activity* act;
13060 	int i;
13061 	for(i=0; i<r.activitiesList.count(); i++){
13062 		act=r.activitiesList.at(i);
13063 
13064 		//check if this activity has the corresponding teacher
13065 		if(this->teacherName!=""){
13066 			it = act->teachersNames.indexOf(this->teacherName);
13067 			if(it==-1)
13068 				continue;
13069 		}
13070 		//check if this activity has the corresponding students
13071 		if(this->studentsName!=""){
13072 			bool commonStudents=false;
13073 			for(const QString& st : qAsConst(act->studentsNames))
13074 				if(r.setsShareStudents(st, studentsName)){
13075 					commonStudents=true;
13076 					break;
13077 				}
13078 
13079 			if(!commonStudents)
13080 				continue;
13081 		}
13082 		//check if this activity has the corresponding subject
13083 		if(this->subjectName!="" && act->subjectName!=this->subjectName){
13084 				continue;
13085 		}
13086 		//check if this activity has the corresponding activity tag
13087 		if(this->activityTagName!="" && !act->activityTagsNames.contains(this->activityTagName)){
13088 				continue;
13089 		}
13090 
13091 		if(duration>=1 && act->duration!=duration)
13092 			continue;
13093 
13094 		if(!r.inactiveActivities.contains(act->id))
13095 			localActiveActs.append(act->id);
13096 
13097 		localAllActs.append(act->id);
13098 	}
13099 
13100 	if(localActiveActs.count()==0 && localAllActs.count()>0)
13101 		return true;
13102 	else
13103 		return false;
13104 }
13105 
getXmlDescription(Rules & r)13106 QString ConstraintActivitiesPreferredStartingTimes::getXmlDescription(Rules& r)
13107 {
13108 	QString s="<ConstraintActivitiesPreferredStartingTimes>\n";
13109 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
13110 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
13111 	s+="	<Students_Name>"+protect(this->studentsName)+"</Students_Name>\n";
13112 	s+="	<Subject_Name>"+protect(this->subjectName)+"</Subject_Name>\n";
13113 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
13114 	if(duration>=1)
13115 		s+="	<Duration>"+CustomFETString::number(duration)+"</Duration>\n";
13116 	else
13117 		s+="	<Duration></Duration>\n";
13118 	s+="	<Number_of_Preferred_Starting_Times>"+CustomFETString::number(this->nPreferredStartingTimes_L)+"</Number_of_Preferred_Starting_Times>\n";
13119 	for(int i=0; i<nPreferredStartingTimes_L; i++){
13120 		s+="	<Preferred_Starting_Time>\n";
13121 		if(this->days_L[i]>=0)
13122 			s+="		<Preferred_Starting_Day>"+protect(r.daysOfTheWeek[this->days_L[i]])+"</Preferred_Starting_Day>\n";
13123 		if(this->hours_L[i]>=0)
13124 			s+="		<Preferred_Starting_Hour>"+protect(r.hoursOfTheDay[this->hours_L[i]])+"</Preferred_Starting_Hour>\n";
13125 		s+="	</Preferred_Starting_Time>\n";
13126 	}
13127 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
13128 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
13129 	s+="</ConstraintActivitiesPreferredStartingTimes>\n";
13130 	return s;
13131 }
13132 
getDescription(Rules & r)13133 QString ConstraintActivitiesPreferredStartingTimes::getDescription(Rules& r)
13134 {
13135 	QString begin=QString("");
13136 	if(!active)
13137 		begin="X - ";
13138 
13139 	QString end=QString("");
13140 	if(!comments.isEmpty())
13141 		end=", "+tr("C: %1", "Comments").arg(comments);
13142 
13143 	QString s;
13144 
13145 	QString tc, st, su, at, dur;
13146 
13147 	if(this->teacherName!="")
13148 		tc=tr("teacher=%1").arg(this->teacherName);
13149 	else
13150 		tc=tr("all teachers");
13151 
13152 	if(this->studentsName!="")
13153 		st=tr("students=%1").arg(this->studentsName);
13154 	else
13155 		st=tr("all students");
13156 
13157 	if(this->subjectName!="")
13158 		su=tr("subject=%1").arg(this->subjectName);
13159 	else
13160 		su=tr("all subjects");
13161 
13162 	if(this->activityTagName!="")
13163 		at=tr("activity tag=%1").arg(this->activityTagName);
13164 	else
13165 		at=tr("all activity tags");
13166 
13167 	if(duration>=1)
13168 		dur=tr("duration=%1").arg(duration);
13169 	else
13170 		dur=tr("all durations");
13171 
13172 	s+=tr("Activities with %1, %2, %3, %4, %5, have a set of preferred starting times:", "%1...%5 are conditions for the activities").arg(tc).arg(st).arg(su).arg(at).arg(dur);
13173 	s+=" ";
13174 
13175 	for(int i=0; i<this->nPreferredStartingTimes_L; i++){
13176 		if(this->days_L[i]>=0){
13177 			s+=r.daysOfTheWeek[this->days_L[i]];
13178 			s+=" ";
13179 		}
13180 		if(this->hours_L[i]>=0){
13181 			s+=r.hoursOfTheDay[this->hours_L[i]];
13182 		}
13183 		if(i<this->nPreferredStartingTimes_L-1)
13184 			s+="; ";
13185 	}
13186 	s+=", ";
13187 
13188 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
13189 
13190 	return begin+s+end;
13191 }
13192 
getDetailedDescription(Rules & r)13193 QString ConstraintActivitiesPreferredStartingTimes::getDetailedDescription(Rules& r)
13194 {
13195 	QString s=tr("Time constraint");s+="\n";
13196 	s+=tr("Activities with:");s+="\n";
13197 
13198 	if(this->teacherName!="")
13199 		s+=tr("Teacher=%1").arg(this->teacherName);
13200 	else
13201 		s+=tr("All teachers");
13202 	s+="\n";
13203 
13204 	if(this->studentsName!="")
13205 		s+=tr("Students=%1").arg(this->studentsName);
13206 	else
13207 		s+=tr("All students");
13208 	s+="\n";
13209 
13210 	if(this->subjectName!="")
13211 		s+=tr("Subject=%1").arg(this->subjectName);
13212 	else
13213 		s+=tr("All subjects");
13214 	s+="\n";
13215 
13216 	if(this->activityTagName!="")
13217 		s+=tr("Activity tag=%1").arg(this->activityTagName);
13218 	else
13219 		s+=tr("All activity tags");
13220 	s+="\n";
13221 
13222 	if(duration>=1)
13223 		s+=tr("Duration=%1").arg(duration);
13224 	else
13225 		s+=tr("All durations");
13226 	s+="\n";
13227 
13228 	s+=tr("have a set of preferred starting times:");
13229 	s+="\n";
13230 	for(int i=0; i<this->nPreferredStartingTimes_L; i++){
13231 		if(this->days_L[i]>=0){
13232 			s+=r.daysOfTheWeek[this->days_L[i]];
13233 			s+=" ";
13234 		}
13235 		if(this->hours_L[i]>=0){
13236 			s+=r.hoursOfTheDay[this->hours_L[i]];
13237 		}
13238 		if(i<this->nPreferredStartingTimes_L-1)
13239 			s+=";  ";
13240 	}
13241 	s+="\n";
13242 
13243 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
13244 
13245 	if(!active){
13246 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
13247 		s+="\n";
13248 	}
13249 	if(!comments.isEmpty()){
13250 		s+=tr("Comments=%1").arg(comments);
13251 		s+="\n";
13252 	}
13253 
13254 	return s;
13255 }
13256 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)13257 double ConstraintActivitiesPreferredStartingTimes::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
13258 {
13259 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
13260 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
13261 		c.teachersMatrixReady=true;
13262 		c.subgroupsMatrixReady=true;
13263 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
13264 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
13265 
13266 		c.changedForMatrixCalculation=false;
13267 	}
13268 
13269 	int nbroken;
13270 
13271 	assert(r.internalStructureComputed);
13272 
13273 	nbroken=0;
13274 	int tmp;
13275 
13276 	for(int i=0; i<this->nActivities; i++){
13277 		tmp=0;
13278 		int ai=this->activitiesIndices[i];
13279 		if(c.times[ai]!=UNALLOCATED_TIME){
13280 			int d=c.times[ai]%r.nDaysPerWeek; //the day when this activity was scheduled
13281 			int h=c.times[ai]/r.nDaysPerWeek; //the hour
13282 			int i;
13283 			for(i=0; i<this->nPreferredStartingTimes_L; i++){
13284 				if(this->days_L[i]>=0 && this->days_L[i]!=d)
13285 					continue;
13286 				if(this->hours_L[i]>=0 && this->hours_L[i]!=h)
13287 					continue;
13288 				break;
13289 			}
13290 			if(i==this->nPreferredStartingTimes_L){
13291 				tmp=1;
13292 			}
13293 		}
13294 		nbroken+=tmp;
13295 		if(conflictsString!=nullptr && tmp>0){
13296 			QString s=tr("Time constraint activities preferred starting times broken"
13297 			 " for activity with id=%1 (%2),"
13298 			 " increases conflicts total by %3", "%1 is the id, %2 is the detailed description of the activity")
13299 			 .arg(r.internalActivitiesList[ai].id)
13300 			 .arg(getActivityDetailedDescription(r, r.internalActivitiesList[ai].id))
13301 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*tmp));
13302 
13303 			dl.append(s);
13304 			cl.append(weightPercentage/100*tmp);
13305 
13306 			*conflictsString+= s+"\n";
13307 		}
13308 	}
13309 
13310 	if(weightPercentage==100)
13311 		assert(nbroken==0);
13312 	return nbroken * weightPercentage / 100.0;
13313 }
13314 
isRelatedToActivity(Rules & r,Activity * a)13315 bool ConstraintActivitiesPreferredStartingTimes::isRelatedToActivity(Rules& r, Activity* a)
13316 {
13317 	int it;
13318 
13319 	//check if this activity has the corresponding teacher
13320 	if(this->teacherName!=""){
13321 		it = a->teachersNames.indexOf(this->teacherName);
13322 		if(it==-1)
13323 			return false;
13324 	}
13325 	//check if this activity has the corresponding students
13326 	if(this->studentsName!=""){
13327 		bool commonStudents=false;
13328 		for(const QString& st : qAsConst(a->studentsNames)){
13329 			if(r.setsShareStudents(st, this->studentsName)){
13330 				commonStudents=true;
13331 				break;
13332 			}
13333 		}
13334 		if(!commonStudents)
13335 			return false;
13336 	}
13337 	//check if this activity has the corresponding subject
13338 	if(this->subjectName!="" && a->subjectName!=this->subjectName)
13339 		return false;
13340 	//check if this activity has the corresponding activity tag
13341 	if(this->activityTagName!="" && !a->activityTagsNames.contains(this->activityTagName))
13342 		return false;
13343 
13344 	if(duration>=1 && a->duration!=duration)
13345 		return false;
13346 
13347 	return true;
13348 }
13349 
isRelatedToTeacher(Teacher * t)13350 bool ConstraintActivitiesPreferredStartingTimes::isRelatedToTeacher(Teacher* t)
13351 {
13352 	Q_UNUSED(t);
13353 
13354 	return false;
13355 }
13356 
isRelatedToSubject(Subject * s)13357 bool ConstraintActivitiesPreferredStartingTimes::isRelatedToSubject(Subject* s)
13358 {
13359 	Q_UNUSED(s);
13360 
13361 	return false;
13362 }
13363 
isRelatedToActivityTag(ActivityTag * s)13364 bool ConstraintActivitiesPreferredStartingTimes::isRelatedToActivityTag(ActivityTag* s)
13365 {
13366 	Q_UNUSED(s);
13367 
13368 	return false;
13369 }
13370 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)13371 bool ConstraintActivitiesPreferredStartingTimes::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
13372 {
13373 	Q_UNUSED(r);
13374 	Q_UNUSED(s);
13375 
13376 	return false;
13377 }
13378 
hasWrongDayOrHour(Rules & r)13379 bool ConstraintActivitiesPreferredStartingTimes::hasWrongDayOrHour(Rules& r)
13380 {
13381 	assert(nPreferredStartingTimes_L==days_L.count());
13382 	assert(nPreferredStartingTimes_L==hours_L.count());
13383 
13384 	for(int i=0; i<nPreferredStartingTimes_L; i++)
13385 		if(days_L.at(i)<0 || days_L.at(i)>=r.nDaysPerWeek
13386 		 || hours_L.at(i)<0 || hours_L.at(i)>=r.nHoursPerDay)
13387 			return true;
13388 
13389 	return false;
13390 }
13391 
canRepairWrongDayOrHour(Rules & r)13392 bool ConstraintActivitiesPreferredStartingTimes::canRepairWrongDayOrHour(Rules& r)
13393 {
13394 	assert(hasWrongDayOrHour(r));
13395 
13396 	return true;
13397 }
13398 
repairWrongDayOrHour(Rules & r)13399 bool ConstraintActivitiesPreferredStartingTimes::repairWrongDayOrHour(Rules& r)
13400 {
13401 	assert(hasWrongDayOrHour(r));
13402 
13403 	assert(nPreferredStartingTimes_L==days_L.count());
13404 	assert(nPreferredStartingTimes_L==hours_L.count());
13405 
13406 	QList<int> newDays;
13407 	QList<int> newHours;
13408 	int newNPref=0;
13409 
13410 	for(int i=0; i<nPreferredStartingTimes_L; i++)
13411 		if(days_L.at(i)>=0 && days_L.at(i)<r.nDaysPerWeek
13412 		 && hours_L.at(i)>=0 && hours_L.at(i)<r.nHoursPerDay){
13413 			newDays.append(days_L.at(i));
13414 			newHours.append(hours_L.at(i));
13415 			newNPref++;
13416 		}
13417 
13418 	nPreferredStartingTimes_L=newNPref;
13419 	days_L=newDays;
13420 	hours_L=newHours;
13421 
13422 	r.internalStructureComputed=false;
13423 	setRulesModifiedAndOtherThings(&r);
13424 
13425 	return true;
13426 }
13427 
13428 ////////////////////////////////////////////////////////////////////////////////////////////
13429 ////////////////////////////////////////////////////////////////////////////////////////////
13430 
ConstraintSubactivitiesPreferredStartingTimes()13431 ConstraintSubactivitiesPreferredStartingTimes::ConstraintSubactivitiesPreferredStartingTimes()
13432 	: TimeConstraint()
13433 {
13434 	this->type = CONSTRAINT_SUBACTIVITIES_PREFERRED_STARTING_TIMES;
13435 }
13436 
ConstraintSubactivitiesPreferredStartingTimes(double wp,int compNo,const QString & te,const QString & st,const QString & su,const QString & sut,int dur,int nPT_L,QList<int> d_L,QList<int> h_L)13437 ConstraintSubactivitiesPreferredStartingTimes::ConstraintSubactivitiesPreferredStartingTimes(double wp, int compNo, const QString& te,
13438 	const QString& st, const QString& su, const QString& sut, int dur, int nPT_L, QList<int> d_L, QList<int> h_L)
13439 	: TimeConstraint(wp)
13440 {
13441 	assert(dur==-1 || dur>=1);
13442 	duration=dur;
13443 
13444 	assert(d_L.count()==nPT_L);
13445 	assert(h_L.count()==nPT_L);
13446 
13447 	this->componentNumber=compNo;
13448 	this->teacherName=te;
13449 	this->subjectName=su;
13450 	this->activityTagName=sut;
13451 	this->studentsName=st;
13452 	this->nPreferredStartingTimes_L=nPT_L;
13453 	this->days_L=d_L;
13454 	this->hours_L=h_L;
13455 	this->type=CONSTRAINT_SUBACTIVITIES_PREFERRED_STARTING_TIMES;
13456 }
13457 
computeInternalStructure(QWidget * parent,Rules & r)13458 bool ConstraintSubactivitiesPreferredStartingTimes::computeInternalStructure(QWidget* parent, Rules& r)
13459 {
13460 	this->nActivities=0;
13461 	this->activitiesIndices.clear();
13462 
13463 	int it;
13464 	Activity* act;
13465 	int i;
13466 	for(i=0; i<r.nInternalActivities; i++){
13467 		act=&r.internalActivitiesList[i];
13468 
13469 		if(!act->representsComponentNumber(this->componentNumber))
13470 			continue;
13471 
13472 		//check if this activity has the corresponding teacher
13473 		if(this->teacherName!=""){
13474 			it = act->teachersNames.indexOf(this->teacherName);
13475 			if(it==-1)
13476 				continue;
13477 		}
13478 		//check if this activity has the corresponding students
13479 		if(this->studentsName!=""){
13480 			bool commonStudents=false;
13481 			for(const QString& st : qAsConst(act->studentsNames))
13482 				if(r.augmentedSetsShareStudentsFaster(st, studentsName)){
13483 					commonStudents=true;
13484 					break;
13485 				}
13486 
13487 			if(!commonStudents)
13488 				continue;
13489 		}
13490 		//check if this activity has the corresponding subject
13491 		if(this->subjectName!="" && act->subjectName!=this->subjectName){
13492 				continue;
13493 		}
13494 		//check if this activity has the corresponding activity tag
13495 		if(this->activityTagName!="" && !act->activityTagsNames.contains(this->activityTagName)){
13496 				continue;
13497 		}
13498 
13499 		if(duration>=1 && act->duration!=duration)
13500 			continue;
13501 
13502 		assert(this->nActivities < r.nInternalActivities);
13503 		this->nActivities++;
13504 		this->activitiesIndices.append(i);
13505 	}
13506 
13507 	assert(this->activitiesIndices.count()==this->nActivities);
13508 
13509 	//////////////////////
13510 	for(int k=0; k<nPreferredStartingTimes_L; k++){
13511 		if(this->days_L[k] >= r.nDaysPerWeek){
13512 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
13513 			 tr("Constraint subactivities preferred starting times is wrong because it refers to removed day. Please correct"
13514 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
13515 
13516 			return false;
13517 		}
13518 		if(this->hours_L[k] == r.nHoursPerDay){
13519 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
13520 			 tr("Constraint subactivities preferred starting times is wrong because a preferred hour is too late (after the last acceptable slot). Please correct"
13521 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
13522 
13523 			return false;
13524 		}
13525 		if(this->hours_L[k] > r.nHoursPerDay){
13526 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
13527 			 tr("Constraint subactivities preferred starting times is wrong because it refers to removed hour. Please correct"
13528 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
13529 
13530 			return false;
13531 		}
13532 	}
13533 	///////////////////////
13534 
13535 	if(this->nActivities>0)
13536 		return true;
13537 	else{
13538 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
13539 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
13540 		return false;
13541 	}
13542 }
13543 
hasInactiveActivities(Rules & r)13544 bool ConstraintSubactivitiesPreferredStartingTimes::hasInactiveActivities(Rules& r)
13545 {
13546 	QList<int> localActiveActs;
13547 	QList<int> localAllActs;
13548 
13549 	//returns true if all activities are inactive
13550 	int it;
13551 	Activity* act;
13552 	int i;
13553 	for(i=0; i<r.activitiesList.count(); i++){
13554 		act=r.activitiesList.at(i);
13555 
13556 		if(!act->representsComponentNumber(this->componentNumber))
13557 			continue;
13558 
13559 		//check if this activity has the corresponding teacher
13560 		if(this->teacherName!=""){
13561 			it = act->teachersNames.indexOf(this->teacherName);
13562 			if(it==-1)
13563 				continue;
13564 		}
13565 		//check if this activity has the corresponding students
13566 		if(this->studentsName!=""){
13567 			bool commonStudents=false;
13568 			for(const QString& st : qAsConst(act->studentsNames))
13569 				if(r.setsShareStudents(st, studentsName)){
13570 					commonStudents=true;
13571 					break;
13572 				}
13573 
13574 			if(!commonStudents)
13575 				continue;
13576 		}
13577 		//check if this activity has the corresponding subject
13578 		if(this->subjectName!="" && act->subjectName!=this->subjectName){
13579 				continue;
13580 		}
13581 		//check if this activity has the corresponding activity tag
13582 		if(this->activityTagName!="" && !act->activityTagsNames.contains(this->activityTagName)){
13583 				continue;
13584 		}
13585 
13586 		if(duration>=1 && act->duration!=duration)
13587 			continue;
13588 
13589 		if(!r.inactiveActivities.contains(act->id))
13590 			localActiveActs.append(act->id);
13591 
13592 		localAllActs.append(act->id);
13593 	}
13594 
13595 	if(localActiveActs.count()==0 && localAllActs.count()>0)
13596 		return true;
13597 	else
13598 		return false;
13599 }
13600 
getXmlDescription(Rules & r)13601 QString ConstraintSubactivitiesPreferredStartingTimes::getXmlDescription(Rules& r)
13602 {
13603 	QString s="<ConstraintSubactivitiesPreferredStartingTimes>\n";
13604 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
13605 	s+="	<Component_Number>"+CustomFETString::number(this->componentNumber)+"</Component_Number>\n";
13606 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
13607 	s+="	<Students_Name>"+protect(this->studentsName)+"</Students_Name>\n";
13608 	s+="	<Subject_Name>"+protect(this->subjectName)+"</Subject_Name>\n";
13609 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
13610 	if(duration>=1)
13611 		s+="	<Duration>"+CustomFETString::number(duration)+"</Duration>\n";
13612 	else
13613 		s+="	<Duration></Duration>\n";
13614 	s+="	<Number_of_Preferred_Starting_Times>"+CustomFETString::number(this->nPreferredStartingTimes_L)+"</Number_of_Preferred_Starting_Times>\n";
13615 	for(int i=0; i<nPreferredStartingTimes_L; i++){
13616 		s+="	<Preferred_Starting_Time>\n";
13617 		if(this->days_L[i]>=0)
13618 			s+="		<Preferred_Starting_Day>"+protect(r.daysOfTheWeek[this->days_L[i]])+"</Preferred_Starting_Day>\n";
13619 		if(this->hours_L[i]>=0)
13620 			s+="		<Preferred_Starting_Hour>"+protect(r.hoursOfTheDay[this->hours_L[i]])+"</Preferred_Starting_Hour>\n";
13621 		s+="	</Preferred_Starting_Time>\n";
13622 	}
13623 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
13624 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
13625 	s+="</ConstraintSubactivitiesPreferredStartingTimes>\n";
13626 	return s;
13627 }
13628 
getDescription(Rules & r)13629 QString ConstraintSubactivitiesPreferredStartingTimes::getDescription(Rules& r)
13630 {
13631 	QString begin=QString("");
13632 	if(!active)
13633 		begin="X - ";
13634 
13635 	QString end=QString("");
13636 	if(!comments.isEmpty())
13637 		end=", "+tr("C: %1", "Comments").arg(comments);
13638 
13639 	QString tc, st, su, at, dur;
13640 
13641 	if(this->teacherName!="")
13642 		tc=tr("teacher=%1").arg(this->teacherName);
13643 	else
13644 		tc=tr("all teachers");
13645 
13646 	if(this->studentsName!="")
13647 		st=tr("students=%1").arg(this->studentsName);
13648 	else
13649 		st=tr("all students");
13650 
13651 	if(this->subjectName!="")
13652 		su=tr("subject=%1").arg(this->subjectName);
13653 	else
13654 		su=tr("all subjects");
13655 
13656 	if(this->activityTagName!="")
13657 		at=tr("activity tag=%1").arg(this->activityTagName);
13658 	else
13659 		at=tr("all activity tags");
13660 
13661 	if(duration>=1)
13662 		dur=tr("duration=%1").arg(duration);
13663 	else
13664 		dur=tr("all durations");
13665 
13666 	QString s;
13667 
13668 	s+=tr("Subactivities with %1, %2, %3, %4, %5, %6, have a set of preferred starting times:", "%1...%6 are conditions for the subactivities")
13669 		.arg(tr("component number=%1").arg(this->componentNumber)).arg(tc).arg(st).arg(su).arg(at).arg(dur);
13670 	s+=" ";
13671 
13672 	for(int i=0; i<this->nPreferredStartingTimes_L; i++){
13673 		if(this->days_L[i]>=0){
13674 			s+=r.daysOfTheWeek[this->days_L[i]];
13675 			s+=" ";
13676 		}
13677 		if(this->hours_L[i]>=0){
13678 			s+=r.hoursOfTheDay[this->hours_L[i]];
13679 		}
13680 		if(i<this->nPreferredStartingTimes_L-1)
13681 			s+="; ";
13682 	}
13683 	s+=", ";
13684 
13685 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
13686 
13687 	return begin+s+end;
13688 }
13689 
getDetailedDescription(Rules & r)13690 QString ConstraintSubactivitiesPreferredStartingTimes::getDetailedDescription(Rules& r)
13691 {
13692 	QString s=tr("Time constraint");s+="\n";
13693 	s+=tr("Subactivities with:");s+="\n";
13694 
13695 	s+=tr("Component number=%1").arg(this->componentNumber);s+="\n";
13696 
13697 	if(this->teacherName!="")
13698 		s+=tr("Teacher=%1").arg(this->teacherName);
13699 	else
13700 		s+=tr("All teachers");
13701 	s+="\n";
13702 
13703 	if(this->studentsName!="")
13704 		s+=tr("Students=%1").arg(this->studentsName);
13705 	else
13706 		s+=tr("All students");
13707 	s+="\n";
13708 
13709 	if(this->subjectName!="")
13710 		s+=tr("Subject=%1").arg(this->subjectName);
13711 	else
13712 		s+=tr("All subjects");
13713 	s+="\n";
13714 
13715 	if(this->activityTagName!="")
13716 		s+=tr("Activity tag=%1").arg(this->activityTagName);
13717 	else
13718 		s+=tr("All activity tags");
13719 	s+="\n";
13720 
13721 	if(duration>=1)
13722 		s+=tr("Duration=%1").arg(duration);
13723 	else
13724 		s+=tr("All durations");
13725 	s+="\n";
13726 
13727 	s+=tr("have a set of preferred starting times:");
13728 	s+="\n";
13729 	for(int i=0; i<this->nPreferredStartingTimes_L; i++){
13730 		if(this->days_L[i]>=0){
13731 			s+=r.daysOfTheWeek[this->days_L[i]];
13732 			s+=" ";
13733 		}
13734 		if(this->hours_L[i]>=0){
13735 			s+=r.hoursOfTheDay[this->hours_L[i]];
13736 		}
13737 		if(i<this->nPreferredStartingTimes_L-1)
13738 			s+=";  ";
13739 	}
13740 	s+="\n";
13741 
13742 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
13743 
13744 	if(!active){
13745 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
13746 		s+="\n";
13747 	}
13748 	if(!comments.isEmpty()){
13749 		s+=tr("Comments=%1").arg(comments);
13750 		s+="\n";
13751 	}
13752 
13753 	return s;
13754 }
13755 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)13756 double ConstraintSubactivitiesPreferredStartingTimes::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
13757 {
13758 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
13759 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
13760 		c.teachersMatrixReady=true;
13761 		c.subgroupsMatrixReady=true;
13762 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
13763 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
13764 
13765 		c.changedForMatrixCalculation=false;
13766 	}
13767 
13768 	int nbroken;
13769 
13770 	assert(r.internalStructureComputed);
13771 
13772 	nbroken=0;
13773 	int tmp;
13774 
13775 	for(int i=0; i<this->nActivities; i++){
13776 		tmp=0;
13777 		int ai=this->activitiesIndices[i];
13778 		if(c.times[ai]!=UNALLOCATED_TIME){
13779 			int d=c.times[ai]%r.nDaysPerWeek; //the day when this activity was scheduled
13780 			int h=c.times[ai]/r.nDaysPerWeek; //the hour
13781 			int i;
13782 			for(i=0; i<this->nPreferredStartingTimes_L; i++){
13783 				if(this->days_L[i]>=0 && this->days_L[i]!=d)
13784 					continue;
13785 				if(this->hours_L[i]>=0 && this->hours_L[i]!=h)
13786 					continue;
13787 				break;
13788 			}
13789 			if(i==this->nPreferredStartingTimes_L){
13790 				tmp=1;
13791 			}
13792 		}
13793 		nbroken+=tmp;
13794 		if(conflictsString!=nullptr && tmp>0){
13795 			QString s=tr("Time constraint subactivities preferred starting times broken"
13796 			 " for activity with id=%1 (%2), component number %3,"
13797 			 " increases conflicts total by %4", "%1 is the id, %2 is the detailed description of the activity")
13798 			 .arg(r.internalActivitiesList[ai].id)
13799 			 .arg(getActivityDetailedDescription(r, r.internalActivitiesList[ai].id))
13800 			 .arg(this->componentNumber)
13801 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*tmp));
13802 
13803 			dl.append(s);
13804 			cl.append(weightPercentage/100*tmp);
13805 
13806 			*conflictsString+= s+"\n";
13807 		}
13808 	}
13809 
13810 	if(weightPercentage==100)
13811 		assert(nbroken==0);
13812 	return nbroken * weightPercentage / 100.0;
13813 }
13814 
isRelatedToActivity(Rules & r,Activity * a)13815 bool ConstraintSubactivitiesPreferredStartingTimes::isRelatedToActivity(Rules& r, Activity* a)
13816 {
13817 	if(!a->representsComponentNumber(this->componentNumber))
13818 		return false;
13819 
13820 	int it;
13821 
13822 	//check if this activity has the corresponding teacher
13823 	if(this->teacherName!=""){
13824 		it = a->teachersNames.indexOf(this->teacherName);
13825 		if(it==-1)
13826 			return false;
13827 	}
13828 	//check if this activity has the corresponding students
13829 	if(this->studentsName!=""){
13830 		bool commonStudents=false;
13831 		for(const QString& st : qAsConst(a->studentsNames)){
13832 			if(r.setsShareStudents(st, this->studentsName)){
13833 				commonStudents=true;
13834 				break;
13835 			}
13836 		}
13837 		if(!commonStudents)
13838 			return false;
13839 	}
13840 	//check if this activity has the corresponding subject
13841 	if(this->subjectName!="" && a->subjectName!=this->subjectName)
13842 		return false;
13843 	//check if this activity has the corresponding activity tag
13844 	if(this->activityTagName!="" && !a->activityTagsNames.contains(this->activityTagName))
13845 		return false;
13846 
13847 	if(duration>=1 && a->duration!=duration)
13848 		return false;
13849 
13850 	return true;
13851 }
13852 
isRelatedToTeacher(Teacher * t)13853 bool ConstraintSubactivitiesPreferredStartingTimes::isRelatedToTeacher(Teacher* t)
13854 {
13855 	Q_UNUSED(t);
13856 
13857 	return false;
13858 }
13859 
isRelatedToSubject(Subject * s)13860 bool ConstraintSubactivitiesPreferredStartingTimes::isRelatedToSubject(Subject* s)
13861 {
13862 	Q_UNUSED(s);
13863 
13864 	return false;
13865 }
13866 
isRelatedToActivityTag(ActivityTag * s)13867 bool ConstraintSubactivitiesPreferredStartingTimes::isRelatedToActivityTag(ActivityTag* s)
13868 {
13869 	Q_UNUSED(s);
13870 
13871 	return false;
13872 }
13873 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)13874 bool ConstraintSubactivitiesPreferredStartingTimes::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
13875 {
13876 	Q_UNUSED(r);
13877 	Q_UNUSED(s);
13878 
13879 	return false;
13880 }
13881 
hasWrongDayOrHour(Rules & r)13882 bool ConstraintSubactivitiesPreferredStartingTimes::hasWrongDayOrHour(Rules& r)
13883 {
13884 	assert(nPreferredStartingTimes_L==days_L.count());
13885 	assert(nPreferredStartingTimes_L==hours_L.count());
13886 
13887 	for(int i=0; i<nPreferredStartingTimes_L; i++)
13888 		if(days_L.at(i)<0 || days_L.at(i)>=r.nDaysPerWeek
13889 		 || hours_L.at(i)<0 || hours_L.at(i)>=r.nHoursPerDay)
13890 			return true;
13891 
13892 	return false;
13893 }
13894 
canRepairWrongDayOrHour(Rules & r)13895 bool ConstraintSubactivitiesPreferredStartingTimes::canRepairWrongDayOrHour(Rules& r)
13896 {
13897 	assert(hasWrongDayOrHour(r));
13898 
13899 	return true;
13900 }
13901 
repairWrongDayOrHour(Rules & r)13902 bool ConstraintSubactivitiesPreferredStartingTimes::repairWrongDayOrHour(Rules& r)
13903 {
13904 	assert(hasWrongDayOrHour(r));
13905 
13906 	assert(nPreferredStartingTimes_L==days_L.count());
13907 	assert(nPreferredStartingTimes_L==hours_L.count());
13908 
13909 	QList<int> newDays;
13910 	QList<int> newHours;
13911 	int newNPref=0;
13912 
13913 	for(int i=0; i<nPreferredStartingTimes_L; i++)
13914 		if(days_L.at(i)>=0 && days_L.at(i)<r.nDaysPerWeek
13915 		 && hours_L.at(i)>=0 && hours_L.at(i)<r.nHoursPerDay){
13916 			newDays.append(days_L.at(i));
13917 			newHours.append(hours_L.at(i));
13918 			newNPref++;
13919 		}
13920 
13921 	nPreferredStartingTimes_L=newNPref;
13922 	days_L=newDays;
13923 	hours_L=newHours;
13924 
13925 	r.internalStructureComputed=false;
13926 	setRulesModifiedAndOtherThings(&r);
13927 
13928 	return true;
13929 }
13930 
13931 /////////////////////////////////////////////////////////////////////////////////////////////////
13932 /////////////////////////////////////////////////////////////////////////////////////////////////
13933 
ConstraintActivitiesSameStartingHour()13934 ConstraintActivitiesSameStartingHour::ConstraintActivitiesSameStartingHour()
13935 	: TimeConstraint()
13936 {
13937 	type=CONSTRAINT_ACTIVITIES_SAME_STARTING_HOUR;
13938 }
13939 
ConstraintActivitiesSameStartingHour(double wp,int nact,const QList<int> & act)13940 ConstraintActivitiesSameStartingHour::ConstraintActivitiesSameStartingHour(double wp, int nact, const QList<int>& act)
13941  : TimeConstraint(wp)
13942  {
13943 	assert(nact>=2);
13944 	assert(act.count()==nact);
13945 	this->n_activities=nact;
13946 	this->activitiesId.clear();
13947 	for(int i=0; i<nact; i++)
13948 		this->activitiesId.append(act.at(i));
13949 
13950 	this->type=CONSTRAINT_ACTIVITIES_SAME_STARTING_HOUR;
13951 }
13952 
computeInternalStructure(QWidget * parent,Rules & r)13953 bool ConstraintActivitiesSameStartingHour::computeInternalStructure(QWidget* parent, Rules& r)
13954 {
13955 	//compute the indices of the activities,
13956 	//based on their unique ID
13957 
13958 	assert(this->n_activities==this->activitiesId.count());
13959 
13960 	this->_activities.clear();
13961 	for(int i=0; i<this->n_activities; i++){
13962 		int j=r.activitiesHash.value(activitiesId.at(i), -1);
13963 		//assert(j>=0);
13964 		if(j>=0)
13965 			_activities.append(j);
13966 		/*Activity* act;
13967 		for(j=0; j<r.nInternalActivities; j++){
13968 			act=&r.internalActivitiesList[j];
13969 			if(act->id==this->activitiesId[i]){
13970 				this->_activities.append(j);
13971 				break;
13972 			}
13973 		}*/
13974 	}
13975 	this->_n_activities=this->_activities.count();
13976 
13977 	if(this->_n_activities<=1){
13978 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
13979 			tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
13980 		//assert(0);
13981 		return false;
13982 	}
13983 
13984 	return true;
13985 }
13986 
removeUseless(Rules & r)13987 void ConstraintActivitiesSameStartingHour::removeUseless(Rules& r)
13988 {
13989 	//remove the activitiesId which no longer exist (used after the deletion of an activity)
13990 
13991 	assert(this->n_activities==this->activitiesId.count());
13992 
13993 	QList<int> tmpList;
13994 
13995 	for(int i=0; i<this->n_activities; i++){
13996 		Activity* act=r.activitiesPointerHash.value(activitiesId[i], nullptr);
13997 		if(act!=nullptr)
13998 			tmpList.append(act->id);
13999 		/*for(int k=0; k<r.activitiesList.size(); k++){
14000 			Activity* act=r.activitiesList[k];
14001 			if(act->id==this->activitiesId[i]){
14002 				tmpList.append(act->id);
14003 				break;
14004 			}
14005 		}*/
14006 	}
14007 
14008 	this->activitiesId=tmpList;
14009 	this->n_activities=this->activitiesId.count();
14010 
14011 	r.internalStructureComputed=false;
14012 }
14013 
hasInactiveActivities(Rules & r)14014 bool ConstraintActivitiesSameStartingHour::hasInactiveActivities(Rules& r)
14015 {
14016 	int count=0;
14017 
14018 	for(int i=0; i<this->n_activities; i++)
14019 		if(r.inactiveActivities.contains(this->activitiesId[i]))
14020 			count++;
14021 
14022 	if(this->n_activities-count<=1)
14023 		return true;
14024 	else
14025 		return false;
14026 }
14027 
getXmlDescription(Rules & r)14028 QString ConstraintActivitiesSameStartingHour::getXmlDescription(Rules& r)
14029 {
14030 	Q_UNUSED(r);
14031 
14032 	QString s="<ConstraintActivitiesSameStartingHour>\n";
14033 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
14034 	s+="	<Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
14035 	for(int i=0; i<this->n_activities; i++)
14036 		s+="	<Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
14037 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
14038 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
14039 	s+="</ConstraintActivitiesSameStartingHour>\n";
14040 	return s;
14041 }
14042 
getDescription(Rules & r)14043 QString ConstraintActivitiesSameStartingHour::getDescription(Rules& r)
14044 {
14045 	Q_UNUSED(r);
14046 
14047 	QString begin=QString("");
14048 	if(!active)
14049 		begin="X - ";
14050 
14051 	QString end=QString("");
14052 	if(!comments.isEmpty())
14053 		end=", "+tr("C: %1", "Comments").arg(comments);
14054 
14055 	QString s;
14056 	s+=tr("Activities same starting hour");s+=", ";
14057 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
14058 	s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
14059 	for(int i=0; i<this->n_activities; i++){
14060 		s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);
14061 		if(i<this->n_activities-1)
14062 			s+=", ";
14063 	}
14064 
14065 	return begin+s+end;
14066 }
14067 
getDetailedDescription(Rules & r)14068 QString ConstraintActivitiesSameStartingHour::getDetailedDescription(Rules& r)
14069 {
14070 	QString s;
14071 
14072 	s=tr("Time constraint");s+="\n";
14073 	s+=tr("Activities must have the same starting hour");s+="\n";
14074 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
14075 	s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
14076 	for(int i=0; i<this->n_activities; i++){
14077 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
14078 			.arg(this->activitiesId[i])
14079 			.arg(getActivityDetailedDescription(r, this->activitiesId[i]));
14080 		s+="\n";
14081 	}
14082 
14083 	if(!active){
14084 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
14085 		s+="\n";
14086 	}
14087 	if(!comments.isEmpty()){
14088 		s+=tr("Comments=%1").arg(comments);
14089 		s+="\n";
14090 	}
14091 
14092 	return s;
14093 }
14094 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)14095 double ConstraintActivitiesSameStartingHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
14096 {
14097 	assert(r.internalStructureComputed);
14098 
14099 	int nbroken;
14100 
14101 	//We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
14102 
14103 	//sum the differences in the scheduled hour for all pairs of activities.
14104 
14105 	//without logging
14106 	if(conflictsString==nullptr){
14107 		nbroken=0;
14108 		for(int i=1; i<this->_n_activities; i++){
14109 			int t1=c.times[this->_activities[i]];
14110 			if(t1!=UNALLOCATED_TIME){
14111 				//int day1=t1%r.nDaysPerWeek;
14112 				int hour1=t1/r.nDaysPerWeek;
14113 				for(int j=0; j<i; j++){
14114 					int t2=c.times[this->_activities[j]];
14115 					if(t2!=UNALLOCATED_TIME){
14116 						//int day2=t2%r.nDaysPerWeek;
14117 						int hour2=t2/r.nDaysPerWeek;
14118 						int tmp=0;
14119 
14120 						//	tmp = abs(hour1-hour2);
14121 						if(hour1!=hour2)
14122 							tmp=1;
14123 
14124 						nbroken+=tmp;
14125 					}
14126 				}
14127 			}
14128 		}
14129 	}
14130 	//with logging
14131 	else{
14132 		nbroken=0;
14133 		for(int i=1; i<this->_n_activities; i++){
14134 			int t1=c.times[this->_activities[i]];
14135 			if(t1!=UNALLOCATED_TIME){
14136 				//int day1=t1%r.nDaysPerWeek;
14137 				int hour1=t1/r.nDaysPerWeek;
14138 				for(int j=0; j<i; j++){
14139 					int t2=c.times[this->_activities[j]];
14140 					if(t2!=UNALLOCATED_TIME){
14141 						//int day2=t2%r.nDaysPerWeek;
14142 						int hour2=t2/r.nDaysPerWeek;
14143 						int tmp=0;
14144 
14145 						//	tmp = abs(hour1-hour2);
14146 						if(hour1!=hour2)
14147 							tmp=1;
14148 
14149 						nbroken+=tmp;
14150 
14151 						if(tmp>0 && conflictsString!=nullptr){
14152 							QString s=tr("Time constraint activities same starting hour broken, because activity with id=%1 (%2) is not at the same hour with activity with id=%3 (%4)"
14153 							 , "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
14154 							 .arg(this->activitiesId[i])
14155 							 .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
14156 							 .arg(this->activitiesId[j])
14157 							 .arg(getActivityDetailedDescription(r, this->activitiesId[j]));
14158 							s+=". ";
14159 							s+=tr("Conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
14160 
14161 							dl.append(s);
14162 							cl.append(tmp*weightPercentage/100);
14163 
14164 							*conflictsString+= s+"\n";
14165 						}
14166 					}
14167 				}
14168 			}
14169 		}
14170 	}
14171 
14172 	if(weightPercentage==100)
14173 		assert(nbroken==0);
14174 	return weightPercentage/100 * nbroken;
14175 }
14176 
isRelatedToActivity(Rules & r,Activity * a)14177 bool ConstraintActivitiesSameStartingHour::isRelatedToActivity(Rules& r, Activity* a)
14178 {
14179 	Q_UNUSED(r);
14180 
14181 	for(int i=0; i<this->n_activities; i++)
14182 		if(this->activitiesId[i]==a->id)
14183 			return true;
14184 	return false;
14185 }
14186 
isRelatedToTeacher(Teacher * t)14187 bool ConstraintActivitiesSameStartingHour::isRelatedToTeacher(Teacher* t)
14188 {
14189 	Q_UNUSED(t);
14190 
14191 	return false;
14192 }
14193 
isRelatedToSubject(Subject * s)14194 bool ConstraintActivitiesSameStartingHour::isRelatedToSubject(Subject* s)
14195 {
14196 	Q_UNUSED(s);
14197 
14198 	return false;
14199 }
14200 
isRelatedToActivityTag(ActivityTag * s)14201 bool ConstraintActivitiesSameStartingHour::isRelatedToActivityTag(ActivityTag* s)
14202 {
14203 	Q_UNUSED(s);
14204 
14205 	return false;
14206 }
14207 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)14208 bool ConstraintActivitiesSameStartingHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
14209 {
14210 	Q_UNUSED(r);
14211 	Q_UNUSED(s);
14212 
14213 	return false;
14214 }
14215 
hasWrongDayOrHour(Rules & r)14216 bool ConstraintActivitiesSameStartingHour::hasWrongDayOrHour(Rules& r)
14217 {
14218 	Q_UNUSED(r);
14219 	return false;
14220 }
14221 
canRepairWrongDayOrHour(Rules & r)14222 bool ConstraintActivitiesSameStartingHour::canRepairWrongDayOrHour(Rules& r)
14223 {
14224 	Q_UNUSED(r);
14225 	assert(0);
14226 
14227 	return true;
14228 }
14229 
repairWrongDayOrHour(Rules & r)14230 bool ConstraintActivitiesSameStartingHour::repairWrongDayOrHour(Rules& r)
14231 {
14232 	Q_UNUSED(r);
14233 	assert(0); //should check hasWrongDayOrHour, firstly
14234 
14235 	return true;
14236 }
14237 
14238 /////////////////////////////////////////////////////////////////////////////////////////////////
14239 /////////////////////////////////////////////////////////////////////////////////////////////////
14240 
ConstraintActivitiesSameStartingDay()14241 ConstraintActivitiesSameStartingDay::ConstraintActivitiesSameStartingDay()
14242 	: TimeConstraint()
14243 {
14244 	type=CONSTRAINT_ACTIVITIES_SAME_STARTING_DAY;
14245 }
14246 
ConstraintActivitiesSameStartingDay(double wp,int nact,const QList<int> & act)14247 ConstraintActivitiesSameStartingDay::ConstraintActivitiesSameStartingDay(double wp, int nact, const QList<int>& act)
14248  : TimeConstraint(wp)
14249  {
14250 	assert(nact>=2);
14251 	assert(act.count()==nact);
14252 	this->n_activities=nact;
14253 	this->activitiesId.clear();
14254 	for(int i=0; i<nact; i++)
14255 		this->activitiesId.append(act.at(i));
14256 
14257 	this->type=CONSTRAINT_ACTIVITIES_SAME_STARTING_DAY;
14258 }
14259 
computeInternalStructure(QWidget * parent,Rules & r)14260 bool ConstraintActivitiesSameStartingDay::computeInternalStructure(QWidget* parent, Rules& r)
14261 {
14262 	//compute the indices of the activities,
14263 	//based on their unique ID
14264 
14265 	assert(this->n_activities==this->activitiesId.count());
14266 
14267 	this->_activities.clear();
14268 	for(int i=0; i<this->n_activities; i++){
14269 		int j=r.activitiesHash.value(activitiesId.at(i), -1);
14270 		//assert(j>=0);
14271 		if(j>=0)
14272 			_activities.append(j);
14273 		/*int j;
14274 		Activity* act;
14275 		for(j=0; j<r.nInternalActivities; j++){
14276 			act=&r.internalActivitiesList[j];
14277 			if(act->id==this->activitiesId[i]){
14278 				this->_activities.append(j);
14279 				break;
14280 			}
14281 		}*/
14282 	}
14283 	this->_n_activities=this->_activities.count();
14284 
14285 	if(this->_n_activities<=1){
14286 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
14287 			tr("Following constraint is wrong (because you need 2 or more activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
14288 		//assert(0);
14289 		return false;
14290 	}
14291 
14292 	return true;
14293 }
14294 
removeUseless(Rules & r)14295 void ConstraintActivitiesSameStartingDay::removeUseless(Rules& r)
14296 {
14297 	//remove the activitiesId which no longer exist (used after the deletion of an activity)
14298 
14299 	assert(this->n_activities==this->activitiesId.count());
14300 
14301 	QList<int> tmpList;
14302 
14303 	for(int i=0; i<this->n_activities; i++){
14304 		Activity* act=r.activitiesPointerHash.value(activitiesId[i], nullptr);
14305 		if(act!=nullptr)
14306 			tmpList.append(act->id);
14307 		/*for(int k=0; k<r.activitiesList.size(); k++){
14308 			Activity* act=r.activitiesList[k];
14309 			if(act->id==this->activitiesId[i]){
14310 				tmpList.append(act->id);
14311 				break;
14312 			}
14313 		}*/
14314 	}
14315 
14316 	this->activitiesId=tmpList;
14317 	this->n_activities=this->activitiesId.count();
14318 
14319 	r.internalStructureComputed=false;
14320 }
14321 
hasInactiveActivities(Rules & r)14322 bool ConstraintActivitiesSameStartingDay::hasInactiveActivities(Rules& r)
14323 {
14324 	int count=0;
14325 
14326 	for(int i=0; i<this->n_activities; i++)
14327 		if(r.inactiveActivities.contains(this->activitiesId[i]))
14328 			count++;
14329 
14330 	if(this->n_activities-count<=1)
14331 		return true;
14332 	else
14333 		return false;
14334 }
14335 
getXmlDescription(Rules & r)14336 QString ConstraintActivitiesSameStartingDay::getXmlDescription(Rules& r)
14337 {
14338 	Q_UNUSED(r);
14339 
14340 	QString s="<ConstraintActivitiesSameStartingDay>\n";
14341 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
14342 	s+="	<Number_of_Activities>"+CustomFETString::number(this->n_activities)+"</Number_of_Activities>\n";
14343 	for(int i=0; i<this->n_activities; i++)
14344 		s+="	<Activity_Id>"+CustomFETString::number(this->activitiesId[i])+"</Activity_Id>\n";
14345 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
14346 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
14347 	s+="</ConstraintActivitiesSameStartingDay>\n";
14348 	return s;
14349 }
14350 
getDescription(Rules & r)14351 QString ConstraintActivitiesSameStartingDay::getDescription(Rules& r)
14352 {
14353 	Q_UNUSED(r);
14354 
14355 	QString begin=QString("");
14356 	if(!active)
14357 		begin="X - ";
14358 
14359 	QString end=QString("");
14360 	if(!comments.isEmpty())
14361 		end=", "+tr("C: %1", "Comments").arg(comments);
14362 
14363 	QString s;
14364 	s+=tr("Activities same starting day");s+=", ";
14365 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
14366 	s+=tr("NA:%1", "Number of activities").arg(this->n_activities);s+=", ";
14367 	for(int i=0; i<this->n_activities; i++){
14368 		s+=tr("Id:%1", "Id of activity").arg(this->activitiesId[i]);
14369 		if(i<this->n_activities-1)
14370 			s+=", ";
14371 	}
14372 
14373 	return begin+s+end;
14374 }
14375 
getDetailedDescription(Rules & r)14376 QString ConstraintActivitiesSameStartingDay::getDetailedDescription(Rules& r)
14377 {
14378 	QString s;
14379 
14380 	s=tr("Time constraint");s+="\n";
14381 	s+=tr("Activities must have the same starting day");s+="\n";
14382 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
14383 	s+=tr("Number of activities=%1").arg(this->n_activities);s+="\n";
14384 	for(int i=0; i<this->n_activities; i++){
14385 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
14386 			.arg(this->activitiesId[i])
14387 			.arg(getActivityDetailedDescription(r, this->activitiesId[i]));
14388 		s+="\n";
14389 	}
14390 
14391 	if(!active){
14392 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
14393 		s+="\n";
14394 	}
14395 	if(!comments.isEmpty()){
14396 		s+=tr("Comments=%1").arg(comments);
14397 		s+="\n";
14398 	}
14399 
14400 	return s;
14401 }
14402 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)14403 double ConstraintActivitiesSameStartingDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
14404 {
14405 	assert(r.internalStructureComputed);
14406 
14407 	int nbroken;
14408 
14409 	//We do not use the matrices 'subgroupsMatrix' nor 'teachersMatrix'.
14410 
14411 	//sum the differences in the scheduled hour for all pairs of activities.
14412 
14413 	//without logging
14414 	if(conflictsString==nullptr){
14415 		nbroken=0;
14416 		for(int i=1; i<this->_n_activities; i++){
14417 			int t1=c.times[this->_activities[i]];
14418 			if(t1!=UNALLOCATED_TIME){
14419 				int day1=t1%r.nDaysPerWeek;
14420 				//int hour1=t1/r.nDaysPerWeek;
14421 				for(int j=0; j<i; j++){
14422 					int t2=c.times[this->_activities[j]];
14423 					if(t2!=UNALLOCATED_TIME){
14424 						int day2=t2%r.nDaysPerWeek;
14425 						//int hour2=t2/r.nDaysPerWeek;
14426 						int tmp=0;
14427 
14428 						if(day1!=day2)
14429 							tmp=1;
14430 
14431 						nbroken+=tmp;
14432 					}
14433 				}
14434 			}
14435 		}
14436 	}
14437 	//with logging
14438 	else{
14439 		nbroken=0;
14440 		for(int i=1; i<this->_n_activities; i++){
14441 			int t1=c.times[this->_activities[i]];
14442 			if(t1!=UNALLOCATED_TIME){
14443 				int day1=t1%r.nDaysPerWeek;
14444 				//int hour1=t1/r.nDaysPerWeek;
14445 				for(int j=0; j<i; j++){
14446 					int t2=c.times[this->_activities[j]];
14447 					if(t2!=UNALLOCATED_TIME){
14448 						int day2=t2%r.nDaysPerWeek;
14449 						//int hour2=t2/r.nDaysPerWeek;
14450 						int tmp=0;
14451 
14452 						if(day1!=day2)
14453 							tmp=1;
14454 
14455 						nbroken+=tmp;
14456 
14457 						if(tmp>0 && conflictsString!=nullptr){
14458 							QString s=tr("Time constraint activities same starting day broken, because activity with id=%1 (%2) is not in the same day with activity with id=%3 (%4)"
14459 							 , "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
14460 							 .arg(this->activitiesId[i])
14461 							 .arg(getActivityDetailedDescription(r, this->activitiesId[i]))
14462 							 .arg(this->activitiesId[j])
14463 							 .arg(getActivityDetailedDescription(r, this->activitiesId[j]));
14464 							s+=". ";
14465 							s+=tr("Conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(tmp*weightPercentage/100));
14466 
14467 							dl.append(s);
14468 							cl.append(tmp*weightPercentage/100);
14469 
14470 							*conflictsString+= s+"\n";
14471 						}
14472 					}
14473 				}
14474 			}
14475 		}
14476 	}
14477 
14478 	if(weightPercentage==100)
14479 		assert(nbroken==0);
14480 	return weightPercentage/100 * nbroken;
14481 }
14482 
isRelatedToActivity(Rules & r,Activity * a)14483 bool ConstraintActivitiesSameStartingDay::isRelatedToActivity(Rules& r, Activity* a)
14484 {
14485 	Q_UNUSED(r);
14486 
14487 	for(int i=0; i<this->n_activities; i++)
14488 		if(this->activitiesId[i]==a->id)
14489 			return true;
14490 	return false;
14491 }
14492 
isRelatedToTeacher(Teacher * t)14493 bool ConstraintActivitiesSameStartingDay::isRelatedToTeacher(Teacher* t)
14494 {
14495 	Q_UNUSED(t);
14496 
14497 	return false;
14498 }
14499 
isRelatedToSubject(Subject * s)14500 bool ConstraintActivitiesSameStartingDay::isRelatedToSubject(Subject* s)
14501 {
14502 	Q_UNUSED(s);
14503 
14504 	return false;
14505 }
14506 
isRelatedToActivityTag(ActivityTag * s)14507 bool ConstraintActivitiesSameStartingDay::isRelatedToActivityTag(ActivityTag* s)
14508 {
14509 	Q_UNUSED(s);
14510 
14511 	return false;
14512 }
14513 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)14514 bool ConstraintActivitiesSameStartingDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
14515 {
14516 	Q_UNUSED(r);
14517 	Q_UNUSED(s);
14518 
14519 	return false;
14520 }
14521 
hasWrongDayOrHour(Rules & r)14522 bool ConstraintActivitiesSameStartingDay::hasWrongDayOrHour(Rules& r)
14523 {
14524 	Q_UNUSED(r);
14525 	return false;
14526 }
14527 
canRepairWrongDayOrHour(Rules & r)14528 bool ConstraintActivitiesSameStartingDay::canRepairWrongDayOrHour(Rules& r)
14529 {
14530 	Q_UNUSED(r);
14531 	assert(0);
14532 
14533 	return true;
14534 }
14535 
repairWrongDayOrHour(Rules & r)14536 bool ConstraintActivitiesSameStartingDay::repairWrongDayOrHour(Rules& r)
14537 {
14538 	Q_UNUSED(r);
14539 	assert(0); //should check hasWrongDayOrHour, firstly
14540 
14541 	return true;
14542 }
14543 
14544 ////////////////////////////////////////////////////////////////////////////////////////////
14545 ////////////////////////////////////////////////////////////////////////////////////////////
14546 
ConstraintTwoActivitiesConsecutive()14547 ConstraintTwoActivitiesConsecutive::ConstraintTwoActivitiesConsecutive()
14548 	: TimeConstraint()
14549 {
14550 	this->type = CONSTRAINT_TWO_ACTIVITIES_CONSECUTIVE;
14551 }
14552 
ConstraintTwoActivitiesConsecutive(double wp,int firstActId,int secondActId)14553 ConstraintTwoActivitiesConsecutive::ConstraintTwoActivitiesConsecutive(double wp, int firstActId, int secondActId)
14554 	: TimeConstraint(wp)
14555 {
14556 	this->firstActivityId = firstActId;
14557 	this->secondActivityId=secondActId;
14558 	this->type = CONSTRAINT_TWO_ACTIVITIES_CONSECUTIVE;
14559 }
14560 
computeInternalStructure(QWidget * parent,Rules & r)14561 bool ConstraintTwoActivitiesConsecutive::computeInternalStructure(QWidget* parent, Rules& r)
14562 {
14563 	/*Activity* act;
14564 	int i;
14565 	for(i=0; i<r.nInternalActivities; i++){
14566 		act=&r.internalActivitiesList[i];
14567 		if(act->id==this->firstActivityId)
14568 			break;
14569 	}*/
14570 
14571 	int i=r.activitiesHash.value(firstActivityId, r.nInternalActivities);
14572 
14573 	if(i==r.nInternalActivities){
14574 		//assert(0);
14575 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
14576 			tr("Following constraint is wrong (refers to inexistent activity ids):\n%1").arg(this->getDetailedDescription(r)));
14577 		return false;
14578 	}
14579 
14580 	this->firstActivityIndex=i;
14581 
14582 	////////
14583 
14584 	/*for(i=0; i<r.nInternalActivities; i++){
14585 		act=&r.internalActivitiesList[i];
14586 		if(act->id==this->secondActivityId)
14587 			break;
14588 	}*/
14589 
14590 	i=r.activitiesHash.value(secondActivityId, r.nInternalActivities);
14591 
14592 	if(i==r.nInternalActivities){
14593 		//assert(0);
14594 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
14595 			tr("Following constraint is wrong (refers to inexistent activity ids):\n%1").arg(this->getDetailedDescription(r)));
14596 		return false;
14597 	}
14598 
14599 	this->secondActivityIndex=i;
14600 
14601 	if(firstActivityIndex==secondActivityIndex){
14602 		//assert(0);
14603 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
14604 			tr("Following constraint is wrong (refers to same activities):\n%1").arg(this->getDetailedDescription(r)));
14605 		return false;
14606 	}
14607 	assert(firstActivityIndex!=secondActivityIndex);
14608 
14609 	return true;
14610 }
14611 
hasInactiveActivities(Rules & r)14612 bool ConstraintTwoActivitiesConsecutive::hasInactiveActivities(Rules& r)
14613 {
14614 	if(r.inactiveActivities.contains(this->firstActivityId))
14615 		return true;
14616 	if(r.inactiveActivities.contains(this->secondActivityId))
14617 		return true;
14618 	return false;
14619 }
14620 
getXmlDescription(Rules & r)14621 QString ConstraintTwoActivitiesConsecutive::getXmlDescription(Rules& r)
14622 {
14623 	Q_UNUSED(r);
14624 
14625 	QString s="<ConstraintTwoActivitiesConsecutive>\n";
14626 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
14627 	s+="	<First_Activity_Id>"+CustomFETString::number(this->firstActivityId)+"</First_Activity_Id>\n";
14628 	s+="	<Second_Activity_Id>"+CustomFETString::number(this->secondActivityId)+"</Second_Activity_Id>\n";
14629 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
14630 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
14631 	s+="</ConstraintTwoActivitiesConsecutive>\n";
14632 	return s;
14633 }
14634 
getDescription(Rules & r)14635 QString ConstraintTwoActivitiesConsecutive::getDescription(Rules& r)
14636 {
14637 	Q_UNUSED(r);
14638 
14639 	QString begin=QString("");
14640 	if(!active)
14641 		begin="X - ";
14642 
14643 	QString end=QString("");
14644 	if(!comments.isEmpty())
14645 		end=", "+tr("C: %1", "Comments").arg(comments);
14646 
14647 	QString s;
14648 
14649 	s=tr("Two activities consecutive:");
14650 	s+=" ";
14651 
14652 	s+=tr("first act. id: %1", "act.=activity").arg(this->firstActivityId);
14653 	s+=", ";
14654 	s+=tr("second act. id: %1", "act.=activity").arg(this->secondActivityId);
14655 	s+=", ";
14656 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
14657 
14658 	return begin+s+end;
14659 }
14660 
getDetailedDescription(Rules & r)14661 QString ConstraintTwoActivitiesConsecutive::getDetailedDescription(Rules& r)
14662 {
14663 	QString s=tr("Time constraint");s+="\n";
14664 	s+=tr("Two activities consecutive (second activity must be placed immediately after the first"
14665 	 " activity, in the same day, possibly separated by breaks)"); s+="\n";
14666 
14667 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
14668 
14669 	s+=tr("First activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
14670 		.arg(this->firstActivityId)
14671 		.arg(getActivityDetailedDescription(r, this->firstActivityId));
14672 	s+="\n";
14673 
14674 	s+=tr("Second activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
14675 		.arg(this->secondActivityId)
14676 		.arg(getActivityDetailedDescription(r, this->secondActivityId));
14677 	s+="\n";
14678 
14679 	if(!active){
14680 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
14681 		s+="\n";
14682 	}
14683 	if(!comments.isEmpty()){
14684 		s+=tr("Comments=%1").arg(comments);
14685 		s+="\n";
14686 	}
14687 
14688 	return s;
14689 }
14690 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)14691 double ConstraintTwoActivitiesConsecutive::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
14692 {
14693 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
14694 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
14695 		c.teachersMatrixReady=true;
14696 		c.subgroupsMatrixReady=true;
14697 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
14698 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
14699 
14700 		c.changedForMatrixCalculation=false;
14701 	}
14702 
14703 	int nbroken;
14704 
14705 	assert(r.internalStructureComputed);
14706 
14707 	nbroken=0;
14708 	if(c.times[this->firstActivityIndex]!=UNALLOCATED_TIME && c.times[this->secondActivityIndex]!=UNALLOCATED_TIME){
14709 		int fd=c.times[this->firstActivityIndex]%r.nDaysPerWeek; //the day when first activity was scheduled
14710 		int fh=c.times[this->firstActivityIndex]/r.nDaysPerWeek; //the hour
14711 		int sd=c.times[this->secondActivityIndex]%r.nDaysPerWeek; //the day when second activity was scheduled
14712 		int sh=c.times[this->secondActivityIndex]/r.nDaysPerWeek; //the hour
14713 
14714 		if(fd!=sd)
14715 			nbroken=1;
14716 		else if(fh+r.internalActivitiesList[this->firstActivityIndex].duration>sh)
14717 			nbroken=1;
14718 		else{
14719 			assert(fd==sd);
14720 			int h;
14721 			int d=fd;
14722 			assert(d==sd);
14723 			for(h=fh+r.internalActivitiesList[this->firstActivityIndex].duration; h<r.nHoursPerDay; h++)
14724 				if(!breakDayHour[d][h])
14725 					break;
14726 
14727 			assert(h<=sh);
14728 
14729 			if(h!=sh)
14730 				nbroken=1;
14731 		}
14732 	}
14733 
14734 	assert(nbroken==0 || nbroken==1);
14735 
14736 	if(conflictsString!=nullptr && nbroken>0){
14737 		QString s=tr("Time constraint two activities consecutive broken for first activity with id=%1 (%2) and "
14738 		 "second activity with id=%3 (%4), increases conflicts total by %5", "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
14739 		 .arg(this->firstActivityId)
14740 		 .arg(getActivityDetailedDescription(r, this->firstActivityId))
14741 		 .arg(this->secondActivityId)
14742 		 .arg(getActivityDetailedDescription(r, this->secondActivityId))
14743 		 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*nbroken));
14744 
14745 		dl.append(s);
14746 		cl.append(weightPercentage/100*nbroken);
14747 
14748 		*conflictsString+= s+"\n";
14749 	}
14750 
14751 	if(weightPercentage==100)
14752 		assert(nbroken==0);
14753 	return nbroken * weightPercentage/100;
14754 }
14755 
isRelatedToActivity(Rules & r,Activity * a)14756 bool ConstraintTwoActivitiesConsecutive::isRelatedToActivity(Rules& r, Activity* a)
14757 {
14758 	Q_UNUSED(r);
14759 
14760 	if(this->firstActivityId==a->id)
14761 		return true;
14762 	if(this->secondActivityId==a->id)
14763 		return true;
14764 	return false;
14765 }
14766 
isRelatedToTeacher(Teacher * t)14767 bool ConstraintTwoActivitiesConsecutive::isRelatedToTeacher(Teacher* t)
14768 {
14769 	Q_UNUSED(t);
14770 
14771 	return false;
14772 }
14773 
isRelatedToSubject(Subject * s)14774 bool ConstraintTwoActivitiesConsecutive::isRelatedToSubject(Subject* s)
14775 {
14776 	Q_UNUSED(s);
14777 
14778 	return false;
14779 }
14780 
isRelatedToActivityTag(ActivityTag * s)14781 bool ConstraintTwoActivitiesConsecutive::isRelatedToActivityTag(ActivityTag* s)
14782 {
14783 	Q_UNUSED(s);
14784 
14785 	return false;
14786 }
14787 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)14788 bool ConstraintTwoActivitiesConsecutive::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
14789 {
14790 	Q_UNUSED(r);
14791 	Q_UNUSED(s);
14792 
14793 	return false;
14794 }
14795 
hasWrongDayOrHour(Rules & r)14796 bool ConstraintTwoActivitiesConsecutive::hasWrongDayOrHour(Rules& r)
14797 {
14798 	Q_UNUSED(r);
14799 	return false;
14800 }
14801 
canRepairWrongDayOrHour(Rules & r)14802 bool ConstraintTwoActivitiesConsecutive::canRepairWrongDayOrHour(Rules& r)
14803 {
14804 	Q_UNUSED(r);
14805 	assert(0);
14806 
14807 	return true;
14808 }
14809 
repairWrongDayOrHour(Rules & r)14810 bool ConstraintTwoActivitiesConsecutive::repairWrongDayOrHour(Rules& r)
14811 {
14812 	Q_UNUSED(r);
14813 	assert(0); //should check hasWrongDayOrHour, firstly
14814 
14815 	return true;
14816 }
14817 
14818 ////////////////////////////////////////////////////////////////////////////////////////////
14819 ////////////////////////////////////////////////////////////////////////////////////////////
14820 
ConstraintTwoActivitiesGrouped()14821 ConstraintTwoActivitiesGrouped::ConstraintTwoActivitiesGrouped()
14822 	: TimeConstraint()
14823 {
14824 	this->type = CONSTRAINT_TWO_ACTIVITIES_GROUPED;
14825 }
14826 
ConstraintTwoActivitiesGrouped(double wp,int firstActId,int secondActId)14827 ConstraintTwoActivitiesGrouped::ConstraintTwoActivitiesGrouped(double wp, int firstActId, int secondActId)
14828 	: TimeConstraint(wp)
14829 {
14830 	this->firstActivityId = firstActId;
14831 	this->secondActivityId=secondActId;
14832 	this->type = CONSTRAINT_TWO_ACTIVITIES_GROUPED;
14833 }
14834 
computeInternalStructure(QWidget * parent,Rules & r)14835 bool ConstraintTwoActivitiesGrouped::computeInternalStructure(QWidget* parent, Rules& r)
14836 {
14837 	/*Activity* act;
14838 	int i;
14839 	for(i=0; i<r.nInternalActivities; i++){
14840 		act=&r.internalActivitiesList[i];
14841 		if(act->id==this->firstActivityId)
14842 			break;
14843 	}*/
14844 
14845 	int i=r.activitiesHash.value(firstActivityId, r.nInternalActivities);
14846 
14847 	if(i==r.nInternalActivities){
14848 		//assert(0);
14849 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
14850 			tr("Following constraint is wrong (refers to inexistent activity ids):\n%1").arg(this->getDetailedDescription(r)));
14851 		return false;
14852 	}
14853 
14854 	this->firstActivityIndex=i;
14855 
14856 	////////
14857 
14858 	/*for(i=0; i<r.nInternalActivities; i++){
14859 		act=&r.internalActivitiesList[i];
14860 		if(act->id==this->secondActivityId)
14861 			break;
14862 	}*/
14863 
14864 	i=r.activitiesHash.value(secondActivityId, r.nInternalActivities);
14865 
14866 	if(i==r.nInternalActivities){
14867 		//assert(0);
14868 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
14869 			tr("Following constraint is wrong (refers to inexistent activity ids):\n%1").arg(this->getDetailedDescription(r)));
14870 		return false;
14871 	}
14872 
14873 	this->secondActivityIndex=i;
14874 
14875 	if(firstActivityIndex==secondActivityIndex){
14876 		//assert(0);
14877 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
14878 			tr("Following constraint is wrong (refers to same activities):\n%1").arg(this->getDetailedDescription(r)));
14879 		return false;
14880 	}
14881 	assert(firstActivityIndex!=secondActivityIndex);
14882 
14883 	return true;
14884 }
14885 
hasInactiveActivities(Rules & r)14886 bool ConstraintTwoActivitiesGrouped::hasInactiveActivities(Rules& r)
14887 {
14888 	if(r.inactiveActivities.contains(this->firstActivityId))
14889 		return true;
14890 	if(r.inactiveActivities.contains(this->secondActivityId))
14891 		return true;
14892 	return false;
14893 }
14894 
getXmlDescription(Rules & r)14895 QString ConstraintTwoActivitiesGrouped::getXmlDescription(Rules& r)
14896 {
14897 	Q_UNUSED(r);
14898 
14899 	QString s="<ConstraintTwoActivitiesGrouped>\n";
14900 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
14901 	s+="	<First_Activity_Id>"+CustomFETString::number(this->firstActivityId)+"</First_Activity_Id>\n";
14902 	s+="	<Second_Activity_Id>"+CustomFETString::number(this->secondActivityId)+"</Second_Activity_Id>\n";
14903 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
14904 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
14905 	s+="</ConstraintTwoActivitiesGrouped>\n";
14906 	return s;
14907 }
14908 
getDescription(Rules & r)14909 QString ConstraintTwoActivitiesGrouped::getDescription(Rules& r)
14910 {
14911 	Q_UNUSED(r);
14912 
14913 	QString begin=QString("");
14914 	if(!active)
14915 		begin="X - ";
14916 
14917 	QString end=QString("");
14918 	if(!comments.isEmpty())
14919 		end=", "+tr("C: %1", "Comments").arg(comments);
14920 
14921 	QString s;
14922 
14923 	s=tr("Two activities grouped:");
14924 	s+=" ";
14925 
14926 	s+=tr("first act. id: %1", "act.=activity").arg(this->firstActivityId);
14927 	s+=", ";
14928 	s+=tr("second act. id: %1", "act.=activity").arg(this->secondActivityId);
14929 	s+=", ";
14930 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
14931 
14932 	return begin+s+end;
14933 }
14934 
getDetailedDescription(Rules & r)14935 QString ConstraintTwoActivitiesGrouped::getDetailedDescription(Rules& r)
14936 {
14937 	QString s=tr("Time constraint");s+="\n";
14938 	s+=tr("Two activities grouped (the activities must be placed in the same day, "
14939 	 "one immediately following the other, in any order, possibly separated by breaks)"); s+="\n";
14940 
14941 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
14942 
14943 	s+=tr("First activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
14944 		.arg(this->firstActivityId)
14945 		.arg(getActivityDetailedDescription(r, this->firstActivityId));
14946 	s+="\n";
14947 
14948 	s+=tr("Second activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
14949 		.arg(this->secondActivityId)
14950 		.arg(getActivityDetailedDescription(r, this->secondActivityId));
14951 	s+="\n";
14952 
14953 	if(!active){
14954 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
14955 		s+="\n";
14956 	}
14957 	if(!comments.isEmpty()){
14958 		s+=tr("Comments=%1").arg(comments);
14959 		s+="\n";
14960 	}
14961 
14962 	return s;
14963 }
14964 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)14965 double ConstraintTwoActivitiesGrouped::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
14966 {
14967 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
14968 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
14969 		c.teachersMatrixReady=true;
14970 		c.subgroupsMatrixReady=true;
14971 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
14972 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
14973 
14974 		c.changedForMatrixCalculation=false;
14975 	}
14976 
14977 	int nbroken;
14978 
14979 	assert(r.internalStructureComputed);
14980 
14981 	nbroken=0;
14982 	if(c.times[this->firstActivityIndex]!=UNALLOCATED_TIME && c.times[this->secondActivityIndex]!=UNALLOCATED_TIME){
14983 		int fd=c.times[this->firstActivityIndex]%r.nDaysPerWeek; //the day when first activity was scheduled
14984 		int fh=c.times[this->firstActivityIndex]/r.nDaysPerWeek; //the hour
14985 		int sd=c.times[this->secondActivityIndex]%r.nDaysPerWeek; //the day when second activity was scheduled
14986 		int sh=c.times[this->secondActivityIndex]/r.nDaysPerWeek; //the hour
14987 
14988 		if(fd!=sd)
14989 			nbroken=1;
14990 		else if(fd==sd && fh+r.internalActivitiesList[this->firstActivityIndex].duration <= sh){
14991 			int h;
14992 			int d=fd;
14993 			assert(d==sd);
14994 			for(h=fh+r.internalActivitiesList[this->firstActivityIndex].duration; h<r.nHoursPerDay; h++)
14995 				if(!breakDayHour[d][h])
14996 					break;
14997 
14998 			assert(h<=sh);
14999 
15000 			if(h!=sh)
15001 				nbroken=1;
15002 		}
15003 		else if(fd==sd && sh+r.internalActivitiesList[this->secondActivityIndex].duration <= fh){
15004 			int h;
15005 			int d=sd;
15006 			assert(d==fd);
15007 			for(h=sh+r.internalActivitiesList[this->secondActivityIndex].duration; h<r.nHoursPerDay; h++)
15008 				if(!breakDayHour[d][h])
15009 					break;
15010 
15011 			assert(h<=fh);
15012 
15013 			if(h!=fh)
15014 				nbroken=1;
15015 		}
15016 		else
15017 			nbroken=1;
15018 	}
15019 
15020 	assert(nbroken==0 || nbroken==1);
15021 
15022 	if(conflictsString!=nullptr && nbroken>0){
15023 		QString s=tr("Time constraint two activities grouped broken for first activity with id=%1 (%2) and "
15024 		 "second activity with id=%3 (%4), increases conflicts total by %5", "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
15025 		 .arg(this->firstActivityId)
15026 		 .arg(getActivityDetailedDescription(r, this->firstActivityId))
15027 		 .arg(this->secondActivityId)
15028 		 .arg(getActivityDetailedDescription(r, this->secondActivityId))
15029 		 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*nbroken));
15030 
15031 		dl.append(s);
15032 		cl.append(weightPercentage/100*nbroken);
15033 
15034 		*conflictsString+= s+"\n";
15035 	}
15036 
15037 	if(weightPercentage==100)
15038 		assert(nbroken==0);
15039 	return nbroken * weightPercentage/100;
15040 }
15041 
isRelatedToActivity(Rules & r,Activity * a)15042 bool ConstraintTwoActivitiesGrouped::isRelatedToActivity(Rules& r, Activity* a)
15043 {
15044 	Q_UNUSED(r);
15045 
15046 	if(this->firstActivityId==a->id)
15047 		return true;
15048 	if(this->secondActivityId==a->id)
15049 		return true;
15050 	return false;
15051 }
15052 
isRelatedToTeacher(Teacher * t)15053 bool ConstraintTwoActivitiesGrouped::isRelatedToTeacher(Teacher* t)
15054 {
15055 	Q_UNUSED(t);
15056 
15057 	return false;
15058 }
15059 
isRelatedToSubject(Subject * s)15060 bool ConstraintTwoActivitiesGrouped::isRelatedToSubject(Subject* s)
15061 {
15062 	Q_UNUSED(s);
15063 
15064 	return false;
15065 }
15066 
isRelatedToActivityTag(ActivityTag * s)15067 bool ConstraintTwoActivitiesGrouped::isRelatedToActivityTag(ActivityTag* s)
15068 {
15069 	Q_UNUSED(s);
15070 
15071 	return false;
15072 }
15073 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)15074 bool ConstraintTwoActivitiesGrouped::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
15075 {
15076 	Q_UNUSED(r);
15077 	Q_UNUSED(s);
15078 
15079 	return false;
15080 }
15081 
hasWrongDayOrHour(Rules & r)15082 bool ConstraintTwoActivitiesGrouped::hasWrongDayOrHour(Rules& r)
15083 {
15084 	Q_UNUSED(r);
15085 	return false;
15086 }
15087 
canRepairWrongDayOrHour(Rules & r)15088 bool ConstraintTwoActivitiesGrouped::canRepairWrongDayOrHour(Rules& r)
15089 {
15090 	Q_UNUSED(r);
15091 	assert(0);
15092 
15093 	return true;
15094 }
15095 
repairWrongDayOrHour(Rules & r)15096 bool ConstraintTwoActivitiesGrouped::repairWrongDayOrHour(Rules& r)
15097 {
15098 	Q_UNUSED(r);
15099 	assert(0); //should check hasWrongDayOrHour, firstly
15100 
15101 	return true;
15102 }
15103 
15104 ////////////////////////////////////////////////////////////////////////////////////////////
15105 ////////////////////////////////////////////////////////////////////////////////////////////
15106 
ConstraintThreeActivitiesGrouped()15107 ConstraintThreeActivitiesGrouped::ConstraintThreeActivitiesGrouped()
15108 	: TimeConstraint()
15109 {
15110 	this->type = CONSTRAINT_THREE_ACTIVITIES_GROUPED;
15111 }
15112 
ConstraintThreeActivitiesGrouped(double wp,int firstActId,int secondActId,int thirdActId)15113 ConstraintThreeActivitiesGrouped::ConstraintThreeActivitiesGrouped(double wp, int firstActId, int secondActId, int thirdActId)
15114 	: TimeConstraint(wp)
15115 {
15116 	this->firstActivityId = firstActId;
15117 	this->secondActivityId=secondActId;
15118 	this->thirdActivityId=thirdActId;
15119 	this->type = CONSTRAINT_THREE_ACTIVITIES_GROUPED;
15120 }
15121 
computeInternalStructure(QWidget * parent,Rules & r)15122 bool ConstraintThreeActivitiesGrouped::computeInternalStructure(QWidget* parent, Rules& r)
15123 {
15124 	/*Activity* act;
15125 	int i;
15126 	for(i=0; i<r.nInternalActivities; i++){
15127 		act=&r.internalActivitiesList[i];
15128 		if(act->id==this->firstActivityId)
15129 			break;
15130 	}*/
15131 
15132 	int i=r.activitiesHash.value(firstActivityId, r.nInternalActivities);
15133 
15134 	if(i==r.nInternalActivities){
15135 		//assert(0);
15136 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
15137 			tr("Following constraint is wrong (refers to inexistent activity ids):\n%1").arg(this->getDetailedDescription(r)));
15138 		return false;
15139 	}
15140 
15141 	this->firstActivityIndex=i;
15142 
15143 	////////
15144 
15145 	/*for(i=0; i<r.nInternalActivities; i++){
15146 		act=&r.internalActivitiesList[i];
15147 		if(act->id==this->secondActivityId)
15148 			break;
15149 	}*/
15150 
15151 	i=r.activitiesHash.value(secondActivityId, r.nInternalActivities);
15152 
15153 	if(i==r.nInternalActivities){
15154 		//assert(0);
15155 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
15156 			tr("Following constraint is wrong (refers to inexistent activity ids):\n%1").arg(this->getDetailedDescription(r)));
15157 		return false;
15158 	}
15159 
15160 	this->secondActivityIndex=i;
15161 
15162 	////////
15163 
15164 	/*for(i=0; i<r.nInternalActivities; i++){
15165 		act=&r.internalActivitiesList[i];
15166 		if(act->id==this->thirdActivityId)
15167 			break;
15168 	}*/
15169 
15170 	i=r.activitiesHash.value(thirdActivityId, r.nInternalActivities);
15171 
15172 	if(i==r.nInternalActivities){
15173 		//assert(0);
15174 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
15175 			tr("Following constraint is wrong (refers to inexistent activity ids):\n%1").arg(this->getDetailedDescription(r)));
15176 		return false;
15177 	}
15178 
15179 	this->thirdActivityIndex=i;
15180 
15181 	if(firstActivityIndex==secondActivityIndex || firstActivityIndex==thirdActivityIndex || secondActivityIndex==thirdActivityIndex){
15182 		//assert(0);
15183 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
15184 			tr("Following constraint is wrong (refers to same activities):\n%1").arg(this->getDetailedDescription(r)));
15185 		return false;
15186 	}
15187 	assert(firstActivityIndex!=secondActivityIndex && firstActivityIndex!=thirdActivityIndex && secondActivityIndex!=thirdActivityIndex);
15188 
15189 	return true;
15190 }
15191 
hasInactiveActivities(Rules & r)15192 bool ConstraintThreeActivitiesGrouped::hasInactiveActivities(Rules& r)
15193 {
15194 	if(r.inactiveActivities.contains(this->firstActivityId))
15195 		return true;
15196 	if(r.inactiveActivities.contains(this->secondActivityId))
15197 		return true;
15198 	if(r.inactiveActivities.contains(this->thirdActivityId))
15199 		return true;
15200 	return false;
15201 }
15202 
getXmlDescription(Rules & r)15203 QString ConstraintThreeActivitiesGrouped::getXmlDescription(Rules& r)
15204 {
15205 	Q_UNUSED(r);
15206 
15207 	QString s="<ConstraintThreeActivitiesGrouped>\n";
15208 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
15209 	s+="	<First_Activity_Id>"+CustomFETString::number(this->firstActivityId)+"</First_Activity_Id>\n";
15210 	s+="	<Second_Activity_Id>"+CustomFETString::number(this->secondActivityId)+"</Second_Activity_Id>\n";
15211 	s+="	<Third_Activity_Id>"+CustomFETString::number(this->thirdActivityId)+"</Third_Activity_Id>\n";
15212 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
15213 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
15214 	s+="</ConstraintThreeActivitiesGrouped>\n";
15215 	return s;
15216 }
15217 
getDescription(Rules & r)15218 QString ConstraintThreeActivitiesGrouped::getDescription(Rules& r)
15219 {
15220 	Q_UNUSED(r);
15221 
15222 	QString begin=QString("");
15223 	if(!active)
15224 		begin="X - ";
15225 
15226 	QString end=QString("");
15227 	if(!comments.isEmpty())
15228 		end=", "+tr("C: %1", "Comments").arg(comments);
15229 
15230 	QString s;
15231 
15232 	s=tr("Three activities grouped:");
15233 	s+=" ";
15234 
15235 	s+=tr("first act. id: %1", "act.=activity").arg(this->firstActivityId);
15236 	s+=", ";
15237 	s+=tr("second act. id: %1", "act.=activity").arg(this->secondActivityId);
15238 	s+=", ";
15239 	s+=tr("third act. id: %1", "act.=activity").arg(this->thirdActivityId);
15240 	s+=", ";
15241 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
15242 
15243 	return begin+s+end;
15244 }
15245 
getDetailedDescription(Rules & r)15246 QString ConstraintThreeActivitiesGrouped::getDetailedDescription(Rules& r)
15247 {
15248 	QString s=tr("Time constraint");s+="\n";
15249 	s+=tr("Three activities grouped (the activities must be placed in the same day, "
15250 	 "one immediately following the other, as a block of three activities, in any order, possibly separated by breaks)"); s+="\n";
15251 
15252 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
15253 
15254 	s+=tr("First activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
15255 		.arg(this->firstActivityId)
15256 		.arg(getActivityDetailedDescription(r, this->firstActivityId));
15257 	s+="\n";
15258 
15259 	s+=tr("Second activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
15260 		.arg(this->secondActivityId)
15261 		.arg(getActivityDetailedDescription(r, this->secondActivityId));
15262 	s+="\n";
15263 
15264 	s+=tr("Third activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
15265 		.arg(this->thirdActivityId)
15266 		.arg(getActivityDetailedDescription(r, this->thirdActivityId));
15267 	s+="\n";
15268 
15269 	if(!active){
15270 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
15271 		s+="\n";
15272 	}
15273 	if(!comments.isEmpty()){
15274 		s+=tr("Comments=%1").arg(comments);
15275 		s+="\n";
15276 	}
15277 
15278 	return s;
15279 }
15280 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)15281 double ConstraintThreeActivitiesGrouped::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
15282 {
15283 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
15284 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
15285 		c.teachersMatrixReady=true;
15286 		c.subgroupsMatrixReady=true;
15287 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
15288 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
15289 
15290 		c.changedForMatrixCalculation=false;
15291 	}
15292 
15293 	int nbroken;
15294 
15295 	assert(r.internalStructureComputed);
15296 
15297 	nbroken=0;
15298 	if(c.times[this->firstActivityIndex]!=UNALLOCATED_TIME && c.times[this->secondActivityIndex]!=UNALLOCATED_TIME && c.times[this->thirdActivityIndex]!=UNALLOCATED_TIME){
15299 		int fd=c.times[this->firstActivityIndex]%r.nDaysPerWeek; //the day when first activity was scheduled
15300 		int fh=c.times[this->firstActivityIndex]/r.nDaysPerWeek; //the hour
15301 		int sd=c.times[this->secondActivityIndex]%r.nDaysPerWeek; //the day when second activity was scheduled
15302 		int sh=c.times[this->secondActivityIndex]/r.nDaysPerWeek; //the hour
15303 		int td=c.times[this->thirdActivityIndex]%r.nDaysPerWeek; //the day when third activity was scheduled
15304 		int th=c.times[this->thirdActivityIndex]/r.nDaysPerWeek; //the hour
15305 
15306 		if(!(fd==sd && fd==td))
15307 			nbroken=1;
15308 		else{
15309 			assert(fd==sd && fd==td && sd==td);
15310 			int a1=-1,a2=-1,a3=-1;
15311 			if(fh>=sh && fh>=th && sh>=th){
15312 				a1=thirdActivityIndex;
15313 				a2=secondActivityIndex;
15314 				a3=firstActivityIndex;
15315 				//out<<"321"<<endl;
15316 			}
15317 			else if(fh>=sh && fh>=th && th>=sh){
15318 				a1=secondActivityIndex;
15319 				a2=thirdActivityIndex;
15320 				a3=firstActivityIndex;
15321 				//out<<"231"<<endl;
15322 			}
15323 			else if(sh>=fh && sh>=th && fh>=th){
15324 				a1=thirdActivityIndex;
15325 				a2=firstActivityIndex;
15326 				a3=secondActivityIndex;
15327 				//out<<"312"<<endl;
15328 			}
15329 			else if(sh>=fh && sh>=th && th>=fh){
15330 				a1=firstActivityIndex;
15331 				a2=thirdActivityIndex;
15332 				a3=secondActivityIndex;
15333 				//out<<"132"<<endl;
15334 			}
15335 			else if(th>=fh && th>=sh && fh>=sh){
15336 				a1=secondActivityIndex;
15337 				a2=firstActivityIndex;
15338 				a3=thirdActivityIndex;
15339 				//out<<"213"<<endl;
15340 			}
15341 			else if(th>=fh && th>=sh && sh>=fh){
15342 				a1=firstActivityIndex;
15343 				a2=secondActivityIndex;
15344 				a3=thirdActivityIndex;
15345 				//out<<"123"<<endl;
15346 			}
15347 			else
15348 				assert(0);
15349 
15350 			int a1d=c.times[a1]%r.nDaysPerWeek; //the day for a1
15351 			int a1h=c.times[a1]/r.nDaysPerWeek; //the day for a1
15352 			int a1dur=r.internalActivitiesList[a1].duration;
15353 
15354 			int a2d=c.times[a2]%r.nDaysPerWeek; //the day for a2
15355 			int a2h=c.times[a2]/r.nDaysPerWeek; //the day for a2
15356 			int a2dur=r.internalActivitiesList[a2].duration;
15357 
15358 			int a3d=c.times[a3]%r.nDaysPerWeek; //the day for a3
15359 			int a3h=c.times[a3]/r.nDaysPerWeek; //the day for a3
15360 			//int a3dur=r.internalActivitiesList[a3].duration;
15361 
15362 			int hoursBetweenThem=-1;
15363 
15364 			assert(a1d==a2d && a1d==a3d);
15365 
15366 			if(a1h+a1dur<=a2h && a2h+a2dur<=a3h){
15367 				hoursBetweenThem=0;
15368 				for(int hh=a1h+a1dur; hh<a2h; hh++)
15369 					if(!breakDayHour[a1d][hh])
15370 						hoursBetweenThem++;
15371 
15372 				for(int hh=a2h+a2dur; hh<a3h; hh++)
15373 					if(!breakDayHour[a2d][hh])
15374 						hoursBetweenThem++;
15375 			}
15376 
15377 			if(hoursBetweenThem==0)
15378 				nbroken=0;
15379 			else
15380 				nbroken=1;
15381 		}
15382 	}
15383 
15384 	assert(nbroken==0 || nbroken==1);
15385 
15386 	if(conflictsString!=nullptr && nbroken>0){
15387 		QString s=tr("Time constraint three activities grouped broken for first activity with id=%1 (%2), "
15388 		 "second activity with id=%3 (%4) and third activity with id=%5 (%6), increases conflicts total by %7",
15389 		 "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr., %5 id, %6 det. descr.")
15390 		 .arg(this->firstActivityId)
15391 		 .arg(getActivityDetailedDescription(r, this->firstActivityId))
15392 		 .arg(this->secondActivityId)
15393 		 .arg(getActivityDetailedDescription(r, this->secondActivityId))
15394 		 .arg(this->thirdActivityId)
15395 		 .arg(getActivityDetailedDescription(r, this->thirdActivityId))
15396 		 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*nbroken));
15397 
15398 		dl.append(s);
15399 		cl.append(weightPercentage/100*nbroken);
15400 
15401 		*conflictsString+= s+"\n";
15402 	}
15403 
15404 	if(weightPercentage==100)
15405 		assert(nbroken==0);
15406 	return nbroken * weightPercentage/100;
15407 }
15408 
isRelatedToActivity(Rules & r,Activity * a)15409 bool ConstraintThreeActivitiesGrouped::isRelatedToActivity(Rules& r, Activity* a)
15410 {
15411 	Q_UNUSED(r);
15412 
15413 	if(this->firstActivityId==a->id)
15414 		return true;
15415 	if(this->secondActivityId==a->id)
15416 		return true;
15417 	if(this->thirdActivityId==a->id)
15418 		return true;
15419 	return false;
15420 }
15421 
isRelatedToTeacher(Teacher * t)15422 bool ConstraintThreeActivitiesGrouped::isRelatedToTeacher(Teacher* t)
15423 {
15424 	Q_UNUSED(t);
15425 
15426 	return false;
15427 }
15428 
isRelatedToSubject(Subject * s)15429 bool ConstraintThreeActivitiesGrouped::isRelatedToSubject(Subject* s)
15430 {
15431 	Q_UNUSED(s);
15432 
15433 	return false;
15434 }
15435 
isRelatedToActivityTag(ActivityTag * s)15436 bool ConstraintThreeActivitiesGrouped::isRelatedToActivityTag(ActivityTag* s)
15437 {
15438 	Q_UNUSED(s);
15439 
15440 	return false;
15441 }
15442 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)15443 bool ConstraintThreeActivitiesGrouped::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
15444 {
15445 	Q_UNUSED(r);
15446 	Q_UNUSED(s);
15447 
15448 	return false;
15449 }
15450 
hasWrongDayOrHour(Rules & r)15451 bool ConstraintThreeActivitiesGrouped::hasWrongDayOrHour(Rules& r)
15452 {
15453 	Q_UNUSED(r);
15454 	return false;
15455 }
15456 
canRepairWrongDayOrHour(Rules & r)15457 bool ConstraintThreeActivitiesGrouped::canRepairWrongDayOrHour(Rules& r)
15458 {
15459 	Q_UNUSED(r);
15460 	assert(0);
15461 
15462 	return true;
15463 }
15464 
repairWrongDayOrHour(Rules & r)15465 bool ConstraintThreeActivitiesGrouped::repairWrongDayOrHour(Rules& r)
15466 {
15467 	Q_UNUSED(r);
15468 	assert(0); //should check hasWrongDayOrHour, firstly
15469 
15470 	return true;
15471 }
15472 
15473 ////////////////////////////////////////////////////////////////////////////////////////////
15474 ////////////////////////////////////////////////////////////////////////////////////////////
15475 
ConstraintTwoActivitiesOrdered()15476 ConstraintTwoActivitiesOrdered::ConstraintTwoActivitiesOrdered()
15477 	: TimeConstraint()
15478 {
15479 	this->type = CONSTRAINT_TWO_ACTIVITIES_ORDERED;
15480 }
15481 
ConstraintTwoActivitiesOrdered(double wp,int firstActId,int secondActId)15482 ConstraintTwoActivitiesOrdered::ConstraintTwoActivitiesOrdered(double wp, int firstActId, int secondActId)
15483 	: TimeConstraint(wp)
15484 {
15485 	this->firstActivityId = firstActId;
15486 	this->secondActivityId=secondActId;
15487 	this->type = CONSTRAINT_TWO_ACTIVITIES_ORDERED;
15488 }
15489 
computeInternalStructure(QWidget * parent,Rules & r)15490 bool ConstraintTwoActivitiesOrdered::computeInternalStructure(QWidget* parent, Rules& r)
15491 {
15492 	/*Activity* act;
15493 	int i;
15494 	for(i=0; i<r.nInternalActivities; i++){
15495 		act=&r.internalActivitiesList[i];
15496 		if(act->id==this->firstActivityId)
15497 			break;
15498 	}*/
15499 
15500 	int i=r.activitiesHash.value(firstActivityId, r.nInternalActivities);
15501 
15502 	if(i==r.nInternalActivities){
15503 		//assert(0);
15504 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
15505 			tr("Following constraint is wrong (refers to inexistent activity ids):\n%1").arg(this->getDetailedDescription(r)));
15506 		return false;
15507 	}
15508 
15509 	this->firstActivityIndex=i;
15510 
15511 	////////
15512 
15513 	/*for(i=0; i<r.nInternalActivities; i++){
15514 		act=&r.internalActivitiesList[i];
15515 		if(act->id==this->secondActivityId)
15516 			break;
15517 	}*/
15518 
15519 	i=r.activitiesHash.value(secondActivityId, r.nInternalActivities);
15520 
15521 	if(i==r.nInternalActivities){
15522 		//assert(0);
15523 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
15524 			tr("Following constraint is wrong (refers to inexistent activity ids):\n%1").arg(this->getDetailedDescription(r)));
15525 		return false;
15526 	}
15527 
15528 	this->secondActivityIndex=i;
15529 
15530 	if(firstActivityIndex==secondActivityIndex){
15531 		//assert(0);
15532 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
15533 			tr("Following constraint is wrong (refers to same activities):\n%1").arg(this->getDetailedDescription(r)));
15534 		return false;
15535 	}
15536 	assert(firstActivityIndex!=secondActivityIndex);
15537 
15538 	return true;
15539 }
15540 
hasInactiveActivities(Rules & r)15541 bool ConstraintTwoActivitiesOrdered::hasInactiveActivities(Rules& r)
15542 {
15543 	if(r.inactiveActivities.contains(this->firstActivityId))
15544 		return true;
15545 	if(r.inactiveActivities.contains(this->secondActivityId))
15546 		return true;
15547 	return false;
15548 }
15549 
getXmlDescription(Rules & r)15550 QString ConstraintTwoActivitiesOrdered::getXmlDescription(Rules& r)
15551 {
15552 	Q_UNUSED(r);
15553 
15554 	QString s="<ConstraintTwoActivitiesOrdered>\n";
15555 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
15556 	s+="	<First_Activity_Id>"+CustomFETString::number(this->firstActivityId)+"</First_Activity_Id>\n";
15557 	s+="	<Second_Activity_Id>"+CustomFETString::number(this->secondActivityId)+"</Second_Activity_Id>\n";
15558 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
15559 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
15560 	s+="</ConstraintTwoActivitiesOrdered>\n";
15561 	return s;
15562 }
15563 
getDescription(Rules & r)15564 QString ConstraintTwoActivitiesOrdered::getDescription(Rules& r)
15565 {
15566 	Q_UNUSED(r);
15567 
15568 	QString begin=QString("");
15569 	if(!active)
15570 		begin="X - ";
15571 
15572 	QString end=QString("");
15573 	if(!comments.isEmpty())
15574 		end=", "+tr("C: %1", "Comments").arg(comments);
15575 
15576 	QString s;
15577 
15578 	s=tr("Two activities ordered:");
15579 	s+=" ";
15580 
15581 	s+=tr("first act. id: %1", "act.=activity").arg(this->firstActivityId);
15582 	s+=", ";
15583 	s+=tr("second act. id: %1", "act.=activity").arg(this->secondActivityId);
15584 	s+=", ";
15585 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
15586 
15587 	return begin+s+end;
15588 }
15589 
getDetailedDescription(Rules & r)15590 QString ConstraintTwoActivitiesOrdered::getDetailedDescription(Rules& r)
15591 {
15592 	QString s=tr("Time constraint");s+="\n";
15593 	s+=tr("Two activities ordered (the second activity must begin at any time in the week later than the first"
15594 	 " activity has finished)"); s+="\n";
15595 
15596 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
15597 
15598 	s+=tr("First activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
15599 		.arg(this->firstActivityId)
15600 		.arg(getActivityDetailedDescription(r, this->firstActivityId));
15601 	s+="\n";
15602 
15603 	s+=tr("Second activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
15604 		.arg(this->secondActivityId)
15605 		.arg(getActivityDetailedDescription(r, this->secondActivityId));
15606 	s+="\n";
15607 
15608 	if(!active){
15609 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
15610 		s+="\n";
15611 	}
15612 	if(!comments.isEmpty()){
15613 		s+=tr("Comments=%1").arg(comments);
15614 		s+="\n";
15615 	}
15616 
15617 	return s;
15618 }
15619 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)15620 double ConstraintTwoActivitiesOrdered::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
15621 {
15622 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
15623 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
15624 		c.teachersMatrixReady=true;
15625 		c.subgroupsMatrixReady=true;
15626 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
15627 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
15628 
15629 		c.changedForMatrixCalculation=false;
15630 	}
15631 
15632 	int nbroken;
15633 
15634 	assert(r.internalStructureComputed);
15635 
15636 	nbroken=0;
15637 	if(c.times[this->firstActivityIndex]!=UNALLOCATED_TIME && c.times[this->secondActivityIndex]!=UNALLOCATED_TIME){
15638 		int fd=c.times[this->firstActivityIndex]%r.nDaysPerWeek; //the day when first activity was scheduled
15639 		int fh=c.times[this->firstActivityIndex]/r.nDaysPerWeek
15640 		  + r.internalActivitiesList[this->firstActivityIndex].duration-1; //the end hour of first activity
15641 		int sd=c.times[this->secondActivityIndex]%r.nDaysPerWeek; //the day when second activity was scheduled
15642 		int sh=c.times[this->secondActivityIndex]/r.nDaysPerWeek; //the start hour of second activity
15643 
15644 		if(!(fd<sd || (fd==sd && fh<sh)))
15645 			nbroken=1;
15646 	}
15647 
15648 	assert(nbroken==0 || nbroken==1);
15649 
15650 	if(conflictsString!=nullptr && nbroken>0){
15651 		QString s=tr("Time constraint two activities ordered broken for first activity with id=%1 (%2) and "
15652 		 "second activity with id=%3 (%4), increases conflicts total by %5", "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
15653 		 .arg(this->firstActivityId)
15654 		 .arg(getActivityDetailedDescription(r, this->firstActivityId))
15655 		 .arg(this->secondActivityId)
15656 		 .arg(getActivityDetailedDescription(r, this->secondActivityId))
15657 		 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*nbroken));
15658 
15659 		dl.append(s);
15660 		cl.append(weightPercentage/100*nbroken);
15661 
15662 		*conflictsString+= s+"\n";
15663 	}
15664 
15665 	if(weightPercentage==100)
15666 		assert(nbroken==0);
15667 	return nbroken * weightPercentage/100;
15668 }
15669 
isRelatedToActivity(Rules & r,Activity * a)15670 bool ConstraintTwoActivitiesOrdered::isRelatedToActivity(Rules& r, Activity* a)
15671 {
15672 	Q_UNUSED(r);
15673 
15674 	if(this->firstActivityId==a->id)
15675 		return true;
15676 	if(this->secondActivityId==a->id)
15677 		return true;
15678 	return false;
15679 }
15680 
isRelatedToTeacher(Teacher * t)15681 bool ConstraintTwoActivitiesOrdered::isRelatedToTeacher(Teacher* t)
15682 {
15683 	Q_UNUSED(t);
15684 
15685 	return false;
15686 }
15687 
isRelatedToSubject(Subject * s)15688 bool ConstraintTwoActivitiesOrdered::isRelatedToSubject(Subject* s)
15689 {
15690 	Q_UNUSED(s);
15691 
15692 	return false;
15693 }
15694 
isRelatedToActivityTag(ActivityTag * s)15695 bool ConstraintTwoActivitiesOrdered::isRelatedToActivityTag(ActivityTag* s)
15696 {
15697 	Q_UNUSED(s);
15698 
15699 	return false;
15700 }
15701 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)15702 bool ConstraintTwoActivitiesOrdered::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
15703 {
15704 	Q_UNUSED(r);
15705 	Q_UNUSED(s);
15706 
15707 	return false;
15708 }
15709 
hasWrongDayOrHour(Rules & r)15710 bool ConstraintTwoActivitiesOrdered::hasWrongDayOrHour(Rules& r)
15711 {
15712 	Q_UNUSED(r);
15713 	return false;
15714 }
15715 
canRepairWrongDayOrHour(Rules & r)15716 bool ConstraintTwoActivitiesOrdered::canRepairWrongDayOrHour(Rules& r)
15717 {
15718 	Q_UNUSED(r);
15719 	assert(0);
15720 
15721 	return true;
15722 }
15723 
repairWrongDayOrHour(Rules & r)15724 bool ConstraintTwoActivitiesOrdered::repairWrongDayOrHour(Rules& r)
15725 {
15726 	Q_UNUSED(r);
15727 	assert(0); //should check hasWrongDayOrHour, firstly
15728 
15729 	return true;
15730 }
15731 
15732 ////////////////////////////////////////////////////////////////////////////////////////////
15733 ////////////////////////////////////////////////////////////////////////////////////////////
15734 
ConstraintTwoSetsOfActivitiesOrdered()15735 ConstraintTwoSetsOfActivitiesOrdered::ConstraintTwoSetsOfActivitiesOrdered()
15736 	: TimeConstraint()
15737 {
15738 	this->type = CONSTRAINT_TWO_SETS_OF_ACTIVITIES_ORDERED;
15739 }
15740 
ConstraintTwoSetsOfActivitiesOrdered(double wp,const QList<int> & firstActsIds,const QList<int> & secondActsIds)15741 ConstraintTwoSetsOfActivitiesOrdered::ConstraintTwoSetsOfActivitiesOrdered(double wp, const QList<int>& firstActsIds, const QList<int>& secondActsIds)
15742 	: TimeConstraint(wp)
15743 {
15744 	this->firstActivitiesIdsList = firstActsIds;
15745 	this->secondActivitiesIdsList=secondActsIds;
15746 	this->type = CONSTRAINT_TWO_SETS_OF_ACTIVITIES_ORDERED;
15747 }
15748 
computeInternalStructure(QWidget * parent,Rules & r)15749 bool ConstraintTwoSetsOfActivitiesOrdered::computeInternalStructure(QWidget* parent, Rules& r)
15750 {
15751 	QSet<int> sf;
15752 
15753 	this->firstActivitiesIndicesList.clear();
15754 	for(int firstActivityId : qAsConst(this->firstActivitiesIdsList)){
15755 		int i=r.activitiesHash.value(firstActivityId, r.nInternalActivities);
15756 
15757 		if(i<r.nInternalActivities){
15758 			this->firstActivitiesIndicesList.append(i);
15759 			sf.insert(i);
15760 		}
15761 	}
15762 
15763 	if(this->firstActivitiesIndicesList.isEmpty()){
15764 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
15765 			tr("Following constraint is wrong (its first set of activities is empty/incorrect). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
15766 		return false;
15767 	}
15768 
15769 	////////
15770 
15771 	bool intersect=false;
15772 	this->secondActivitiesIndicesList.clear();
15773 	for(int secondActivityId : qAsConst(this->secondActivitiesIdsList)){
15774 		int i=r.activitiesHash.value(secondActivityId, r.nInternalActivities);
15775 
15776 		if(i<r.nInternalActivities){
15777 			this->secondActivitiesIndicesList.append(i);
15778 			if(sf.contains(i)){
15779 				intersect=true;
15780 				break;
15781 			}
15782 		}
15783 	}
15784 
15785 	if(this->secondActivitiesIndicesList.isEmpty()){
15786 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
15787 			tr("Following constraint is wrong (its second set of activities is empty/incorrect). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
15788 		return false;
15789 	}
15790 
15791 	if(intersect){
15792 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
15793 			tr("Following constraint is wrong (the first set of activities has activities in common with the second set of activities). Please correct it:\n%1")
15794 			 .arg(this->getDetailedDescription(r)));
15795 		return false;
15796 	}
15797 
15798 	return true;
15799 }
15800 
hasInactiveActivities(Rules & r)15801 bool ConstraintTwoSetsOfActivitiesOrdered::hasInactiveActivities(Rules& r)
15802 {
15803 	bool okf=false;
15804 	for(int ai : this->firstActivitiesIdsList)
15805 		if(!r.inactiveActivities.contains(ai)){
15806 			okf=true;
15807 			break;
15808 		}
15809 
15810 	bool oks=false;
15811 	for(int ai : this->secondActivitiesIdsList)
15812 		if(!r.inactiveActivities.contains(ai)){
15813 			oks=true;
15814 			break;
15815 		}
15816 
15817 	if(!okf || !oks)
15818 		return true;
15819 	return false;
15820 }
15821 
getXmlDescription(Rules & r)15822 QString ConstraintTwoSetsOfActivitiesOrdered::getXmlDescription(Rules& r)
15823 {
15824 	Q_UNUSED(r);
15825 
15826 	QString s="<ConstraintTwoSetsOfActivitiesOrdered>\n";
15827 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
15828 	s+="	<First_Activities_Ids_Set>\n";
15829 	s+="		<Number_of_Activities>"+QString::number(this->firstActivitiesIdsList.count())+"</Number_of_Activities>\n";
15830 	for(int ai : qAsConst(this->firstActivitiesIdsList))
15831 		s+="		<Activity_Id>"+CustomFETString::number(ai)+"</Activity_Id>\n";
15832 	s+="	</First_Activities_Ids_Set>\n";
15833 	s+="	<Second_Activities_Ids_Set>\n";
15834 	s+="		<Number_of_Activities>"+QString::number(this->secondActivitiesIdsList.count())+"</Number_of_Activities>\n";
15835 	for(int ai : qAsConst(this->secondActivitiesIdsList))
15836 		s+="		<Activity_Id>"+CustomFETString::number(ai)+"</Activity_Id>\n";
15837 	s+="	</Second_Activities_Ids_Set>\n";
15838 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
15839 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
15840 	s+="</ConstraintTwoSetsOfActivitiesOrdered>\n";
15841 	return s;
15842 }
15843 
getDescription(Rules & r)15844 QString ConstraintTwoSetsOfActivitiesOrdered::getDescription(Rules& r)
15845 {
15846 	Q_UNUSED(r);
15847 
15848 	QString begin=QString("");
15849 	if(!active)
15850 		begin="X - ";
15851 
15852 	QString end=QString("");
15853 	if(!comments.isEmpty())
15854 		end=", "+tr("C: %1", "Comments").arg(comments);
15855 
15856 	QString s;
15857 
15858 	s=tr("Two sets of activities ordered:");
15859 	s+=" ";
15860 
15861 	s+=tr("first activities set:");
15862 	s+=" ";
15863 	s+=tr("NA:%1", "Number of activities").arg(this->firstActivitiesIdsList.count());
15864 	s+=", ";
15865 	for(int ai : qAsConst(this->firstActivitiesIdsList))
15866 		s+=tr("Id:%1").arg(ai)+", ";
15867 
15868 	s+=tr("second activities set:");
15869 	s+=" ";
15870 	s+=tr("NA:%1", "Number of activities").arg(this->secondActivitiesIdsList.count());
15871 	s+=", ";
15872 	for(int ai : qAsConst(this->secondActivitiesIdsList))
15873 		s+=tr("Id:%1").arg(ai)+", ";
15874 
15875 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
15876 
15877 	return begin+s+end;
15878 }
15879 
getDetailedDescription(Rules & r)15880 QString ConstraintTwoSetsOfActivitiesOrdered::getDetailedDescription(Rules& r)
15881 {
15882 	QString s=tr("Time constraint");s+="\n";
15883 	s+=tr("Two sets of activities ordered (each activity from the second set must begin at any time in the week later than each activity"
15884 	 " from the first set has finished)"); s+="\n";
15885 
15886 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
15887 
15888 	s+=tr("First activities set ids:");
15889 	s+="\n";
15890 	s+=tr("Number of activities=%1").arg(this->firstActivitiesIdsList.count());s+="\n";
15891 	for(int ai : qAsConst(this->firstActivitiesIdsList)){
15892 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
15893 			.arg(ai).arg(getActivityDetailedDescription(r, ai));
15894 		s+="\n";
15895 	}
15896 
15897 	s+=tr("Second activities set ids:");
15898 	s+="\n";
15899 	s+=tr("Number of activities=%1").arg(this->secondActivitiesIdsList.count());s+="\n";
15900 	for(int ai : qAsConst(this->secondActivitiesIdsList)){
15901 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
15902 			.arg(ai).arg(getActivityDetailedDescription(r, ai));
15903 		s+="\n";
15904 	}
15905 
15906 	if(!active){
15907 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
15908 		s+="\n";
15909 	}
15910 	if(!comments.isEmpty()){
15911 		s+=tr("Comments=%1").arg(comments);
15912 		s+="\n";
15913 	}
15914 
15915 	return s;
15916 }
15917 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)15918 double ConstraintTwoSetsOfActivitiesOrdered::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
15919 {
15920 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
15921 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
15922 		c.teachersMatrixReady=true;
15923 		c.subgroupsMatrixReady=true;
15924 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
15925 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
15926 
15927 		c.changedForMatrixCalculation=false;
15928 	}
15929 
15930 	int nbroken;
15931 
15932 	assert(r.internalStructureComputed);
15933 
15934 	int totalBroken=0;
15935 
15936 	for(int i=0; i<this->firstActivitiesIndicesList.count(); i++){
15937 		for(int j=0; j<this->secondActivitiesIndicesList.count(); j++){
15938 			nbroken=0;
15939 
15940 			int firstActivityIndex=this->firstActivitiesIndicesList[i];
15941 			int secondActivityIndex=this->secondActivitiesIndicesList[j];
15942 
15943 			if(c.times[firstActivityIndex]!=UNALLOCATED_TIME && c.times[secondActivityIndex]!=UNALLOCATED_TIME){
15944 				int fd=c.times[firstActivityIndex]%r.nDaysPerWeek; //the day when first activity was scheduled
15945 				int fh=c.times[firstActivityIndex]/r.nDaysPerWeek
15946 				  + r.internalActivitiesList[firstActivityIndex].duration-1; //the end hour of first activity
15947 				int sd=c.times[secondActivityIndex]%r.nDaysPerWeek; //the day when second activity was scheduled
15948 				int sh=c.times[secondActivityIndex]/r.nDaysPerWeek; //the start hour of second activity
15949 
15950 				if(!(fd<sd || (fd==sd && fh<sh)))
15951 					nbroken=1;
15952 			}
15953 
15954 			assert(nbroken==0 || nbroken==1);
15955 
15956 			totalBroken+=nbroken;
15957 
15958 			if(conflictsString!=nullptr && nbroken>0){
15959 				int firstActivityId=this->firstActivitiesIdsList[i];
15960 				int secondActivityId=this->secondActivitiesIdsList[j];
15961 
15962 				QString s=tr("Time constraint two sets of activities ordered broken for first activity with id=%1 (%2) and "
15963 				 "second activity with id=%3 (%4), increases conflicts total by %5", "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
15964 				 .arg(firstActivityId)
15965 				 .arg(getActivityDetailedDescription(r, firstActivityId))
15966 				 .arg(secondActivityId)
15967 				 .arg(getActivityDetailedDescription(r, secondActivityId))
15968 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*nbroken));
15969 
15970 				dl.append(s);
15971 				cl.append(weightPercentage/100*nbroken);
15972 
15973 				*conflictsString+= s+"\n";
15974 			}
15975 		}
15976 	}
15977 
15978 	if(weightPercentage==100)
15979 		assert(totalBroken==0);
15980 	return totalBroken * weightPercentage/100;
15981 }
15982 
removeUseless(Rules & r)15983 void ConstraintTwoSetsOfActivitiesOrdered::removeUseless(Rules& r)
15984 {
15985 	//remove the activitiesId which no longer exist (used after the deletion of an activity)
15986 
15987 	QList<int> tmpList;
15988 
15989 	tmpList.clear();
15990 	for(int ai : qAsConst(this->firstActivitiesIdsList)){
15991 		Activity* act=r.activitiesPointerHash.value(ai, nullptr);
15992 		if(act!=nullptr){
15993 			assert(act->id==ai);
15994 			tmpList.append(act->id);
15995 		}
15996 	}
15997 	this->firstActivitiesIdsList=tmpList;
15998 
15999 	tmpList.clear();
16000 	for(int ai : qAsConst(this->secondActivitiesIdsList)){
16001 		Activity* act=r.activitiesPointerHash.value(ai, nullptr);
16002 		if(act!=nullptr){
16003 			assert(act->id==ai);
16004 			tmpList.append(act->id);
16005 		}
16006 	}
16007 	this->secondActivitiesIdsList=tmpList;
16008 
16009 	r.internalStructureComputed=false;
16010 }
16011 
isRelatedToActivity(Rules & r,Activity * a)16012 bool ConstraintTwoSetsOfActivitiesOrdered::isRelatedToActivity(Rules& r, Activity* a)
16013 {
16014 	Q_UNUSED(r);
16015 
16016 	for(int ai : qAsConst(this->firstActivitiesIdsList))
16017 		if(ai==a->id)
16018 			return true;
16019 	for(int ai : qAsConst(this->secondActivitiesIdsList))
16020 		if(ai==a->id)
16021 			return true;
16022 	return false;
16023 }
16024 
isRelatedToTeacher(Teacher * t)16025 bool ConstraintTwoSetsOfActivitiesOrdered::isRelatedToTeacher(Teacher* t)
16026 {
16027 	Q_UNUSED(t);
16028 
16029 	return false;
16030 }
16031 
isRelatedToSubject(Subject * s)16032 bool ConstraintTwoSetsOfActivitiesOrdered::isRelatedToSubject(Subject* s)
16033 {
16034 	Q_UNUSED(s);
16035 
16036 	return false;
16037 }
16038 
isRelatedToActivityTag(ActivityTag * s)16039 bool ConstraintTwoSetsOfActivitiesOrdered::isRelatedToActivityTag(ActivityTag* s)
16040 {
16041 	Q_UNUSED(s);
16042 
16043 	return false;
16044 }
16045 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)16046 bool ConstraintTwoSetsOfActivitiesOrdered::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
16047 {
16048 	Q_UNUSED(r);
16049 	Q_UNUSED(s);
16050 
16051 	return false;
16052 }
16053 
hasWrongDayOrHour(Rules & r)16054 bool ConstraintTwoSetsOfActivitiesOrdered::hasWrongDayOrHour(Rules& r)
16055 {
16056 	Q_UNUSED(r);
16057 	return false;
16058 }
16059 
canRepairWrongDayOrHour(Rules & r)16060 bool ConstraintTwoSetsOfActivitiesOrdered::canRepairWrongDayOrHour(Rules& r)
16061 {
16062 	Q_UNUSED(r);
16063 	assert(0);
16064 
16065 	return true;
16066 }
16067 
repairWrongDayOrHour(Rules & r)16068 bool ConstraintTwoSetsOfActivitiesOrdered::repairWrongDayOrHour(Rules& r)
16069 {
16070 	Q_UNUSED(r);
16071 	assert(0); //should check hasWrongDayOrHour, firstly
16072 
16073 	return true;
16074 }
16075 
16076 ////////////////////////////////////////////////////////////////////////////////////////////
16077 ////////////////////////////////////////////////////////////////////////////////////////////
16078 
ConstraintTwoActivitiesOrderedIfSameDay()16079 ConstraintTwoActivitiesOrderedIfSameDay::ConstraintTwoActivitiesOrderedIfSameDay()
16080 	: TimeConstraint()
16081 {
16082 	this->type = CONSTRAINT_TWO_ACTIVITIES_ORDERED_IF_SAME_DAY;
16083 }
16084 
ConstraintTwoActivitiesOrderedIfSameDay(double wp,int firstActId,int secondActId)16085 ConstraintTwoActivitiesOrderedIfSameDay::ConstraintTwoActivitiesOrderedIfSameDay(double wp, int firstActId, int secondActId)
16086 	: TimeConstraint(wp)
16087 {
16088 	this->firstActivityId = firstActId;
16089 	this->secondActivityId=secondActId;
16090 	this->type = CONSTRAINT_TWO_ACTIVITIES_ORDERED_IF_SAME_DAY;
16091 }
16092 
computeInternalStructure(QWidget * parent,Rules & r)16093 bool ConstraintTwoActivitiesOrderedIfSameDay::computeInternalStructure(QWidget* parent, Rules& r)
16094 {
16095 	/*Activity* act;
16096 	int i;
16097 	for(i=0; i<r.nInternalActivities; i++){
16098 		act=&r.internalActivitiesList[i];
16099 		if(act->id==this->firstActivityId)
16100 			break;
16101 	}*/
16102 
16103 	int i=r.activitiesHash.value(firstActivityId, r.nInternalActivities);
16104 
16105 	if(i==r.nInternalActivities){
16106 		//assert(0);
16107 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
16108 			tr("Following constraint is wrong (refers to inexistent activity ids):\n%1").arg(this->getDetailedDescription(r)));
16109 		return false;
16110 	}
16111 
16112 	this->firstActivityIndex=i;
16113 
16114 	////////
16115 
16116 	/*for(i=0; i<r.nInternalActivities; i++){
16117 		act=&r.internalActivitiesList[i];
16118 		if(act->id==this->secondActivityId)
16119 			break;
16120 	}*/
16121 
16122 	i=r.activitiesHash.value(secondActivityId, r.nInternalActivities);
16123 
16124 	if(i==r.nInternalActivities){
16125 		//assert(0);
16126 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
16127 			tr("Following constraint is wrong (refers to inexistent activity ids):\n%1").arg(this->getDetailedDescription(r)));
16128 		return false;
16129 	}
16130 
16131 	this->secondActivityIndex=i;
16132 
16133 	if(firstActivityIndex==secondActivityIndex){
16134 		//assert(0);
16135 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
16136 			tr("Following constraint is wrong (refers to same activities):\n%1").arg(this->getDetailedDescription(r)));
16137 		return false;
16138 	}
16139 	assert(firstActivityIndex!=secondActivityIndex);
16140 
16141 	return true;
16142 }
16143 
hasInactiveActivities(Rules & r)16144 bool ConstraintTwoActivitiesOrderedIfSameDay::hasInactiveActivities(Rules& r)
16145 {
16146 	if(r.inactiveActivities.contains(this->firstActivityId))
16147 		return true;
16148 	if(r.inactiveActivities.contains(this->secondActivityId))
16149 		return true;
16150 	return false;
16151 }
16152 
getXmlDescription(Rules & r)16153 QString ConstraintTwoActivitiesOrderedIfSameDay::getXmlDescription(Rules& r)
16154 {
16155 	Q_UNUSED(r);
16156 
16157 	QString s="<ConstraintTwoActivitiesOrderedIfSameDay>\n";
16158 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
16159 	s+="	<First_Activity_Id>"+CustomFETString::number(this->firstActivityId)+"</First_Activity_Id>\n";
16160 	s+="	<Second_Activity_Id>"+CustomFETString::number(this->secondActivityId)+"</Second_Activity_Id>\n";
16161 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
16162 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
16163 	s+="</ConstraintTwoActivitiesOrderedIfSameDay>\n";
16164 	return s;
16165 }
16166 
getDescription(Rules & r)16167 QString ConstraintTwoActivitiesOrderedIfSameDay::getDescription(Rules& r)
16168 {
16169 	Q_UNUSED(r);
16170 
16171 	QString begin=QString("");
16172 	if(!active)
16173 		begin="X - ";
16174 
16175 	QString end=QString("");
16176 	if(!comments.isEmpty())
16177 		end=", "+tr("C: %1", "Comments").arg(comments);
16178 
16179 	QString s;
16180 
16181 	s=tr("Two activities ordered if same day:");
16182 	s+=" ";
16183 
16184 	s+=tr("first act. id: %1", "act.=activity").arg(this->firstActivityId);
16185 	s+=", ";
16186 	s+=tr("second act. id: %1", "act.=activity").arg(this->secondActivityId);
16187 	s+=", ";
16188 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
16189 
16190 	return begin+s+end;
16191 }
16192 
getDetailedDescription(Rules & r)16193 QString ConstraintTwoActivitiesOrderedIfSameDay::getDetailedDescription(Rules& r)
16194 {
16195 	QString s=tr("Time constraint");s+="\n";
16196 	s+=tr("Two activities are ordered if they are on the same day (the second activity must begin later than the first"
16197 	 " activity has finished if they are on the same day)");
16198 	s+="\n";
16199 
16200 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));
16201 	s+="\n";
16202 
16203 	s+=tr("First activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
16204 		.arg(this->firstActivityId)
16205 		.arg(getActivityDetailedDescription(r, this->firstActivityId));
16206 	s+="\n";
16207 
16208 	s+=tr("Second activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
16209 		.arg(this->secondActivityId)
16210 		.arg(getActivityDetailedDescription(r, this->secondActivityId));
16211 	s+="\n";
16212 
16213 	if(!active){
16214 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
16215 		s+="\n";
16216 	}
16217 	if(!comments.isEmpty()){
16218 		s+=tr("Comments=%1").arg(comments);
16219 		s+="\n";
16220 	}
16221 
16222 	return s;
16223 }
16224 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)16225 double ConstraintTwoActivitiesOrderedIfSameDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
16226 {
16227 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
16228 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
16229 		c.teachersMatrixReady=true;
16230 		c.subgroupsMatrixReady=true;
16231 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
16232 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
16233 
16234 		c.changedForMatrixCalculation=false;
16235 	}
16236 
16237 	int nbroken;
16238 
16239 	assert(r.internalStructureComputed);
16240 
16241 	nbroken=0;
16242 	if(c.times[this->firstActivityIndex]!=UNALLOCATED_TIME && c.times[this->secondActivityIndex]!=UNALLOCATED_TIME){
16243 		int fd=c.times[this->firstActivityIndex]%r.nDaysPerWeek; //the day when first activity was scheduled
16244 		int fh=c.times[this->firstActivityIndex]/r.nDaysPerWeek
16245 		  + r.internalActivitiesList[this->firstActivityIndex].duration-1; //the end hour of first activity
16246 		int sd=c.times[this->secondActivityIndex]%r.nDaysPerWeek; //the day when second activity was scheduled
16247 		int sh=c.times[this->secondActivityIndex]/r.nDaysPerWeek; //the start hour of second activity
16248 
16249 		if(!(fd!=sd || (fd==sd && fh<sh)))
16250 			nbroken=1;
16251 	}
16252 
16253 	assert(nbroken==0 || nbroken==1);
16254 
16255 	if(conflictsString!=nullptr && nbroken>0){
16256 		QString s=tr("Time constraint two activities ordered if on the same day broken for first activity with id=%1 (%2) and "
16257 		 "second activity with id=%3 (%4), increases conflicts total by %5", "%1 is the id, %2 is the detailed description of the activity, %3 id, %4 det. descr.")
16258 		 .arg(this->firstActivityId)
16259 		 .arg(getActivityDetailedDescription(r, this->firstActivityId))
16260 		 .arg(this->secondActivityId)
16261 		 .arg(getActivityDetailedDescription(r, this->secondActivityId))
16262 		 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*nbroken));
16263 
16264 		dl.append(s);
16265 		cl.append(weightPercentage/100*nbroken);
16266 
16267 		*conflictsString+= s+"\n";
16268 	}
16269 
16270 	if(weightPercentage==100)
16271 		assert(nbroken==0);
16272 	return nbroken * weightPercentage/100;
16273 }
16274 
isRelatedToActivity(Rules & r,Activity * a)16275 bool ConstraintTwoActivitiesOrderedIfSameDay::isRelatedToActivity(Rules& r, Activity* a)
16276 {
16277 	Q_UNUSED(r);
16278 
16279 	if(this->firstActivityId==a->id)
16280 		return true;
16281 	if(this->secondActivityId==a->id)
16282 		return true;
16283 	return false;
16284 }
16285 
isRelatedToTeacher(Teacher * t)16286 bool ConstraintTwoActivitiesOrderedIfSameDay::isRelatedToTeacher(Teacher* t)
16287 {
16288 	Q_UNUSED(t);
16289 
16290 	return false;
16291 }
16292 
isRelatedToSubject(Subject * s)16293 bool ConstraintTwoActivitiesOrderedIfSameDay::isRelatedToSubject(Subject* s)
16294 {
16295 	Q_UNUSED(s);
16296 
16297 	return false;
16298 }
16299 
isRelatedToActivityTag(ActivityTag * s)16300 bool ConstraintTwoActivitiesOrderedIfSameDay::isRelatedToActivityTag(ActivityTag* s)
16301 {
16302 	Q_UNUSED(s);
16303 
16304 	return false;
16305 }
16306 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)16307 bool ConstraintTwoActivitiesOrderedIfSameDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
16308 {
16309 	Q_UNUSED(r);
16310 	Q_UNUSED(s);
16311 
16312 	return false;
16313 }
16314 
hasWrongDayOrHour(Rules & r)16315 bool ConstraintTwoActivitiesOrderedIfSameDay::hasWrongDayOrHour(Rules& r)
16316 {
16317 	Q_UNUSED(r);
16318 	return false;
16319 }
16320 
canRepairWrongDayOrHour(Rules & r)16321 bool ConstraintTwoActivitiesOrderedIfSameDay::canRepairWrongDayOrHour(Rules& r)
16322 {
16323 	Q_UNUSED(r);
16324 	assert(0);
16325 
16326 	return true;
16327 }
16328 
repairWrongDayOrHour(Rules & r)16329 bool ConstraintTwoActivitiesOrderedIfSameDay::repairWrongDayOrHour(Rules& r)
16330 {
16331 	Q_UNUSED(r);
16332 	assert(0); //should check hasWrongDayOrHour, firstly
16333 
16334 	return true;
16335 }
16336 
16337 ////////////////////////////////////////////////////////////////////////////////////////////
16338 ////////////////////////////////////////////////////////////////////////////////////////////
16339 
ConstraintActivityEndsStudentsDay()16340 ConstraintActivityEndsStudentsDay::ConstraintActivityEndsStudentsDay()
16341 	: TimeConstraint()
16342 {
16343 	this->type = CONSTRAINT_ACTIVITY_ENDS_STUDENTS_DAY;
16344 }
16345 
ConstraintActivityEndsStudentsDay(double wp,int actId)16346 ConstraintActivityEndsStudentsDay::ConstraintActivityEndsStudentsDay(double wp, int actId)
16347 	: TimeConstraint(wp)
16348 {
16349 	this->activityId = actId;
16350 	this->type = CONSTRAINT_ACTIVITY_ENDS_STUDENTS_DAY;
16351 }
16352 
computeInternalStructure(QWidget * parent,Rules & r)16353 bool ConstraintActivityEndsStudentsDay::computeInternalStructure(QWidget* parent, Rules& r)
16354 {
16355 	/*Activity* act;
16356 	int i;
16357 	for(i=0; i<r.nInternalActivities; i++){
16358 		act=&r.internalActivitiesList[i];
16359 		if(act->id==this->activityId)
16360 			break;
16361 	}*/
16362 
16363 	int i=r.activitiesHash.value(activityId, r.nInternalActivities);
16364 
16365 	if(i==r.nInternalActivities){
16366 		//assert(0);
16367 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
16368 			tr("Following constraint is wrong (because it refers to invalid activity id). Please correct it (maybe removing it is a solution):\n%1").arg(this->getDetailedDescription(r)));
16369 		return false;
16370 	}
16371 
16372 	this->activityIndex=i;
16373 	return true;
16374 }
16375 
hasInactiveActivities(Rules & r)16376 bool ConstraintActivityEndsStudentsDay::hasInactiveActivities(Rules& r)
16377 {
16378 	if(r.inactiveActivities.contains(this->activityId))
16379 		return true;
16380 	return false;
16381 }
16382 
getXmlDescription(Rules & r)16383 QString ConstraintActivityEndsStudentsDay::getXmlDescription(Rules& r)
16384 {
16385 	Q_UNUSED(r);
16386 
16387 	QString s="<ConstraintActivityEndsStudentsDay>\n";
16388 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
16389 	s+="	<Activity_Id>"+CustomFETString::number(this->activityId)+"</Activity_Id>\n";
16390 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
16391 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
16392 	s+="</ConstraintActivityEndsStudentsDay>\n";
16393 	return s;
16394 }
16395 
getDescription(Rules & r)16396 QString ConstraintActivityEndsStudentsDay::getDescription(Rules& r)
16397 {
16398 	QString begin=QString("");
16399 	if(!active)
16400 		begin="X - ";
16401 
16402 	QString end=QString("");
16403 	if(!comments.isEmpty())
16404 		end=", "+tr("C: %1", "Comments").arg(comments);
16405 
16406 	QString s;
16407 	s+=tr("Act. id: %1 (%2) must end students' day",
16408 		"%1 is the id, %2 is the detailed description of the activity.")
16409 		.arg(this->activityId)
16410 		.arg(getActivityDetailedDescription(r, this->activityId));
16411 	s+=", ";
16412 
16413 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
16414 
16415 	return begin+s+end;
16416 }
16417 
getDetailedDescription(Rules & r)16418 QString ConstraintActivityEndsStudentsDay::getDetailedDescription(Rules& r)
16419 {
16420 	QString s=tr("Time constraint");s+="\n";
16421 	s+=tr("Activity must end students' day");s+="\n";
16422 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
16423 	s+=tr("Activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
16424 		.arg(this->activityId)
16425 		.arg(getActivityDetailedDescription(r, this->activityId));s+="\n";
16426 
16427 	if(!active){
16428 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
16429 		s+="\n";
16430 	}
16431 	if(!comments.isEmpty()){
16432 		s+=tr("Comments=%1").arg(comments);
16433 		s+="\n";
16434 	}
16435 
16436 	return s;
16437 }
16438 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)16439 double ConstraintActivityEndsStudentsDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
16440 {
16441 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
16442 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
16443 		c.teachersMatrixReady=true;
16444 		c.subgroupsMatrixReady=true;
16445 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
16446 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
16447 
16448 		c.changedForMatrixCalculation=false;
16449 	}
16450 
16451 	int nbroken;
16452 
16453 	assert(r.internalStructureComputed);
16454 
16455 	nbroken=0;
16456 	if(c.times[this->activityIndex]!=UNALLOCATED_TIME){
16457 		int d=c.times[this->activityIndex]%r.nDaysPerWeek; //the day when this activity was scheduled
16458 		int h=c.times[this->activityIndex]/r.nDaysPerWeek; //the hour
16459 
16460 		int i=this->activityIndex;
16461 		for(int j=0; j<r.internalActivitiesList[i].iSubgroupsList.count(); j++){
16462 			int sb=r.internalActivitiesList[i].iSubgroupsList.at(j);
16463 			for(int hh=h+r.internalActivitiesList[i].duration; hh<r.nHoursPerDay; hh++)
16464 				if(subgroupsMatrix[sb][d][hh]>0){
16465 					nbroken=1;
16466 					break;
16467 				}
16468 			if(nbroken)
16469 				break;
16470 		}
16471 	}
16472 
16473 	if(conflictsString!=nullptr && nbroken>0){
16474 		QString s=tr("Time constraint activity ends students' day broken for activity with id=%1 (%2), increases conflicts total by %3",
16475 		 "%1 is the id, %2 is the detailed description of the activity")
16476 		 .arg(this->activityId)
16477 		 .arg(getActivityDetailedDescription(r, this->activityId))
16478 		 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*nbroken));
16479 
16480 		dl.append(s);
16481 		cl.append(weightPercentage/100*nbroken);
16482 
16483 		*conflictsString+= s+"\n";
16484 	}
16485 
16486 	if(weightPercentage==100)
16487 		assert(nbroken==0);
16488 	return nbroken * weightPercentage/100;
16489 }
16490 
isRelatedToActivity(Rules & r,Activity * a)16491 bool ConstraintActivityEndsStudentsDay::isRelatedToActivity(Rules& r, Activity* a)
16492 {
16493 	Q_UNUSED(r);
16494 
16495 	if(this->activityId==a->id)
16496 		return true;
16497 	return false;
16498 }
16499 
isRelatedToTeacher(Teacher * t)16500 bool ConstraintActivityEndsStudentsDay::isRelatedToTeacher(Teacher* t)
16501 {
16502 	Q_UNUSED(t);
16503 
16504 	return false;
16505 }
16506 
isRelatedToSubject(Subject * s)16507 bool ConstraintActivityEndsStudentsDay::isRelatedToSubject(Subject* s)
16508 {
16509 	Q_UNUSED(s);
16510 
16511 	return false;
16512 }
16513 
isRelatedToActivityTag(ActivityTag * s)16514 bool ConstraintActivityEndsStudentsDay::isRelatedToActivityTag(ActivityTag* s)
16515 {
16516 	Q_UNUSED(s);
16517 
16518 	return false;
16519 }
16520 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)16521 bool ConstraintActivityEndsStudentsDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
16522 {
16523 	Q_UNUSED(r);
16524 	Q_UNUSED(s);
16525 
16526 	return false;
16527 }
16528 
hasWrongDayOrHour(Rules & r)16529 bool ConstraintActivityEndsStudentsDay::hasWrongDayOrHour(Rules& r)
16530 {
16531 	Q_UNUSED(r);
16532 	return false;
16533 }
16534 
canRepairWrongDayOrHour(Rules & r)16535 bool ConstraintActivityEndsStudentsDay::canRepairWrongDayOrHour(Rules& r)
16536 {
16537 	Q_UNUSED(r);
16538 	assert(0);
16539 
16540 	return true;
16541 }
16542 
repairWrongDayOrHour(Rules & r)16543 bool ConstraintActivityEndsStudentsDay::repairWrongDayOrHour(Rules& r)
16544 {
16545 	Q_UNUSED(r);
16546 	assert(0); //should check hasWrongDayOrHour, firstly
16547 
16548 	return true;
16549 }
16550 
16551 ///////////////////////////////////////////////////////////////////////////////////////////
16552 ///////////////////////////////////////////////////////////////////////////////////////////
16553 
ConstraintTeachersMinHoursDaily()16554 ConstraintTeachersMinHoursDaily::ConstraintTeachersMinHoursDaily()
16555 	: TimeConstraint()
16556 {
16557 	this->type=CONSTRAINT_TEACHERS_MIN_HOURS_DAILY;
16558 
16559 	this->allowEmptyDays=true;
16560 }
16561 
ConstraintTeachersMinHoursDaily(double wp,int minhours,bool _allowEmptyDays)16562 ConstraintTeachersMinHoursDaily::ConstraintTeachersMinHoursDaily(double wp, int minhours, bool _allowEmptyDays)
16563  : TimeConstraint(wp)
16564  {
16565 	assert(minhours>0);
16566 	this->minHoursDaily=minhours;
16567 
16568 	this->allowEmptyDays=_allowEmptyDays;
16569 
16570 	this->type=CONSTRAINT_TEACHERS_MIN_HOURS_DAILY;
16571 }
16572 
computeInternalStructure(QWidget * parent,Rules & r)16573 bool ConstraintTeachersMinHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
16574 {
16575 	Q_UNUSED(r);
16576 
16577 	if(allowEmptyDays==false){
16578 		QString s=tr("Cannot generate a timetable with a constraint teachers min hours daily with allow empty days=false. Please modify it,"
16579 			" so that it allows empty days. If you need a facility like that, please use constraint teachers min days per week");
16580 		s+="\n\n";
16581 		s+=tr("Constraint is:")+"\n"+this->getDetailedDescription(r);
16582 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"), s);
16583 
16584 		return false;
16585 	}
16586 
16587 	return true;
16588 }
16589 
hasInactiveActivities(Rules & r)16590 bool ConstraintTeachersMinHoursDaily::hasInactiveActivities(Rules& r)
16591 {
16592 	Q_UNUSED(r);
16593 	return false;
16594 }
16595 
getXmlDescription(Rules & r)16596 QString ConstraintTeachersMinHoursDaily::getXmlDescription(Rules& r)
16597 {
16598 	Q_UNUSED(r);
16599 
16600 	QString s="<ConstraintTeachersMinHoursDaily>\n";
16601 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
16602 	s+="	<Minimum_Hours_Daily>"+CustomFETString::number(this->minHoursDaily)+"</Minimum_Hours_Daily>\n";
16603 	if(this->allowEmptyDays)
16604 		s+="	<Allow_Empty_Days>true</Allow_Empty_Days>\n";
16605 	else
16606 		s+="	<Allow_Empty_Days>false</Allow_Empty_Days>\n";
16607 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
16608 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
16609 	s+="</ConstraintTeachersMinHoursDaily>\n";
16610 	return s;
16611 }
16612 
getDescription(Rules & r)16613 QString ConstraintTeachersMinHoursDaily::getDescription(Rules& r)
16614 {
16615 	Q_UNUSED(r);
16616 
16617 	QString begin=QString("");
16618 	if(!active)
16619 		begin="X - ";
16620 
16621 	QString end=QString("");
16622 	if(!comments.isEmpty())
16623 		end=", "+tr("C: %1", "Comments").arg(comments);
16624 
16625 	QString s;
16626 	s+=tr("Teachers min hours daily");s+=", ";
16627 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
16628 	s+=tr("mH:%1", "Min hours (daily)").arg(this->minHoursDaily);s+=", ";
16629 	s+=tr("AED:%1", "Allow empty days").arg(yesNoTranslated(this->allowEmptyDays));
16630 
16631 	return begin+s+end;
16632 }
16633 
getDetailedDescription(Rules & r)16634 QString ConstraintTeachersMinHoursDaily::getDetailedDescription(Rules& r)
16635 {
16636 	Q_UNUSED(r);
16637 
16638 	QString s=tr("Time constraint");s+="\n";
16639 	s+=tr("All teachers must respect the minimum number of hours daily"); s+="\n";
16640 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
16641 	s+=tr("Minimum hours daily=%1").arg(this->minHoursDaily);s+="\n";
16642 	s+=tr("Allow empty days=%1").arg(yesNoTranslated(this->allowEmptyDays));s+="\n";
16643 
16644 	if(!active){
16645 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
16646 		s+="\n";
16647 	}
16648 	if(!comments.isEmpty()){
16649 		s+=tr("Comments=%1").arg(comments);
16650 		s+="\n";
16651 	}
16652 
16653 	return s;
16654 }
16655 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)16656 double ConstraintTeachersMinHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
16657 {
16658 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
16659 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
16660 		c.teachersMatrixReady=true;
16661 		c.subgroupsMatrixReady=true;
16662 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
16663 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
16664 
16665 		c.changedForMatrixCalculation=false;
16666 	}
16667 
16668 	assert(this->allowEmptyDays==true);
16669 
16670 	int nbroken;
16671 
16672 	//without logging
16673 	if(conflictsString==nullptr){
16674 		nbroken=0;
16675 		for(int i=0; i<r.nInternalTeachers; i++){
16676 			for(int d=0; d<r.nDaysPerWeek; d++){
16677 				int n_hours_daily=0;
16678 				for(int h=0; h<r.nHoursPerDay; h++)
16679 					if(teachersMatrix[i][d][h]>0)
16680 						n_hours_daily++;
16681 
16682 				if(n_hours_daily>0 && n_hours_daily<this->minHoursDaily){
16683 					nbroken++;
16684 				}
16685 			}
16686 		}
16687 	}
16688 	//with logging
16689 	else{
16690 		nbroken=0;
16691 		for(int i=0; i<r.nInternalTeachers; i++){
16692 			for(int d=0; d<r.nDaysPerWeek; d++){
16693 				int n_hours_daily=0;
16694 				for(int h=0; h<r.nHoursPerDay; h++)
16695 					if(teachersMatrix[i][d][h]>0)
16696 						n_hours_daily++;
16697 
16698 				if(n_hours_daily>0 && n_hours_daily<this->minHoursDaily){
16699 					nbroken++;
16700 
16701 					if(conflictsString!=nullptr){
16702 						QString s=(tr("Time constraint teachers min %1 hours daily broken for teacher %2, on day %3, length=%4.")
16703 						 .arg(CustomFETString::number(this->minHoursDaily))
16704 						 .arg(r.internalTeachersList[i]->name)
16705 						 .arg(r.daysOfTheWeek[d])
16706 						 .arg(n_hours_daily)
16707 						 )
16708 						 +
16709 						 " "
16710 						 +
16711 						 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
16712 
16713 						dl.append(s);
16714 						cl.append(weightPercentage/100);
16715 
16716 						*conflictsString+= s+"\n";
16717 					}
16718 				}
16719 			}
16720 		}
16721 	}
16722 
16723 	if(c.nPlacedActivities==r.nInternalActivities)
16724 		if(weightPercentage==100)
16725 			assert(nbroken==0);
16726 	return weightPercentage/100 * nbroken;
16727 }
16728 
isRelatedToActivity(Rules & r,Activity * a)16729 bool ConstraintTeachersMinHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
16730 {
16731 	Q_UNUSED(a);
16732 	Q_UNUSED(r);
16733 
16734 	return false;
16735 }
16736 
isRelatedToTeacher(Teacher * t)16737 bool ConstraintTeachersMinHoursDaily::isRelatedToTeacher(Teacher* t)
16738 {
16739 	Q_UNUSED(t);
16740 
16741 	return true;
16742 }
16743 
isRelatedToSubject(Subject * s)16744 bool ConstraintTeachersMinHoursDaily::isRelatedToSubject(Subject* s)
16745 {
16746 	Q_UNUSED(s);
16747 
16748 	return false;
16749 }
16750 
isRelatedToActivityTag(ActivityTag * s)16751 bool ConstraintTeachersMinHoursDaily::isRelatedToActivityTag(ActivityTag* s)
16752 {
16753 	Q_UNUSED(s);
16754 
16755 	return false;
16756 }
16757 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)16758 bool ConstraintTeachersMinHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
16759 {
16760 	Q_UNUSED(r);
16761 	Q_UNUSED(s);
16762 
16763 	return false;
16764 }
16765 
hasWrongDayOrHour(Rules & r)16766 bool ConstraintTeachersMinHoursDaily::hasWrongDayOrHour(Rules& r)
16767 {
16768 	if(minHoursDaily>r.nHoursPerDay)
16769 		return true;
16770 
16771 	return false;
16772 }
16773 
canRepairWrongDayOrHour(Rules & r)16774 bool ConstraintTeachersMinHoursDaily::canRepairWrongDayOrHour(Rules& r)
16775 {
16776 	assert(hasWrongDayOrHour(r));
16777 
16778 	return true;
16779 }
16780 
repairWrongDayOrHour(Rules & r)16781 bool ConstraintTeachersMinHoursDaily::repairWrongDayOrHour(Rules& r)
16782 {
16783 	assert(hasWrongDayOrHour(r));
16784 
16785 	if(minHoursDaily>r.nHoursPerDay)
16786 		minHoursDaily=r.nHoursPerDay;
16787 
16788 	return true;
16789 }
16790 
16791 ///////////////////////////////////////////////////////////////////////////////////////////
16792 ///////////////////////////////////////////////////////////////////////////////////////////
16793 
ConstraintTeacherMinHoursDaily()16794 ConstraintTeacherMinHoursDaily::ConstraintTeacherMinHoursDaily()
16795 	: TimeConstraint()
16796 {
16797 	this->type=CONSTRAINT_TEACHER_MIN_HOURS_DAILY;
16798 
16799 	this->allowEmptyDays=true;
16800 }
16801 
ConstraintTeacherMinHoursDaily(double wp,int minhours,const QString & teacher,bool _allowEmptyDays)16802 ConstraintTeacherMinHoursDaily::ConstraintTeacherMinHoursDaily(double wp, int minhours, const QString& teacher, bool _allowEmptyDays)
16803  : TimeConstraint(wp)
16804  {
16805 	assert(minhours>0);
16806 	this->minHoursDaily=minhours;
16807 	this->teacherName=teacher;
16808 
16809 	this->allowEmptyDays=_allowEmptyDays;
16810 
16811 	this->type=CONSTRAINT_TEACHER_MIN_HOURS_DAILY;
16812 }
16813 
computeInternalStructure(QWidget * parent,Rules & r)16814 bool ConstraintTeacherMinHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
16815 {
16816 	//this->teacher_ID=r.searchTeacher(this->teacherName);
16817 	teacher_ID=r.teachersHash.value(teacherName, -1);
16818 	assert(this->teacher_ID>=0);
16819 
16820 	if(allowEmptyDays==false){
16821 		QString s=tr("Cannot generate a timetable with a constraint teacher min hours daily with allow empty days=false. Please modify it,"
16822 			" so that it allows empty days. If you need a facility like that, please use constraint teacher min days per week");
16823 		s+="\n\n";
16824 		s+=tr("Constraint is:")+"\n"+this->getDetailedDescription(r);
16825 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"), s);
16826 
16827 		return false;
16828 	}
16829 
16830 	return true;
16831 }
16832 
hasInactiveActivities(Rules & r)16833 bool ConstraintTeacherMinHoursDaily::hasInactiveActivities(Rules& r)
16834 {
16835 	Q_UNUSED(r);
16836 	return false;
16837 }
16838 
getXmlDescription(Rules & r)16839 QString ConstraintTeacherMinHoursDaily::getXmlDescription(Rules& r)
16840 {
16841 	Q_UNUSED(r);
16842 
16843 	QString s="<ConstraintTeacherMinHoursDaily>\n";
16844 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
16845 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
16846 	s+="	<Minimum_Hours_Daily>"+CustomFETString::number(this->minHoursDaily)+"</Minimum_Hours_Daily>\n";
16847 	if(this->allowEmptyDays)
16848 		s+="	<Allow_Empty_Days>true</Allow_Empty_Days>\n";
16849 	else
16850 		s+="	<Allow_Empty_Days>false</Allow_Empty_Days>\n";
16851 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
16852 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
16853 	s+="</ConstraintTeacherMinHoursDaily>\n";
16854 	return s;
16855 }
16856 
getDescription(Rules & r)16857 QString ConstraintTeacherMinHoursDaily::getDescription(Rules& r)
16858 {
16859 	Q_UNUSED(r);
16860 
16861 	QString begin=QString("");
16862 	if(!active)
16863 		begin="X - ";
16864 
16865 	QString end=QString("");
16866 	if(!comments.isEmpty())
16867 		end=", "+tr("C: %1", "Comments").arg(comments);
16868 
16869 	QString s;
16870 	s+=tr("Teacher min hours daily");s+=", ";
16871 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
16872 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
16873 	s+=tr("mH:%1", "Minimum hours (daily)").arg(this->minHoursDaily);s+=", ";
16874 	s+=tr("AED:%1", "Allow empty days").arg(yesNoTranslated(this->allowEmptyDays));
16875 
16876 	return begin+s+end;
16877 }
16878 
getDetailedDescription(Rules & r)16879 QString ConstraintTeacherMinHoursDaily::getDetailedDescription(Rules& r)
16880 {
16881 	Q_UNUSED(r);
16882 
16883 	QString s=tr("Time constraint");s+="\n";
16884 	s+=tr("A teacher must respect the minimum number of hours daily");s+="\n";
16885 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
16886 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
16887 	s+=tr("Minimum hours daily=%1").arg(this->minHoursDaily);s+="\n";
16888 	s+=tr("Allow empty days=%1").arg(yesNoTranslated(this->allowEmptyDays));s+="\n";
16889 
16890 	if(!active){
16891 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
16892 		s+="\n";
16893 	}
16894 	if(!comments.isEmpty()){
16895 		s+=tr("Comments=%1").arg(comments);
16896 		s+="\n";
16897 	}
16898 
16899 	return s;
16900 }
16901 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)16902 double ConstraintTeacherMinHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
16903 {
16904 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
16905 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
16906 		c.teachersMatrixReady=true;
16907 		c.subgroupsMatrixReady=true;
16908 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
16909 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
16910 
16911 		c.changedForMatrixCalculation=false;
16912 	}
16913 
16914 	assert(this->allowEmptyDays==true);
16915 
16916 	int nbroken;
16917 
16918 	//without logging
16919 	if(conflictsString==nullptr){
16920 		nbroken=0;
16921 		int i=this->teacher_ID;
16922 		for(int d=0; d<r.nDaysPerWeek; d++){
16923 			int n_hours_daily=0;
16924 			for(int h=0; h<r.nHoursPerDay; h++)
16925 				if(teachersMatrix[i][d][h]>0)
16926 					n_hours_daily++;
16927 
16928 			if(n_hours_daily>0 && n_hours_daily<this->minHoursDaily){
16929 				nbroken++;
16930 			}
16931 		}
16932 	}
16933 	//with logging
16934 	else{
16935 		nbroken=0;
16936 		int i=this->teacher_ID;
16937 		for(int d=0; d<r.nDaysPerWeek; d++){
16938 			int n_hours_daily=0;
16939 			for(int h=0; h<r.nHoursPerDay; h++)
16940 				if(teachersMatrix[i][d][h]>0)
16941 					n_hours_daily++;
16942 
16943 			if(n_hours_daily>0 && n_hours_daily<this->minHoursDaily){
16944 				nbroken++;
16945 
16946 				if(conflictsString!=nullptr){
16947 					QString s=(tr(
16948 					 "Time constraint teacher min %1 hours daily broken for teacher %2, on day %3, length=%4.")
16949 					 .arg(CustomFETString::number(this->minHoursDaily))
16950 					 .arg(r.internalTeachersList[i]->name)
16951 					 .arg(r.daysOfTheWeek[d])
16952 					 .arg(n_hours_daily)
16953 					 )
16954 					 +" "
16955 					 +
16956 					 tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100));
16957 
16958 					dl.append(s);
16959 					cl.append(weightPercentage/100);
16960 
16961 					*conflictsString+= s+"\n";
16962 				}
16963 			}
16964 		}
16965 	}
16966 
16967 	if(c.nPlacedActivities==r.nInternalActivities)
16968 		if(weightPercentage==100)
16969 			assert(nbroken==0);
16970 
16971 	return weightPercentage/100 * nbroken;
16972 }
16973 
isRelatedToActivity(Rules & r,Activity * a)16974 bool ConstraintTeacherMinHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
16975 {
16976 	Q_UNUSED(r);
16977 	Q_UNUSED(a);
16978 
16979 	return false;
16980 }
16981 
isRelatedToTeacher(Teacher * t)16982 bool ConstraintTeacherMinHoursDaily::isRelatedToTeacher(Teacher* t)
16983 {
16984 	if(this->teacherName==t->name)
16985 		return true;
16986 	return false;
16987 }
16988 
isRelatedToSubject(Subject * s)16989 bool ConstraintTeacherMinHoursDaily::isRelatedToSubject(Subject* s)
16990 {
16991 	Q_UNUSED(s);
16992 
16993 	return false;
16994 }
16995 
isRelatedToActivityTag(ActivityTag * s)16996 bool ConstraintTeacherMinHoursDaily::isRelatedToActivityTag(ActivityTag* s)
16997 {
16998 	Q_UNUSED(s);
16999 
17000 	return false;
17001 }
17002 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)17003 bool ConstraintTeacherMinHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
17004 {
17005 	Q_UNUSED(r);
17006 	Q_UNUSED(s);
17007 
17008 	return false;
17009 }
17010 
hasWrongDayOrHour(Rules & r)17011 bool ConstraintTeacherMinHoursDaily::hasWrongDayOrHour(Rules& r)
17012 {
17013 	if(minHoursDaily>r.nHoursPerDay)
17014 		return true;
17015 
17016 	return false;
17017 }
17018 
canRepairWrongDayOrHour(Rules & r)17019 bool ConstraintTeacherMinHoursDaily::canRepairWrongDayOrHour(Rules& r)
17020 {
17021 	assert(hasWrongDayOrHour(r));
17022 
17023 	return true;
17024 }
17025 
repairWrongDayOrHour(Rules & r)17026 bool ConstraintTeacherMinHoursDaily::repairWrongDayOrHour(Rules& r)
17027 {
17028 	assert(hasWrongDayOrHour(r));
17029 
17030 	if(minHoursDaily>r.nHoursPerDay)
17031 		minHoursDaily=r.nHoursPerDay;
17032 
17033 	return true;
17034 }
17035 
17036 ///////////////////////////////////////////////////////////////////////////////////////////
17037 ///////////////////////////////////////////////////////////////////////////////////////////
17038 
ConstraintTeacherMinDaysPerWeek()17039 ConstraintTeacherMinDaysPerWeek::ConstraintTeacherMinDaysPerWeek()
17040 	: TimeConstraint()
17041 {
17042 	this->type=CONSTRAINT_TEACHER_MIN_DAYS_PER_WEEK;
17043 }
17044 
ConstraintTeacherMinDaysPerWeek(double wp,int mindays,const QString & teacher)17045 ConstraintTeacherMinDaysPerWeek::ConstraintTeacherMinDaysPerWeek(double wp, int mindays, const QString& teacher)
17046  : TimeConstraint(wp)
17047  {
17048 	assert(mindays>0);
17049 	this->minDaysPerWeek=mindays;
17050 	this->teacherName=teacher;
17051 
17052 	this->type=CONSTRAINT_TEACHER_MIN_DAYS_PER_WEEK;
17053 }
17054 
computeInternalStructure(QWidget * parent,Rules & r)17055 bool ConstraintTeacherMinDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
17056 {
17057 	Q_UNUSED(parent);
17058 
17059 	//this->teacher_ID=r.searchTeacher(this->teacherName);
17060 	teacher_ID=r.teachersHash.value(teacherName, -1);
17061 	assert(this->teacher_ID>=0);
17062 	return true;
17063 }
17064 
hasInactiveActivities(Rules & r)17065 bool ConstraintTeacherMinDaysPerWeek::hasInactiveActivities(Rules& r)
17066 {
17067 	Q_UNUSED(r);
17068 	return false;
17069 }
17070 
getXmlDescription(Rules & r)17071 QString ConstraintTeacherMinDaysPerWeek::getXmlDescription(Rules& r)
17072 {
17073 	Q_UNUSED(r);
17074 
17075 	QString s="<ConstraintTeacherMinDaysPerWeek>\n";
17076 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
17077 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
17078 	s+="	<Minimum_Days_Per_Week>"+CustomFETString::number(this->minDaysPerWeek)+"</Minimum_Days_Per_Week>\n";
17079 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
17080 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
17081 	s+="</ConstraintTeacherMinDaysPerWeek>\n";
17082 	return s;
17083 }
17084 
getDescription(Rules & r)17085 QString ConstraintTeacherMinDaysPerWeek::getDescription(Rules& r)
17086 {
17087 	Q_UNUSED(r);
17088 
17089 	QString begin=QString("");
17090 	if(!active)
17091 		begin="X - ";
17092 
17093 	QString end=QString("");
17094 	if(!comments.isEmpty())
17095 		end=", "+tr("C: %1", "Comments").arg(comments);
17096 
17097 	QString s;
17098 	s+=tr("Teacher min days per week");s+=", ";
17099 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
17100 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
17101 	s+=tr("mD:%1", "Minimum days per week").arg(this->minDaysPerWeek);
17102 
17103 	return begin+s+end;
17104 }
17105 
getDetailedDescription(Rules & r)17106 QString ConstraintTeacherMinDaysPerWeek::getDetailedDescription(Rules& r)
17107 {
17108 	Q_UNUSED(r);
17109 
17110 	QString s=tr("Time constraint");s+="\n";
17111 	s+=tr("A teacher must respect the minimum number of days per week");s+="\n";
17112 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
17113 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
17114 	s+=tr("Minimum days per week=%1").arg(this->minDaysPerWeek);s+="\n";
17115 
17116 	if(!active){
17117 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
17118 		s+="\n";
17119 	}
17120 	if(!comments.isEmpty()){
17121 		s+=tr("Comments=%1").arg(comments);
17122 		s+="\n";
17123 	}
17124 
17125 	return s;
17126 }
17127 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)17128 double ConstraintTeacherMinDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
17129 {
17130 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
17131 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
17132 		c.teachersMatrixReady=true;
17133 		c.subgroupsMatrixReady=true;
17134 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
17135 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
17136 
17137 		c.changedForMatrixCalculation=false;
17138 	}
17139 
17140 	int nbroken;
17141 
17142 	nbroken=0;
17143 	int i=this->teacher_ID;
17144 	int nd=0;
17145 	for(int d=0; d<r.nDaysPerWeek; d++){
17146 		for(int h=0; h<r.nHoursPerDay; h++){
17147 			if(teachersMatrix[i][d][h]>0){
17148 				nd++;
17149 				break;
17150 			}
17151 		}
17152 	}
17153 
17154 	if(nd<this->minDaysPerWeek){
17155 		nbroken+=this->minDaysPerWeek-nd;
17156 
17157 		if(conflictsString!=nullptr){
17158 			QString s=(tr(
17159 			 "Time constraint teacher min %1 days per week broken for teacher %2.")
17160 			 .arg(CustomFETString::number(this->minDaysPerWeek))
17161 			 .arg(r.internalTeachersList[i]->name)
17162 			 )
17163 			 +" "
17164 			 +
17165 			 tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(double(nbroken)*weightPercentage/100));
17166 
17167 			dl.append(s);
17168 			cl.append(double(nbroken)*weightPercentage/100);
17169 
17170 			*conflictsString+= s+"\n";
17171 		}
17172 	}
17173 
17174 	if(c.nPlacedActivities==r.nInternalActivities)
17175 		if(weightPercentage==100)
17176 			assert(nbroken==0);
17177 
17178 	return weightPercentage/100 * nbroken;
17179 }
17180 
isRelatedToActivity(Rules & r,Activity * a)17181 bool ConstraintTeacherMinDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
17182 {
17183 	Q_UNUSED(r);
17184 	Q_UNUSED(a);
17185 
17186 	return false;
17187 }
17188 
isRelatedToTeacher(Teacher * t)17189 bool ConstraintTeacherMinDaysPerWeek::isRelatedToTeacher(Teacher* t)
17190 {
17191 	if(this->teacherName==t->name)
17192 		return true;
17193 	return false;
17194 }
17195 
isRelatedToSubject(Subject * s)17196 bool ConstraintTeacherMinDaysPerWeek::isRelatedToSubject(Subject* s)
17197 {
17198 	Q_UNUSED(s);
17199 
17200 	return false;
17201 }
17202 
isRelatedToActivityTag(ActivityTag * s)17203 bool ConstraintTeacherMinDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
17204 {
17205 	Q_UNUSED(s);
17206 
17207 	return false;
17208 }
17209 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)17210 bool ConstraintTeacherMinDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
17211 {
17212 	Q_UNUSED(r);
17213 	Q_UNUSED(s);
17214 
17215 	return false;
17216 }
17217 
hasWrongDayOrHour(Rules & r)17218 bool ConstraintTeacherMinDaysPerWeek::hasWrongDayOrHour(Rules& r)
17219 {
17220 	if(minDaysPerWeek>r.nDaysPerWeek)
17221 		return true;
17222 
17223 	return false;
17224 }
17225 
canRepairWrongDayOrHour(Rules & r)17226 bool ConstraintTeacherMinDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
17227 {
17228 	assert(hasWrongDayOrHour(r));
17229 
17230 	return true;
17231 }
17232 
repairWrongDayOrHour(Rules & r)17233 bool ConstraintTeacherMinDaysPerWeek::repairWrongDayOrHour(Rules& r)
17234 {
17235 	assert(hasWrongDayOrHour(r));
17236 
17237 	if(minDaysPerWeek>r.nDaysPerWeek)
17238 		minDaysPerWeek=r.nDaysPerWeek;
17239 
17240 	return true;
17241 }
17242 
17243 ///////////////////////////////////////////////////////////////////////////////////////////
17244 ///////////////////////////////////////////////////////////////////////////////////////////
17245 
ConstraintTeachersMinDaysPerWeek()17246 ConstraintTeachersMinDaysPerWeek::ConstraintTeachersMinDaysPerWeek()
17247 	: TimeConstraint()
17248 {
17249 	this->type=CONSTRAINT_TEACHERS_MIN_DAYS_PER_WEEK;
17250 }
17251 
ConstraintTeachersMinDaysPerWeek(double wp,int mindays)17252 ConstraintTeachersMinDaysPerWeek::ConstraintTeachersMinDaysPerWeek(double wp, int mindays)
17253  : TimeConstraint(wp)
17254  {
17255 	assert(mindays>0);
17256 	this->minDaysPerWeek=mindays;
17257 
17258 	this->type=CONSTRAINT_TEACHERS_MIN_DAYS_PER_WEEK;
17259 }
17260 
computeInternalStructure(QWidget * parent,Rules & r)17261 bool ConstraintTeachersMinDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
17262 {
17263 	Q_UNUSED(parent);
17264 	Q_UNUSED(r);
17265 
17266 	return true;
17267 }
17268 
hasInactiveActivities(Rules & r)17269 bool ConstraintTeachersMinDaysPerWeek::hasInactiveActivities(Rules& r)
17270 {
17271 	Q_UNUSED(r);
17272 	return false;
17273 }
17274 
getXmlDescription(Rules & r)17275 QString ConstraintTeachersMinDaysPerWeek::getXmlDescription(Rules& r)
17276 {
17277 	Q_UNUSED(r);
17278 
17279 	QString s="<ConstraintTeachersMinDaysPerWeek>\n";
17280 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
17281 	s+="	<Minimum_Days_Per_Week>"+CustomFETString::number(this->minDaysPerWeek)+"</Minimum_Days_Per_Week>\n";
17282 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
17283 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
17284 	s+="</ConstraintTeachersMinDaysPerWeek>\n";
17285 	return s;
17286 }
17287 
getDescription(Rules & r)17288 QString ConstraintTeachersMinDaysPerWeek::getDescription(Rules& r)
17289 {
17290 	Q_UNUSED(r);
17291 
17292 	QString begin=QString("");
17293 	if(!active)
17294 		begin="X - ";
17295 
17296 	QString end=QString("");
17297 	if(!comments.isEmpty())
17298 		end=", "+tr("C: %1", "Comments").arg(comments);
17299 
17300 	QString s;
17301 	s+=tr("Teachers min days per week");s+=", ";
17302 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
17303 	s+=tr("mD:%1", "Minimum days per week").arg(this->minDaysPerWeek);
17304 
17305 	return begin+s+end;
17306 }
17307 
getDetailedDescription(Rules & r)17308 QString ConstraintTeachersMinDaysPerWeek::getDetailedDescription(Rules& r)
17309 {
17310 	Q_UNUSED(r);
17311 
17312 	QString s=tr("Time constraint");s+="\n";
17313 	s+=tr("All teachers must respect the minimum number of days per week");s+="\n";
17314 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
17315 	s+=tr("Minimum days per week=%1").arg(this->minDaysPerWeek);s+="\n";
17316 
17317 	if(!active){
17318 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
17319 		s+="\n";
17320 	}
17321 	if(!comments.isEmpty()){
17322 		s+=tr("Comments=%1").arg(comments);
17323 		s+="\n";
17324 	}
17325 
17326 	return s;
17327 }
17328 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)17329 double ConstraintTeachersMinDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
17330 {
17331 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
17332 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
17333 		c.teachersMatrixReady=true;
17334 		c.subgroupsMatrixReady=true;
17335 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
17336 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
17337 
17338 		c.changedForMatrixCalculation=false;
17339 	}
17340 
17341 	int nbrokentotal=0;
17342 	for(int i=0; i<r.nInternalTeachers; i++){
17343 		int nbroken;
17344 
17345 		nbroken=0;
17346 		//int i=this->teacher_ID;
17347 		int nd=0;
17348 		for(int d=0; d<r.nDaysPerWeek; d++){
17349 			for(int h=0; h<r.nHoursPerDay; h++){
17350 				if(teachersMatrix[i][d][h]>0){
17351 					nd++;
17352 					break;
17353 				}
17354 			}
17355 		}
17356 
17357 		if(nd<this->minDaysPerWeek){
17358 			nbroken+=this->minDaysPerWeek-nd;
17359 			nbrokentotal+=nbroken;
17360 
17361 			if(conflictsString!=nullptr){
17362 				QString s=(tr(
17363 				 "Time constraint teachers min %1 days per week broken for teacher %2.")
17364 				 .arg(CustomFETString::number(this->minDaysPerWeek))
17365 				 .arg(r.internalTeachersList[i]->name)
17366 				 )
17367 				 +" "
17368 				 +
17369 				 tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(double(nbroken)*weightPercentage/100));
17370 
17371 				dl.append(s);
17372 				cl.append(double(nbroken)*weightPercentage/100);
17373 
17374 				*conflictsString+= s+"\n";
17375 			}
17376 		}
17377 	}
17378 
17379 	if(c.nPlacedActivities==r.nInternalActivities)
17380 		if(weightPercentage==100)
17381 			assert(nbrokentotal==0);
17382 
17383 	return weightPercentage/100 * nbrokentotal;
17384 }
17385 
isRelatedToActivity(Rules & r,Activity * a)17386 bool ConstraintTeachersMinDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
17387 {
17388 	Q_UNUSED(r);
17389 	Q_UNUSED(a);
17390 
17391 	return false;
17392 }
17393 
isRelatedToTeacher(Teacher * t)17394 bool ConstraintTeachersMinDaysPerWeek::isRelatedToTeacher(Teacher* t)
17395 {
17396 	Q_UNUSED(t);
17397 	return true;
17398 }
17399 
isRelatedToSubject(Subject * s)17400 bool ConstraintTeachersMinDaysPerWeek::isRelatedToSubject(Subject* s)
17401 {
17402 	Q_UNUSED(s);
17403 
17404 	return false;
17405 }
17406 
isRelatedToActivityTag(ActivityTag * s)17407 bool ConstraintTeachersMinDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
17408 {
17409 	Q_UNUSED(s);
17410 
17411 	return false;
17412 }
17413 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)17414 bool ConstraintTeachersMinDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
17415 {
17416 	Q_UNUSED(r);
17417 	Q_UNUSED(s);
17418 
17419 	return false;
17420 }
17421 
hasWrongDayOrHour(Rules & r)17422 bool ConstraintTeachersMinDaysPerWeek::hasWrongDayOrHour(Rules& r)
17423 {
17424 	if(minDaysPerWeek>r.nDaysPerWeek)
17425 		return true;
17426 
17427 	return false;
17428 }
17429 
canRepairWrongDayOrHour(Rules & r)17430 bool ConstraintTeachersMinDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
17431 {
17432 	assert(hasWrongDayOrHour(r));
17433 
17434 	return true;
17435 }
17436 
repairWrongDayOrHour(Rules & r)17437 bool ConstraintTeachersMinDaysPerWeek::repairWrongDayOrHour(Rules& r)
17438 {
17439 	assert(hasWrongDayOrHour(r));
17440 
17441 	if(minDaysPerWeek>r.nDaysPerWeek)
17442 		minDaysPerWeek=r.nDaysPerWeek;
17443 
17444 	return true;
17445 }
17446 
17447 ///////////////////////////////////////////////////////////////////////////////////////////
17448 ///////////////////////////////////////////////////////////////////////////////////////////
17449 
ConstraintTeacherIntervalMaxDaysPerWeek()17450 ConstraintTeacherIntervalMaxDaysPerWeek::ConstraintTeacherIntervalMaxDaysPerWeek()
17451 	: TimeConstraint()
17452 {
17453 	this->type=CONSTRAINT_TEACHER_INTERVAL_MAX_DAYS_PER_WEEK;
17454 }
17455 
ConstraintTeacherIntervalMaxDaysPerWeek(double wp,int maxnd,const QString & tn,int sh,int eh)17456 ConstraintTeacherIntervalMaxDaysPerWeek::ConstraintTeacherIntervalMaxDaysPerWeek(double wp, int maxnd, const QString& tn, int sh, int eh)
17457 	 : TimeConstraint(wp)
17458 {
17459 	this->teacherName = tn;
17460 	this->maxDaysPerWeek=maxnd;
17461 	this->type=CONSTRAINT_TEACHER_INTERVAL_MAX_DAYS_PER_WEEK;
17462 	this->startHour=sh;
17463 	this->endHour=eh;
17464 	assert(sh<eh);
17465 	assert(sh>=0);
17466 }
17467 
computeInternalStructure(QWidget * parent,Rules & r)17468 bool ConstraintTeacherIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
17469 {
17470 	//this->teacher_ID=r.searchTeacher(this->teacherName);
17471 	teacher_ID=r.teachersHash.value(teacherName, -1);
17472 	assert(this->teacher_ID>=0);
17473 	if(this->startHour>=this->endHour){
17474 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
17475 		 tr("Constraint teacher interval max days per week is wrong because start hour >= end hour."
17476 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
17477 
17478 		return false;
17479 	}
17480 	if(this->startHour<0){
17481 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
17482 		 tr("Constraint teacher interval max days per week is wrong because start hour < first hour of the day."
17483 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
17484 
17485 		return false;
17486 	}
17487 	if(this->endHour>r.nHoursPerDay){
17488 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
17489 		 tr("Constraint teacher interval max days per week is wrong because end hour > number of hours per day."
17490 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
17491 
17492 		return false;
17493 	}
17494 	return true;
17495 }
17496 
hasInactiveActivities(Rules & r)17497 bool ConstraintTeacherIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
17498 {
17499 	Q_UNUSED(r);
17500 	return false;
17501 }
17502 
getXmlDescription(Rules & r)17503 QString ConstraintTeacherIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
17504 {
17505 	Q_UNUSED(r);
17506 
17507 	QString s="<ConstraintTeacherIntervalMaxDaysPerWeek>\n";
17508 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
17509 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
17510 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
17511 	if(this->endHour < r.nHoursPerDay){
17512 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
17513 	}
17514 	else{
17515 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
17516 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
17517 	}
17518 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
17519 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
17520 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
17521 	s+="</ConstraintTeacherIntervalMaxDaysPerWeek>\n";
17522 	return s;
17523 }
17524 
getDescription(Rules & r)17525 QString ConstraintTeacherIntervalMaxDaysPerWeek::getDescription(Rules& r)
17526 {
17527 	Q_UNUSED(r);
17528 
17529 	QString begin=QString("");
17530 	if(!active)
17531 		begin="X - ";
17532 
17533 	QString end=QString("");
17534 	if(!comments.isEmpty())
17535 		end=", "+tr("C: %1", "Comments").arg(comments);
17536 
17537 	QString s=tr("Teacher interval max days per week");s+=", ";
17538 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
17539 	s+=tr("T:%1", "Abbreviation for teacher").arg(this->teacherName);s+=", ";
17540 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);s+=", ";
17541 	if(this->endHour<r.nHoursPerDay)
17542 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
17543 	else
17544 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
17545 	s+=", ";
17546 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
17547 
17548 	return begin+s+end;
17549 }
17550 
getDetailedDescription(Rules & r)17551 QString ConstraintTeacherIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r)
17552 {
17553 	Q_UNUSED(r);
17554 
17555 	QString s=tr("Time constraint");s+="\n";
17556 	s+=tr("A teacher respects working in an hourly interval a maximum number of days per week");s+="\n";
17557 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
17558 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
17559 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
17560 
17561 	if(this->endHour<r.nHoursPerDay)
17562 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
17563 	else
17564 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
17565 	s+="\n";
17566 
17567 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
17568 
17569 	if(!active){
17570 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
17571 		s+="\n";
17572 	}
17573 	if(!comments.isEmpty()){
17574 		s+=tr("Comments=%1").arg(comments);
17575 		s+="\n";
17576 	}
17577 
17578 	return s;
17579 }
17580 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)17581 double ConstraintTeacherIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
17582 {
17583 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
17584 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
17585 		c.teachersMatrixReady=true;
17586 		c.subgroupsMatrixReady=true;
17587 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
17588 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
17589 
17590 		c.changedForMatrixCalculation=false;
17591 	}
17592 
17593 	int nbroken;
17594 
17595 	int t=this->teacher_ID;
17596 
17597 	nbroken=0;
17598 
17599 	Matrix1D<bool> ocDay;
17600 	ocDay.resize(r.nDaysPerWeek);
17601 	for(int d=0; d<r.nDaysPerWeek; d++){
17602 		ocDay[d]=false;
17603 		for(int h=startHour; h<endHour; h++){
17604 			if(teachersMatrix[t][d][h]>0){
17605 				ocDay[d]=true;
17606 			}
17607 		}
17608 	}
17609 	int nOcDays=0;
17610 	for(int d=0; d<r.nDaysPerWeek; d++)
17611 		if(ocDay[d])
17612 			nOcDays++;
17613 	if(nOcDays > this->maxDaysPerWeek){
17614 		nbroken+=nOcDays-this->maxDaysPerWeek;
17615 
17616 		if(nbroken>0){
17617 			QString s= tr("Time constraint teacher interval max days per week broken for teacher: %1, allowed %2 days, required %3 days.")
17618 			 .arg(r.internalTeachersList[t]->name)
17619 			 .arg(this->maxDaysPerWeek)
17620 			 .arg(nOcDays);
17621 			s+=" ";
17622 			s += tr("This increases the conflicts total by %1")
17623 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
17624 
17625 			dl.append(s);
17626 			cl.append(nbroken*weightPercentage/100);
17627 
17628 			*conflictsString += s+"\n";
17629 		}
17630 	}
17631 
17632 	if(weightPercentage==100)
17633 		assert(nbroken==0);
17634 	return weightPercentage/100 * nbroken;
17635 }
17636 
isRelatedToActivity(Rules & r,Activity * a)17637 bool ConstraintTeacherIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
17638 {
17639 	Q_UNUSED(r);
17640 	Q_UNUSED(a);
17641 
17642 	return false;
17643 }
17644 
isRelatedToTeacher(Teacher * t)17645 bool ConstraintTeacherIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
17646 {
17647 	if(this->teacherName==t->name)
17648 		return true;
17649 	return false;
17650 }
17651 
isRelatedToSubject(Subject * s)17652 bool ConstraintTeacherIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
17653 {
17654 	Q_UNUSED(s);
17655 
17656 	return false;
17657 }
17658 
isRelatedToActivityTag(ActivityTag * s)17659 bool ConstraintTeacherIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
17660 {
17661 	Q_UNUSED(s);
17662 
17663 	return false;
17664 }
17665 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)17666 bool ConstraintTeacherIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
17667 {
17668 	Q_UNUSED(r);
17669 	Q_UNUSED(s);
17670 
17671 	return false;
17672 }
17673 
hasWrongDayOrHour(Rules & r)17674 bool ConstraintTeacherIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
17675 {
17676 	if(this->startHour>=r.nHoursPerDay)
17677 		return true;
17678 	if(this->endHour>r.nHoursPerDay)
17679 		return true;
17680 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
17681 		return true;
17682 
17683 	return false;
17684 }
17685 
canRepairWrongDayOrHour(Rules & r)17686 bool ConstraintTeacherIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
17687 {
17688 	assert(hasWrongDayOrHour(r));
17689 
17690 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
17691 		return true;
17692 
17693 	return false;
17694 }
17695 
repairWrongDayOrHour(Rules & r)17696 bool ConstraintTeacherIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
17697 {
17698 	assert(hasWrongDayOrHour(r));
17699 
17700 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
17701 
17702 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
17703 		this->maxDaysPerWeek=r.nDaysPerWeek;
17704 
17705 	return true;
17706 }
17707 
17708 ///////////////////////////////////////////////////////////////////////////////////////////
17709 ///////////////////////////////////////////////////////////////////////////////////////////
17710 
ConstraintTeachersIntervalMaxDaysPerWeek()17711 ConstraintTeachersIntervalMaxDaysPerWeek::ConstraintTeachersIntervalMaxDaysPerWeek()
17712 	: TimeConstraint()
17713 {
17714 	this->type=CONSTRAINT_TEACHERS_INTERVAL_MAX_DAYS_PER_WEEK;
17715 }
17716 
ConstraintTeachersIntervalMaxDaysPerWeek(double wp,int maxnd,int sh,int eh)17717 ConstraintTeachersIntervalMaxDaysPerWeek::ConstraintTeachersIntervalMaxDaysPerWeek(double wp, int maxnd, int sh, int eh)
17718 	 : TimeConstraint(wp)
17719 {
17720 	this->maxDaysPerWeek=maxnd;
17721 	this->type=CONSTRAINT_TEACHERS_INTERVAL_MAX_DAYS_PER_WEEK;
17722 	this->startHour=sh;
17723 	this->endHour=eh;
17724 	assert(sh<eh);
17725 	assert(sh>=0);
17726 }
17727 
computeInternalStructure(QWidget * parent,Rules & r)17728 bool ConstraintTeachersIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
17729 {
17730 	if(this->startHour>=this->endHour){
17731 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
17732 		 tr("Constraint teachers interval max days per week is wrong because start hour >= end hour."
17733 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
17734 
17735 		return false;
17736 	}
17737 	if(this->startHour<0){
17738 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
17739 		 tr("Constraint teachers interval max days per week is wrong because start hour < first hour of the day."
17740 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
17741 
17742 		return false;
17743 	}
17744 	if(this->endHour>r.nHoursPerDay){
17745 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
17746 		 tr("Constraint teachers interval max days per week is wrong because end hour > number of hours per day."
17747 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
17748 
17749 		return false;
17750 	}
17751 	return true;
17752 }
17753 
hasInactiveActivities(Rules & r)17754 bool ConstraintTeachersIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
17755 {
17756 	Q_UNUSED(r);
17757 	return false;
17758 }
17759 
getXmlDescription(Rules & r)17760 QString ConstraintTeachersIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
17761 {
17762 	Q_UNUSED(r);
17763 
17764 	QString s="<ConstraintTeachersIntervalMaxDaysPerWeek>\n";
17765 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
17766 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
17767 	if(this->endHour < r.nHoursPerDay){
17768 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
17769 	}
17770 	else{
17771 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
17772 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
17773 	}
17774 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
17775 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
17776 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
17777 	s+="</ConstraintTeachersIntervalMaxDaysPerWeek>\n";
17778 	return s;
17779 }
17780 
getDescription(Rules & r)17781 QString ConstraintTeachersIntervalMaxDaysPerWeek::getDescription(Rules& r)
17782 {
17783 	Q_UNUSED(r);
17784 
17785 	QString begin=QString("");
17786 	if(!active)
17787 		begin="X - ";
17788 
17789 	QString end=QString("");
17790 	if(!comments.isEmpty())
17791 		end=", "+tr("C: %1", "Comments").arg(comments);
17792 
17793 	QString s=tr("Teachers interval max days per week");s+=", ";
17794 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
17795 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);
17796 	s+=", ";
17797 	if(this->endHour<r.nHoursPerDay)
17798 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
17799 	else
17800 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
17801 	s+=", ";
17802 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
17803 
17804 	return begin+s+end;
17805 }
17806 
getDetailedDescription(Rules & r)17807 QString ConstraintTeachersIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r)
17808 {
17809 	Q_UNUSED(r);
17810 
17811 	QString s=tr("Time constraint");s+="\n";
17812 	s+=tr("All teachers respect working in an hourly interval a maximum number of days per week");s+="\n";
17813 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
17814 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
17815 
17816 	if(this->endHour<r.nHoursPerDay)
17817 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
17818 	else
17819 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
17820 	s+="\n";
17821 
17822 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
17823 
17824 	if(!active){
17825 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
17826 		s+="\n";
17827 	}
17828 	if(!comments.isEmpty()){
17829 		s+=tr("Comments=%1").arg(comments);
17830 		s+="\n";
17831 	}
17832 
17833 	return s;
17834 }
17835 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)17836 double ConstraintTeachersIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
17837 {
17838 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
17839 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
17840 		c.teachersMatrixReady=true;
17841 		c.subgroupsMatrixReady=true;
17842 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
17843 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
17844 
17845 		c.changedForMatrixCalculation=false;
17846 	}
17847 
17848 	int nbroken=0;
17849 
17850 	Matrix1D<bool> ocDay;
17851 	ocDay.resize(r.nDaysPerWeek);
17852 	for(int t=0; t<r.nInternalTeachers; t++){
17853 		for(int d=0; d<r.nDaysPerWeek; d++){
17854 			ocDay[d]=false;
17855 			for(int h=startHour; h<endHour; h++){
17856 				if(teachersMatrix[t][d][h]>0){
17857 					ocDay[d]=true;
17858 				}
17859 			}
17860 		}
17861 		int nOcDays=0;
17862 		for(int d=0; d<r.nDaysPerWeek; d++)
17863 			if(ocDay[d])
17864 				nOcDays++;
17865 		if(nOcDays > this->maxDaysPerWeek){
17866 			nbroken+=nOcDays-this->maxDaysPerWeek;
17867 
17868 			if(nOcDays-this->maxDaysPerWeek>0){
17869 				QString s= tr("Time constraint teachers interval max days per week broken for teacher: %1, allowed %2 days, required %3 days.")
17870 				 .arg(r.internalTeachersList[t]->name)
17871 				 .arg(this->maxDaysPerWeek)
17872 				 .arg(nOcDays);
17873 				s+=" ";
17874 				s += tr("This increases the conflicts total by %1")
17875 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
17876 
17877 				dl.append(s);
17878 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
17879 
17880 				*conflictsString += s+"\n";
17881 			}
17882 		}
17883 	}
17884 
17885 	if(weightPercentage==100)
17886 		assert(nbroken==0);
17887 	return weightPercentage/100 * nbroken;
17888 }
17889 
isRelatedToActivity(Rules & r,Activity * a)17890 bool ConstraintTeachersIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
17891 {
17892 	Q_UNUSED(r);
17893 	Q_UNUSED(a);
17894 
17895 	return false;
17896 }
17897 
isRelatedToTeacher(Teacher * t)17898 bool ConstraintTeachersIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
17899 {
17900 	Q_UNUSED(t);
17901 
17902 	return true;
17903 }
17904 
isRelatedToSubject(Subject * s)17905 bool ConstraintTeachersIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
17906 {
17907 	Q_UNUSED(s);
17908 
17909 	return false;
17910 }
17911 
isRelatedToActivityTag(ActivityTag * s)17912 bool ConstraintTeachersIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
17913 {
17914 	Q_UNUSED(s);
17915 
17916 	return false;
17917 }
17918 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)17919 bool ConstraintTeachersIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
17920 {
17921 	Q_UNUSED(r);
17922 	Q_UNUSED(s);
17923 
17924 	return false;
17925 }
17926 
hasWrongDayOrHour(Rules & r)17927 bool ConstraintTeachersIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
17928 {
17929 	if(this->startHour>=r.nHoursPerDay)
17930 		return true;
17931 	if(this->endHour>r.nHoursPerDay)
17932 		return true;
17933 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
17934 		return true;
17935 
17936 	return false;
17937 }
17938 
canRepairWrongDayOrHour(Rules & r)17939 bool ConstraintTeachersIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
17940 {
17941 	assert(hasWrongDayOrHour(r));
17942 
17943 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
17944 		return true;
17945 
17946 	return false;
17947 }
17948 
repairWrongDayOrHour(Rules & r)17949 bool ConstraintTeachersIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
17950 {
17951 	assert(hasWrongDayOrHour(r));
17952 
17953 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
17954 
17955 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
17956 		this->maxDaysPerWeek=r.nDaysPerWeek;
17957 
17958 	return true;
17959 }
17960 
17961 ///////////////////////////////////////////////////////////////////////////////////////////
17962 ///////////////////////////////////////////////////////////////////////////////////////////
17963 
ConstraintStudentsSetIntervalMaxDaysPerWeek()17964 ConstraintStudentsSetIntervalMaxDaysPerWeek::ConstraintStudentsSetIntervalMaxDaysPerWeek()
17965 	: TimeConstraint()
17966 {
17967 	this->type=CONSTRAINT_STUDENTS_SET_INTERVAL_MAX_DAYS_PER_WEEK;
17968 }
17969 
ConstraintStudentsSetIntervalMaxDaysPerWeek(double wp,int maxnd,const QString & sn,int sh,int eh)17970 ConstraintStudentsSetIntervalMaxDaysPerWeek::ConstraintStudentsSetIntervalMaxDaysPerWeek(double wp, int maxnd, const QString& sn, int sh, int eh)
17971 	 : TimeConstraint(wp)
17972 {
17973 	this->students = sn;
17974 	this->maxDaysPerWeek=maxnd;
17975 	this->type=CONSTRAINT_STUDENTS_SET_INTERVAL_MAX_DAYS_PER_WEEK;
17976 	this->startHour=sh;
17977 	this->endHour=eh;
17978 	assert(sh<eh);
17979 	assert(sh>=0);
17980 }
17981 
computeInternalStructure(QWidget * parent,Rules & r)17982 bool ConstraintStudentsSetIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
17983 {
17984 	if(this->startHour>=this->endHour){
17985 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
17986 		 tr("Constraint students set interval max days per week is wrong because start hour >= end hour."
17987 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
17988 
17989 		return false;
17990 	}
17991 	if(this->startHour<0){
17992 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
17993 		 tr("Constraint students set interval max days per week is wrong because start hour < first hour of the day."
17994 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
17995 
17996 		return false;
17997 	}
17998 	if(this->endHour>r.nHoursPerDay){
17999 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
18000 		 tr("Constraint students set interval max days per week is wrong because end hour > number of hours per day."
18001 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
18002 
18003 		return false;
18004 	}
18005 
18006 	/////////
18007 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
18008 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
18009 
18010 	if(ss==nullptr){
18011 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
18012 		 tr("Constraint students set interval max days per week is wrong because it refers to inexistent students set."
18013 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
18014 
18015 		return false;
18016 	}
18017 
18018 	assert(ss!=nullptr);
18019 
18020 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
18021 	/*this->iSubgroupsList.clear();
18022 	if(ss->type==STUDENTS_SUBGROUP){
18023 		int tmp;
18024 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
18025 		assert(tmp>=0);
18026 		assert(tmp<r.nInternalSubgroups);
18027 		if(!this->iSubgroupsList.contains(tmp))
18028 			this->iSubgroupsList.append(tmp);
18029 	}
18030 	else if(ss->type==STUDENTS_GROUP){
18031 		StudentsGroup* stg=(StudentsGroup*)ss;
18032 		for(int i=0; i<stg->subgroupsList.size(); i++){
18033 			StudentsSubgroup* sts=stg->subgroupsList[i];
18034 			int tmp;
18035 			tmp=sts->indexInInternalSubgroupsList;
18036 			assert(tmp>=0);
18037 			assert(tmp<r.nInternalSubgroups);
18038 			if(!this->iSubgroupsList.contains(tmp))
18039 				this->iSubgroupsList.append(tmp);
18040 		}
18041 	}
18042 	else if(ss->type==STUDENTS_YEAR){
18043 		StudentsYear* sty=(StudentsYear*)ss;
18044 		for(int i=0; i<sty->groupsList.size(); i++){
18045 			StudentsGroup* stg=sty->groupsList[i];
18046 			for(int j=0; j<stg->subgroupsList.size(); j++){
18047 				StudentsSubgroup* sts=stg->subgroupsList[j];
18048 				int tmp;
18049 				tmp=sts->indexInInternalSubgroupsList;
18050 				assert(tmp>=0);
18051 				assert(tmp<r.nInternalSubgroups);
18052 				if(!this->iSubgroupsList.contains(tmp))
18053 					this->iSubgroupsList.append(tmp);
18054 			}
18055 		}
18056 	}
18057 	else
18058 		assert(0);*/
18059 
18060 	return true;
18061 }
18062 
hasInactiveActivities(Rules & r)18063 bool ConstraintStudentsSetIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
18064 {
18065 	Q_UNUSED(r);
18066 	return false;
18067 }
18068 
getXmlDescription(Rules & r)18069 QString ConstraintStudentsSetIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
18070 {
18071 	Q_UNUSED(r);
18072 
18073 	QString s="<ConstraintStudentsSetIntervalMaxDaysPerWeek>\n";
18074 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
18075 	s+="	<Students>"+protect(this->students)+"</Students>\n";
18076 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
18077 	if(this->endHour < r.nHoursPerDay){
18078 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
18079 	}
18080 	else{
18081 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
18082 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
18083 	}
18084 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
18085 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
18086 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
18087 	s+="</ConstraintStudentsSetIntervalMaxDaysPerWeek>\n";
18088 	return s;
18089 }
18090 
getDescription(Rules & r)18091 QString ConstraintStudentsSetIntervalMaxDaysPerWeek::getDescription(Rules& r)
18092 {
18093 	Q_UNUSED(r);
18094 
18095 	QString begin=QString("");
18096 	if(!active)
18097 		begin="X - ";
18098 
18099 	QString end=QString("");
18100 	if(!comments.isEmpty())
18101 		end=", "+tr("C: %1", "Comments").arg(comments);
18102 
18103 	QString s=tr("Students set interval max days per week");s+=", ";
18104 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
18105 	s+=tr("St:%1", "Abbreviation for students (sets)").arg(this->students);s+=", ";
18106 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);
18107 	s+=", ";
18108 	if(this->endHour<r.nHoursPerDay)
18109 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
18110 	else
18111 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
18112 	s+=", ";
18113 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
18114 
18115 	return begin+s+end;
18116 }
18117 
getDetailedDescription(Rules & r)18118 QString ConstraintStudentsSetIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r)
18119 {
18120 	Q_UNUSED(r);
18121 
18122 	QString s=tr("Time constraint");s+="\n";
18123 	s+=tr("A students set respects working in an hourly interval a maximum number of days per week");s+="\n";
18124 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
18125 	s+=tr("Students set=%1").arg(this->students);s+="\n";
18126 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
18127 
18128 	if(this->endHour<r.nHoursPerDay)
18129 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
18130 	else
18131 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
18132 	s+="\n";
18133 
18134 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
18135 
18136 	if(!active){
18137 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
18138 		s+="\n";
18139 	}
18140 	if(!comments.isEmpty()){
18141 		s+=tr("Comments=%1").arg(comments);
18142 		s+="\n";
18143 	}
18144 
18145 	return s;
18146 }
18147 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)18148 double ConstraintStudentsSetIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
18149 {
18150 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
18151 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
18152 		c.teachersMatrixReady=true;
18153 		c.subgroupsMatrixReady=true;
18154 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
18155 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
18156 
18157 		c.changedForMatrixCalculation=false;
18158 	}
18159 
18160 	int nbroken;
18161 
18162 	nbroken=0;
18163 
18164 	Matrix1D<bool> ocDay;
18165 	ocDay.resize(r.nDaysPerWeek);
18166 	for(int sbg : qAsConst(this->iSubgroupsList)){
18167 		for(int d=0; d<r.nDaysPerWeek; d++){
18168 			ocDay[d]=false;
18169 			for(int h=startHour; h<endHour; h++){
18170 				if(subgroupsMatrix[sbg][d][h]>0){
18171 					ocDay[d]=true;
18172 				}
18173 			}
18174 		}
18175 		int nOcDays=0;
18176 		for(int d=0; d<r.nDaysPerWeek; d++)
18177 			if(ocDay[d])
18178 				nOcDays++;
18179 		if(nOcDays > this->maxDaysPerWeek){
18180 			nbroken+=nOcDays-this->maxDaysPerWeek;
18181 
18182 			if((nOcDays-this->maxDaysPerWeek)>0){
18183 				QString s= tr("Time constraint students set interval max days per week broken for subgroup: %1, allowed %2 days, required %3 days.")
18184 				 .arg(r.internalSubgroupsList[sbg]->name)
18185 				 .arg(this->maxDaysPerWeek)
18186 				 .arg(nOcDays);
18187 				s+=" ";
18188 				s += tr("This increases the conflicts total by %1")
18189 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
18190 
18191 				dl.append(s);
18192 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
18193 
18194 				*conflictsString += s+"\n";
18195 			}
18196 		}
18197 	}
18198 
18199 	if(weightPercentage==100)
18200 		assert(nbroken==0);
18201 	return weightPercentage/100 * nbroken;
18202 }
18203 
isRelatedToActivity(Rules & r,Activity * a)18204 bool ConstraintStudentsSetIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
18205 {
18206 	Q_UNUSED(r);
18207 	Q_UNUSED(a);
18208 
18209 	return false;
18210 }
18211 
isRelatedToTeacher(Teacher * t)18212 bool ConstraintStudentsSetIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
18213 {
18214 	Q_UNUSED(t);
18215 	return false;
18216 }
18217 
isRelatedToSubject(Subject * s)18218 bool ConstraintStudentsSetIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
18219 {
18220 	Q_UNUSED(s);
18221 
18222 	return false;
18223 }
18224 
isRelatedToActivityTag(ActivityTag * s)18225 bool ConstraintStudentsSetIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
18226 {
18227 	Q_UNUSED(s);
18228 
18229 	return false;
18230 }
18231 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)18232 bool ConstraintStudentsSetIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
18233 {
18234 	return r.setsShareStudents(this->students, s->name);
18235 }
18236 
hasWrongDayOrHour(Rules & r)18237 bool ConstraintStudentsSetIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
18238 {
18239 	if(this->startHour>=r.nHoursPerDay)
18240 		return true;
18241 	if(this->endHour>r.nHoursPerDay)
18242 		return true;
18243 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
18244 		return true;
18245 
18246 	return false;
18247 }
18248 
canRepairWrongDayOrHour(Rules & r)18249 bool ConstraintStudentsSetIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
18250 {
18251 	assert(hasWrongDayOrHour(r));
18252 
18253 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
18254 		return true;
18255 
18256 	return false;
18257 }
18258 
repairWrongDayOrHour(Rules & r)18259 bool ConstraintStudentsSetIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
18260 {
18261 	assert(hasWrongDayOrHour(r));
18262 
18263 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
18264 
18265 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
18266 		this->maxDaysPerWeek=r.nDaysPerWeek;
18267 
18268 	return true;
18269 }
18270 
18271 ///////////////////////////////////////////////////////////////////////////////////////////
18272 ///////////////////////////////////////////////////////////////////////////////////////////
18273 
ConstraintStudentsIntervalMaxDaysPerWeek()18274 ConstraintStudentsIntervalMaxDaysPerWeek::ConstraintStudentsIntervalMaxDaysPerWeek()
18275 	: TimeConstraint()
18276 {
18277 	this->type=CONSTRAINT_STUDENTS_INTERVAL_MAX_DAYS_PER_WEEK;
18278 }
18279 
ConstraintStudentsIntervalMaxDaysPerWeek(double wp,int maxnd,int sh,int eh)18280 ConstraintStudentsIntervalMaxDaysPerWeek::ConstraintStudentsIntervalMaxDaysPerWeek(double wp, int maxnd, int sh, int eh)
18281 	 : TimeConstraint(wp)
18282 {
18283 	this->maxDaysPerWeek=maxnd;
18284 	this->type=CONSTRAINT_STUDENTS_INTERVAL_MAX_DAYS_PER_WEEK;
18285 	this->startHour=sh;
18286 	this->endHour=eh;
18287 	assert(sh<eh);
18288 	assert(sh>=0);
18289 }
18290 
computeInternalStructure(QWidget * parent,Rules & r)18291 bool ConstraintStudentsIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
18292 {
18293 	if(this->startHour>=this->endHour){
18294 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
18295 		 tr("Constraint students interval max days per week is wrong because start hour >= end hour."
18296 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
18297 
18298 		return false;
18299 	}
18300 	if(this->startHour<0){
18301 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
18302 		 tr("Constraint students interval max days per week is wrong because start hour < first hour of the day."
18303 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
18304 
18305 		return false;
18306 	}
18307 	if(this->endHour>r.nHoursPerDay){
18308 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
18309 		 tr("Constraint students interval max days per week is wrong because end hour > number of hours per day."
18310 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
18311 
18312 		return false;
18313 	}
18314 
18315 	return true;
18316 }
18317 
hasInactiveActivities(Rules & r)18318 bool ConstraintStudentsIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
18319 {
18320 	Q_UNUSED(r);
18321 	return false;
18322 }
18323 
getXmlDescription(Rules & r)18324 QString ConstraintStudentsIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
18325 {
18326 	Q_UNUSED(r);
18327 
18328 	QString s="<ConstraintStudentsIntervalMaxDaysPerWeek>\n";
18329 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
18330 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
18331 	if(this->endHour < r.nHoursPerDay){
18332 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
18333 	}
18334 	else{
18335 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
18336 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
18337 	}
18338 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
18339 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
18340 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
18341 	s+="</ConstraintStudentsIntervalMaxDaysPerWeek>\n";
18342 	return s;
18343 }
18344 
getDescription(Rules & r)18345 QString ConstraintStudentsIntervalMaxDaysPerWeek::getDescription(Rules& r)
18346 {
18347 	Q_UNUSED(r);
18348 
18349 	QString begin=QString("");
18350 	if(!active)
18351 		begin="X - ";
18352 
18353 	QString end=QString("");
18354 	if(!comments.isEmpty())
18355 		end=", "+tr("C: %1", "Comments").arg(comments);
18356 
18357 	QString s=tr("Students interval max days per week");s+=", ";
18358 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
18359 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);
18360 	s+=", ";
18361 	if(this->endHour<r.nHoursPerDay)
18362 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
18363 	else
18364 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
18365 	s+=", ";
18366 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
18367 
18368 	return begin+s+end;
18369 }
18370 
getDetailedDescription(Rules & r)18371 QString ConstraintStudentsIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r)
18372 {
18373 	Q_UNUSED(r);
18374 
18375 	QString s=tr("Time constraint");s+="\n";
18376 	s+=tr("All students respect working in an hourly interval a maximum number of days per week");s+="\n";
18377 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
18378 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
18379 
18380 	if(this->endHour<r.nHoursPerDay)
18381 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
18382 	else
18383 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
18384 	s+="\n";
18385 
18386 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
18387 
18388 	if(!active){
18389 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
18390 		s+="\n";
18391 	}
18392 	if(!comments.isEmpty()){
18393 		s+=tr("Comments=%1").arg(comments);
18394 		s+="\n";
18395 	}
18396 
18397 	return s;
18398 }
18399 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)18400 double ConstraintStudentsIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
18401 {
18402 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
18403 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
18404 		c.teachersMatrixReady=true;
18405 		c.subgroupsMatrixReady=true;
18406 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
18407 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
18408 
18409 		c.changedForMatrixCalculation=false;
18410 	}
18411 
18412 	int nbroken;
18413 
18414 	nbroken=0;
18415 
18416 	Matrix1D<bool> ocDay;
18417 	ocDay.resize(r.nDaysPerWeek);
18418 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
18419 		for(int d=0; d<r.nDaysPerWeek; d++){
18420 			ocDay[d]=false;
18421 			for(int h=startHour; h<endHour; h++){
18422 				if(subgroupsMatrix[sbg][d][h]>0){
18423 					ocDay[d]=true;
18424 				}
18425 			}
18426 		}
18427 		int nOcDays=0;
18428 		for(int d=0; d<r.nDaysPerWeek; d++)
18429 			if(ocDay[d])
18430 				nOcDays++;
18431 		if(nOcDays > this->maxDaysPerWeek){
18432 			nbroken+=nOcDays-this->maxDaysPerWeek;
18433 
18434 			if((nOcDays-this->maxDaysPerWeek)>0){
18435 				QString s= tr("Time constraint students interval max days per week broken for subgroup: %1, allowed %2 days, required %3 days.")
18436 				 .arg(r.internalSubgroupsList[sbg]->name)
18437 				 .arg(this->maxDaysPerWeek)
18438 				 .arg(nOcDays);
18439 				s+=" ";
18440 				s += tr("This increases the conflicts total by %1")
18441 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
18442 
18443 				dl.append(s);
18444 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
18445 
18446 				*conflictsString += s+"\n";
18447 			}
18448 		}
18449 	}
18450 
18451 	if(weightPercentage==100)
18452 		assert(nbroken==0);
18453 	return weightPercentage/100 * nbroken;
18454 }
18455 
isRelatedToActivity(Rules & r,Activity * a)18456 bool ConstraintStudentsIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
18457 {
18458 	Q_UNUSED(r);
18459 	Q_UNUSED(a);
18460 
18461 	return false;
18462 }
18463 
isRelatedToTeacher(Teacher * t)18464 bool ConstraintStudentsIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
18465 {
18466 	Q_UNUSED(t);
18467 	return false;
18468 }
18469 
isRelatedToSubject(Subject * s)18470 bool ConstraintStudentsIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
18471 {
18472 	Q_UNUSED(s);
18473 
18474 	return false;
18475 }
18476 
isRelatedToActivityTag(ActivityTag * s)18477 bool ConstraintStudentsIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
18478 {
18479 	Q_UNUSED(s);
18480 
18481 	return false;
18482 }
18483 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)18484 bool ConstraintStudentsIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
18485 {
18486 	Q_UNUSED(r);
18487 	Q_UNUSED(s);
18488 	return true;
18489 }
18490 
hasWrongDayOrHour(Rules & r)18491 bool ConstraintStudentsIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
18492 {
18493 	if(this->startHour>=r.nHoursPerDay)
18494 		return true;
18495 	if(this->endHour>r.nHoursPerDay)
18496 		return true;
18497 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
18498 		return true;
18499 
18500 	return false;
18501 }
18502 
canRepairWrongDayOrHour(Rules & r)18503 bool ConstraintStudentsIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
18504 {
18505 	assert(hasWrongDayOrHour(r));
18506 
18507 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
18508 		return true;
18509 
18510 	return false;
18511 }
18512 
repairWrongDayOrHour(Rules & r)18513 bool ConstraintStudentsIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
18514 {
18515 	assert(hasWrongDayOrHour(r));
18516 
18517 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
18518 
18519 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
18520 		this->maxDaysPerWeek=r.nDaysPerWeek;
18521 
18522 	return true;
18523 }
18524 
18525 ////////////////////////////////////////////////////////////////////////////////////////////
18526 ////////////////////////////////////////////////////////////////////////////////////////////
18527 
ConstraintActivitiesEndStudentsDay()18528 ConstraintActivitiesEndStudentsDay::ConstraintActivitiesEndStudentsDay()
18529 	: TimeConstraint()
18530 {
18531 	this->type = CONSTRAINT_ACTIVITIES_END_STUDENTS_DAY;
18532 }
18533 
ConstraintActivitiesEndStudentsDay(double wp,const QString & te,const QString & st,const QString & su,const QString & sut)18534 ConstraintActivitiesEndStudentsDay::ConstraintActivitiesEndStudentsDay(double wp, const QString& te,
18535 	const QString& st, const QString& su, const QString& sut)
18536 	: TimeConstraint(wp)
18537 {
18538 	this->teacherName=te;
18539 	this->subjectName=su;
18540 	this->activityTagName=sut;
18541 	this->studentsName=st;
18542 	this->type=CONSTRAINT_ACTIVITIES_END_STUDENTS_DAY;
18543 }
18544 
computeInternalStructure(QWidget * parent,Rules & r)18545 bool ConstraintActivitiesEndStudentsDay::computeInternalStructure(QWidget* parent, Rules& r)
18546 {
18547 	this->nActivities=0;
18548 	this->activitiesIndices.clear();
18549 
18550 	int it;
18551 	Activity* act;
18552 	int i;
18553 	for(i=0; i<r.nInternalActivities; i++){
18554 		act=&r.internalActivitiesList[i];
18555 
18556 		//check if this activity has the corresponding teacher
18557 		if(this->teacherName!=""){
18558 			it = act->teachersNames.indexOf(this->teacherName);
18559 			if(it==-1)
18560 				continue;
18561 		}
18562 		//check if this activity has the corresponding students
18563 		if(this->studentsName!=""){
18564 			bool commonStudents=false;
18565 			for(const QString& st : qAsConst(act->studentsNames))
18566 				if(r.augmentedSetsShareStudentsFaster(st, studentsName)){
18567 					commonStudents=true;
18568 					break;
18569 				}
18570 
18571 			if(!commonStudents)
18572 				continue;
18573 		}
18574 		//check if this activity has the corresponding subject
18575 		if(this->subjectName!="" && act->subjectName!=this->subjectName){
18576 				continue;
18577 		}
18578 		//check if this activity has the corresponding activity tag
18579 		if(this->activityTagName!="" && !act->activityTagsNames.contains(this->activityTagName)){
18580 				continue;
18581 		}
18582 
18583 		assert(this->nActivities < r.nInternalActivities);
18584 		this->nActivities++;
18585 		this->activitiesIndices.append(i);
18586 	}
18587 
18588 	assert(this->activitiesIndices.count()==this->nActivities);
18589 
18590 	if(this->nActivities>0)
18591 		return true;
18592 	else{
18593 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
18594 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
18595 		return false;
18596 	}
18597 }
18598 
hasInactiveActivities(Rules & r)18599 bool ConstraintActivitiesEndStudentsDay::hasInactiveActivities(Rules& r)
18600 {
18601 	Q_UNUSED(r);
18602 	return false;
18603 }
18604 
getXmlDescription(Rules & r)18605 QString ConstraintActivitiesEndStudentsDay::getXmlDescription(Rules& r)
18606 {
18607 	Q_UNUSED(r);
18608 
18609 	QString s="<ConstraintActivitiesEndStudentsDay>\n";
18610 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
18611 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
18612 	s+="	<Students_Name>"+protect(this->studentsName)+"</Students_Name>\n";
18613 	s+="	<Subject_Name>"+protect(this->subjectName)+"</Subject_Name>\n";
18614 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
18615 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
18616 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
18617 	s+="</ConstraintActivitiesEndStudentsDay>\n";
18618 	return s;
18619 }
18620 
getDescription(Rules & r)18621 QString ConstraintActivitiesEndStudentsDay::getDescription(Rules& r)
18622 {
18623 	Q_UNUSED(r);
18624 
18625 	QString begin=QString("");
18626 	if(!active)
18627 		begin="X - ";
18628 
18629 	QString end=QString("");
18630 	if(!comments.isEmpty())
18631 		end=", "+tr("C: %1", "Comments").arg(comments);
18632 
18633 	QString tc, st, su, at;
18634 
18635 	if(this->teacherName!="")
18636 		tc=tr("teacher=%1").arg(this->teacherName);
18637 	else
18638 		tc=tr("all teachers");
18639 
18640 	if(this->studentsName!="")
18641 		st=tr("students=%1").arg(this->studentsName);
18642 	else
18643 		st=tr("all students");
18644 
18645 	if(this->subjectName!="")
18646 		su=tr("subject=%1").arg(this->subjectName);
18647 	else
18648 		su=tr("all subjects");
18649 
18650 	if(this->activityTagName!="")
18651 		at=tr("activity tag=%1").arg(this->activityTagName);
18652 	else
18653 		at=tr("all activity tags");
18654 
18655 	QString s;
18656 	s+=tr("Activities with %1, %2, %3, %4, must end students' day", "%1...%4 are conditions for the activities").arg(tc).arg(st).arg(su).arg(at);
18657 
18658 	s+=", ";
18659 
18660 	s+=tr("WP:%1%", "Abbreviation for Weight Percentage").arg(CustomFETString::number(this->weightPercentage));
18661 
18662 	return begin+s+end;
18663 }
18664 
getDetailedDescription(Rules & r)18665 QString ConstraintActivitiesEndStudentsDay::getDetailedDescription(Rules& r)
18666 {
18667 	Q_UNUSED(r);
18668 
18669 	QString s=tr("Time constraint");s+="\n";
18670 	s+=tr("Activities with:");s+="\n";
18671 
18672 	if(this->teacherName!="")
18673 		s+=tr("Teacher=%1").arg(this->teacherName);
18674 	else
18675 		s+=tr("All teachers");
18676 	s+="\n";
18677 
18678 	if(this->studentsName!="")
18679 		s+=tr("Students=%1").arg(this->studentsName);
18680 	else
18681 		s+=tr("All students");
18682 	s+="\n";
18683 
18684 	if(this->subjectName!="")
18685 		s+=tr("Subject=%1").arg(this->subjectName);
18686 	else
18687 		s+=tr("All subjects");
18688 	s+="\n";
18689 
18690 	if(this->activityTagName!="")
18691 		s+=tr("Activity tag=%1").arg(this->activityTagName);
18692 	else
18693 		s+=tr("All activity tags");
18694 	s+="\n";
18695 
18696 	s+=tr("must end students' day");
18697 	s+="\n";
18698 
18699 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
18700 
18701 	if(!active){
18702 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
18703 		s+="\n";
18704 	}
18705 	if(!comments.isEmpty()){
18706 		s+=tr("Comments=%1").arg(comments);
18707 		s+="\n";
18708 	}
18709 
18710 	return s;
18711 }
18712 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)18713 double ConstraintActivitiesEndStudentsDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
18714 {
18715 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
18716 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
18717 		c.teachersMatrixReady=true;
18718 		c.subgroupsMatrixReady=true;
18719 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
18720 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
18721 
18722 		c.changedForMatrixCalculation=false;
18723 	}
18724 
18725 	int nbroken=0;
18726 
18727 	assert(r.internalStructureComputed);
18728 
18729 	for(int kk=0; kk<this->nActivities; kk++){
18730 		int tmp=0;
18731 		int ai=this->activitiesIndices[kk];
18732 
18733 		if(c.times[ai]!=UNALLOCATED_TIME){
18734 			int d=c.times[ai]%r.nDaysPerWeek; //the day when this activity was scheduled
18735 			int h=c.times[ai]/r.nDaysPerWeek; //the hour
18736 
18737 			for(int j=0; j<r.internalActivitiesList[ai].iSubgroupsList.count(); j++){
18738 				int sb=r.internalActivitiesList[ai].iSubgroupsList.at(j);
18739 				for(int hh=h+r.internalActivitiesList[ai].duration; hh<r.nHoursPerDay; hh++)
18740 					if(subgroupsMatrix[sb][d][hh]>0){
18741 						nbroken++;
18742 						tmp=1;
18743 						break;
18744 					}
18745 				if(tmp>0)
18746 					break;
18747 			}
18748 
18749 			if(conflictsString!=nullptr && tmp>0){
18750 				QString s=tr("Time constraint activities end students' day broken for activity with id=%1 (%2), increases conflicts total by %3",
18751 				 "%1 is the id, %2 is the detailed description of the activity")
18752 				 .arg(r.internalActivitiesList[ai].id)
18753 				 .arg(getActivityDetailedDescription(r, r.internalActivitiesList[ai].id))
18754 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*tmp));
18755 
18756 				dl.append(s);
18757 				cl.append(weightPercentage/100*tmp);
18758 
18759 				*conflictsString+= s+"\n";
18760 			}
18761 		}
18762 	}
18763 
18764 	if(weightPercentage==100)
18765 		assert(nbroken==0);
18766 	return nbroken * weightPercentage/100;
18767 }
18768 
isRelatedToActivity(Rules & r,Activity * a)18769 bool ConstraintActivitiesEndStudentsDay::isRelatedToActivity(Rules& r, Activity* a)
18770 {
18771 	int it;
18772 
18773 	//check if this activity has the corresponding teacher
18774 	if(this->teacherName!=""){
18775 		it = a->teachersNames.indexOf(this->teacherName);
18776 		if(it==-1)
18777 			return false;
18778 	}
18779 	//check if this activity has the corresponding students
18780 	if(this->studentsName!=""){
18781 		bool commonStudents=false;
18782 		for(const QString& st : qAsConst(a->studentsNames)){
18783 			if(r.setsShareStudents(st, this->studentsName)){
18784 				commonStudents=true;
18785 				break;
18786 			}
18787 		}
18788 		if(!commonStudents)
18789 			return false;
18790 	}
18791 	//check if this activity has the corresponding subject
18792 	if(this->subjectName!="" && a->subjectName!=this->subjectName)
18793 		return false;
18794 	//check if this activity has the corresponding activity tag
18795 	if(this->activityTagName!="" && !a->activityTagsNames.contains(this->activityTagName))
18796 		return false;
18797 
18798 	return true;
18799 }
18800 
isRelatedToTeacher(Teacher * t)18801 bool ConstraintActivitiesEndStudentsDay::isRelatedToTeacher(Teacher* t)
18802 {
18803 	Q_UNUSED(t);
18804 
18805 	return false;
18806 }
18807 
isRelatedToSubject(Subject * s)18808 bool ConstraintActivitiesEndStudentsDay::isRelatedToSubject(Subject* s)
18809 {
18810 	Q_UNUSED(s);
18811 
18812 	return false;
18813 }
18814 
isRelatedToActivityTag(ActivityTag * s)18815 bool ConstraintActivitiesEndStudentsDay::isRelatedToActivityTag(ActivityTag* s)
18816 {
18817 	Q_UNUSED(s);
18818 
18819 	return false;
18820 }
18821 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)18822 bool ConstraintActivitiesEndStudentsDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
18823 {
18824 	Q_UNUSED(r);
18825 	Q_UNUSED(s);
18826 
18827 	return false;
18828 }
18829 
hasWrongDayOrHour(Rules & r)18830 bool ConstraintActivitiesEndStudentsDay::hasWrongDayOrHour(Rules& r)
18831 {
18832 	Q_UNUSED(r);
18833 	return false;
18834 }
18835 
canRepairWrongDayOrHour(Rules & r)18836 bool ConstraintActivitiesEndStudentsDay::canRepairWrongDayOrHour(Rules& r)
18837 {
18838 	Q_UNUSED(r);
18839 	assert(0);
18840 
18841 	return true;
18842 }
18843 
repairWrongDayOrHour(Rules & r)18844 bool ConstraintActivitiesEndStudentsDay::repairWrongDayOrHour(Rules& r)
18845 {
18846 	Q_UNUSED(r);
18847 	assert(0); //should check hasWrongDayOrHour, firstly
18848 
18849 	return true;
18850 }
18851 
18852 ////////////////////////////////////////////////////////////////////////////////////////////
18853 ////////////////////////////////////////////////////////////////////////////////////////////
18854 
ConstraintActivityEndsTeachersDay()18855 ConstraintActivityEndsTeachersDay::ConstraintActivityEndsTeachersDay()
18856 	: TimeConstraint()
18857 {
18858 	this->type = CONSTRAINT_ACTIVITY_ENDS_TEACHERS_DAY;
18859 }
18860 
ConstraintActivityEndsTeachersDay(double wp,int actId)18861 ConstraintActivityEndsTeachersDay::ConstraintActivityEndsTeachersDay(double wp, int actId)
18862 	: TimeConstraint(wp)
18863 {
18864 	this->activityId = actId;
18865 	this->type = CONSTRAINT_ACTIVITY_ENDS_TEACHERS_DAY;
18866 }
18867 
computeInternalStructure(QWidget * parent,Rules & r)18868 bool ConstraintActivityEndsTeachersDay::computeInternalStructure(QWidget* parent, Rules& r)
18869 {
18870 	/*Activity* act;
18871 	int i;
18872 	for(i=0; i<r.nInternalActivities; i++){
18873 		act=&r.internalActivitiesList[i];
18874 		if(act->id==this->activityId)
18875 			break;
18876 	}*/
18877 
18878 	int i=r.activitiesHash.value(activityId, r.nInternalActivities);
18879 
18880 	if(i==r.nInternalActivities){
18881 		//assert(0);
18882 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
18883 			tr("Following constraint is wrong (because it refers to invalid activity id). Please correct it (maybe removing it is a solution):\n%1").arg(this->getDetailedDescription(r)));
18884 		return false;
18885 	}
18886 
18887 	this->activityIndex=i;
18888 	return true;
18889 }
18890 
hasInactiveActivities(Rules & r)18891 bool ConstraintActivityEndsTeachersDay::hasInactiveActivities(Rules& r)
18892 {
18893 	if(r.inactiveActivities.contains(this->activityId))
18894 		return true;
18895 	return false;
18896 }
18897 
getXmlDescription(Rules & r)18898 QString ConstraintActivityEndsTeachersDay::getXmlDescription(Rules& r)
18899 {
18900 	Q_UNUSED(r);
18901 
18902 	QString s="<ConstraintActivityEndsTeachersDay>\n";
18903 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
18904 	s+="	<Activity_Id>"+CustomFETString::number(this->activityId)+"</Activity_Id>\n";
18905 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
18906 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
18907 	s+="</ConstraintActivityEndsTeachersDay>\n";
18908 	return s;
18909 }
18910 
getDescription(Rules & r)18911 QString ConstraintActivityEndsTeachersDay::getDescription(Rules& r)
18912 {
18913 	QString begin=QString("");
18914 	if(!active)
18915 		begin="X - ";
18916 
18917 	QString end=QString("");
18918 	if(!comments.isEmpty())
18919 		end=", "+tr("C: %1", "Comments").arg(comments);
18920 
18921 	QString s;
18922 	s+=tr("Act. id: %1 (%2) must end teachers' day",
18923 		"%1 is the id, %2 is the detailed description of the activity.")
18924 		.arg(this->activityId)
18925 		.arg(getActivityDetailedDescription(r, this->activityId));
18926 	s+=", ";
18927 
18928 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
18929 
18930 	return begin+s+end;
18931 }
18932 
getDetailedDescription(Rules & r)18933 QString ConstraintActivityEndsTeachersDay::getDetailedDescription(Rules& r)
18934 {
18935 	QString s=tr("Time constraint");s+="\n";
18936 	s+=tr("Activity must end teachers' day");s+="\n";
18937 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
18938 	s+=tr("Activity id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity.")
18939 		.arg(this->activityId)
18940 		.arg(getActivityDetailedDescription(r, this->activityId));s+="\n";
18941 
18942 	if(!active){
18943 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
18944 		s+="\n";
18945 	}
18946 	if(!comments.isEmpty()){
18947 		s+=tr("Comments=%1").arg(comments);
18948 		s+="\n";
18949 	}
18950 
18951 	return s;
18952 }
18953 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)18954 double ConstraintActivityEndsTeachersDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString> &dl, FakeString* conflictsString)
18955 {
18956 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
18957 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
18958 		c.teachersMatrixReady=true;
18959 		c.subgroupsMatrixReady=true;
18960 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
18961 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
18962 
18963 		c.changedForMatrixCalculation=false;
18964 	}
18965 
18966 	int nbroken;
18967 
18968 	assert(r.internalStructureComputed);
18969 
18970 	nbroken=0;
18971 	if(c.times[this->activityIndex]!=UNALLOCATED_TIME){
18972 		int d=c.times[this->activityIndex]%r.nDaysPerWeek; //the day when this activity was scheduled
18973 		int h=c.times[this->activityIndex]/r.nDaysPerWeek; //the hour
18974 
18975 		int i=this->activityIndex;
18976 		for(int j=0; j<r.internalActivitiesList[i].iTeachersList.count(); j++){
18977 			int tch=r.internalActivitiesList[i].iTeachersList.at(j);
18978 			for(int hh=h+r.internalActivitiesList[i].duration; hh<r.nHoursPerDay; hh++)
18979 				if(teachersMatrix[tch][d][hh]>0){
18980 					nbroken=1;
18981 					break;
18982 				}
18983 			if(nbroken)
18984 				break;
18985 		}
18986 	}
18987 
18988 	if(conflictsString!=nullptr && nbroken>0){
18989 		QString s=tr("Time constraint activity ends teachers' day broken for activity with id=%1 (%2), increases conflicts total by %3",
18990 		 "%1 is the id, %2 is the detailed description of the activity")
18991 		 .arg(this->activityId)
18992 		 .arg(getActivityDetailedDescription(r, this->activityId))
18993 		 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*nbroken));
18994 
18995 		dl.append(s);
18996 		cl.append(weightPercentage/100*nbroken);
18997 
18998 		*conflictsString+= s+"\n";
18999 	}
19000 
19001 	if(weightPercentage==100)
19002 		assert(nbroken==0);
19003 	return nbroken * weightPercentage/100;
19004 }
19005 
isRelatedToActivity(Rules & r,Activity * a)19006 bool ConstraintActivityEndsTeachersDay::isRelatedToActivity(Rules& r, Activity* a)
19007 {
19008 	Q_UNUSED(r);
19009 
19010 	if(this->activityId==a->id)
19011 		return true;
19012 	return false;
19013 }
19014 
isRelatedToTeacher(Teacher * t)19015 bool ConstraintActivityEndsTeachersDay::isRelatedToTeacher(Teacher* t)
19016 {
19017 	Q_UNUSED(t);
19018 
19019 	return false;
19020 }
19021 
isRelatedToSubject(Subject * s)19022 bool ConstraintActivityEndsTeachersDay::isRelatedToSubject(Subject* s)
19023 {
19024 	Q_UNUSED(s);
19025 
19026 	return false;
19027 }
19028 
isRelatedToActivityTag(ActivityTag * s)19029 bool ConstraintActivityEndsTeachersDay::isRelatedToActivityTag(ActivityTag* s)
19030 {
19031 	Q_UNUSED(s);
19032 
19033 	return false;
19034 }
19035 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)19036 bool ConstraintActivityEndsTeachersDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
19037 {
19038 	Q_UNUSED(r);
19039 	Q_UNUSED(s);
19040 
19041 	return false;
19042 }
19043 
hasWrongDayOrHour(Rules & r)19044 bool ConstraintActivityEndsTeachersDay::hasWrongDayOrHour(Rules& r)
19045 {
19046 	Q_UNUSED(r);
19047 	return false;
19048 }
19049 
canRepairWrongDayOrHour(Rules & r)19050 bool ConstraintActivityEndsTeachersDay::canRepairWrongDayOrHour(Rules& r)
19051 {
19052 	Q_UNUSED(r);
19053 	assert(0);
19054 
19055 	return true;
19056 }
19057 
repairWrongDayOrHour(Rules & r)19058 bool ConstraintActivityEndsTeachersDay::repairWrongDayOrHour(Rules& r)
19059 {
19060 	Q_UNUSED(r);
19061 	assert(0); //should check hasWrongDayOrHour, firstly
19062 
19063 	return true;
19064 }
19065 
19066 ////////////////////////////////////////////////////////////////////////////////////////////
19067 ////////////////////////////////////////////////////////////////////////////////////////////
19068 
ConstraintActivitiesEndTeachersDay()19069 ConstraintActivitiesEndTeachersDay::ConstraintActivitiesEndTeachersDay()
19070 	: TimeConstraint()
19071 {
19072 	this->type = CONSTRAINT_ACTIVITIES_END_TEACHERS_DAY;
19073 }
19074 
ConstraintActivitiesEndTeachersDay(double wp,const QString & te,const QString & st,const QString & su,const QString & sut)19075 ConstraintActivitiesEndTeachersDay::ConstraintActivitiesEndTeachersDay(double wp, const QString& te,
19076 	const QString& st, const QString& su, const QString& sut)
19077 	: TimeConstraint(wp)
19078 {
19079 	this->teacherName=te;
19080 	this->subjectName=su;
19081 	this->activityTagName=sut;
19082 	this->studentsName=st;
19083 	this->type=CONSTRAINT_ACTIVITIES_END_TEACHERS_DAY;
19084 }
19085 
computeInternalStructure(QWidget * parent,Rules & r)19086 bool ConstraintActivitiesEndTeachersDay::computeInternalStructure(QWidget* parent, Rules& r)
19087 {
19088 	this->nActivities=0;
19089 	this->activitiesIndices.clear();
19090 
19091 	int it;
19092 	Activity* act;
19093 	int i;
19094 	for(i=0; i<r.nInternalActivities; i++){
19095 		act=&r.internalActivitiesList[i];
19096 
19097 		//check if this activity has the corresponding teacher
19098 		if(this->teacherName!=""){
19099 			it = act->teachersNames.indexOf(this->teacherName);
19100 			if(it==-1)
19101 				continue;
19102 		}
19103 		//check if this activity has the corresponding students
19104 		if(this->studentsName!=""){
19105 			bool commonStudents=false;
19106 			for(const QString& st : qAsConst(act->studentsNames))
19107 				if(r.augmentedSetsShareStudentsFaster(st, studentsName)){
19108 					commonStudents=true;
19109 					break;
19110 				}
19111 
19112 			if(!commonStudents)
19113 				continue;
19114 		}
19115 		//check if this activity has the corresponding subject
19116 		if(this->subjectName!="" && act->subjectName!=this->subjectName){
19117 				continue;
19118 		}
19119 		//check if this activity has the corresponding activity tag
19120 		if(this->activityTagName!="" && !act->activityTagsNames.contains(this->activityTagName)){
19121 				continue;
19122 		}
19123 
19124 		assert(this->nActivities < MAX_ACTIVITIES);
19125 		this->nActivities++;
19126 		this->activitiesIndices.append(i);
19127 	}
19128 
19129 	assert(this->activitiesIndices.count()==this->nActivities);
19130 
19131 	if(this->nActivities>0)
19132 		return true;
19133 	else{
19134 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
19135 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
19136 		return false;
19137 	}
19138 }
19139 
hasInactiveActivities(Rules & r)19140 bool ConstraintActivitiesEndTeachersDay::hasInactiveActivities(Rules& r)
19141 {
19142 	Q_UNUSED(r);
19143 	return false;
19144 }
19145 
getXmlDescription(Rules & r)19146 QString ConstraintActivitiesEndTeachersDay::getXmlDescription(Rules& r)
19147 {
19148 	Q_UNUSED(r);
19149 
19150 	QString s="<ConstraintActivitiesEndTeachersDay>\n";
19151 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
19152 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
19153 	s+="	<Students_Name>"+protect(this->studentsName)+"</Students_Name>\n";
19154 	s+="	<Subject_Name>"+protect(this->subjectName)+"</Subject_Name>\n";
19155 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
19156 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
19157 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
19158 	s+="</ConstraintActivitiesEndTeachersDay>\n";
19159 	return s;
19160 }
19161 
getDescription(Rules & r)19162 QString ConstraintActivitiesEndTeachersDay::getDescription(Rules& r)
19163 {
19164 	Q_UNUSED(r);
19165 
19166 	QString begin=QString("");
19167 	if(!active)
19168 		begin="X - ";
19169 
19170 	QString end=QString("");
19171 	if(!comments.isEmpty())
19172 		end=", "+tr("C: %1", "Comments").arg(comments);
19173 
19174 	QString tc, st, su, at;
19175 
19176 	if(this->teacherName!="")
19177 		tc=tr("teacher=%1").arg(this->teacherName);
19178 	else
19179 		tc=tr("all teachers");
19180 
19181 	if(this->studentsName!="")
19182 		st=tr("students=%1").arg(this->studentsName);
19183 	else
19184 		st=tr("all students");
19185 
19186 	if(this->subjectName!="")
19187 		su=tr("subject=%1").arg(this->subjectName);
19188 	else
19189 		su=tr("all subjects");
19190 
19191 	if(this->activityTagName!="")
19192 		at=tr("activity tag=%1").arg(this->activityTagName);
19193 	else
19194 		at=tr("all activity tags");
19195 
19196 	QString s;
19197 	s+=tr("Activities with %1, %2, %3, %4, must end teachers' day", "%1...%4 are conditions for the activities").arg(tc).arg(st).arg(su).arg(at);
19198 
19199 	s+=", ";
19200 
19201 	s+=tr("WP:%1%", "Abbreviation for Weight Percentage").arg(CustomFETString::number(this->weightPercentage));
19202 
19203 	return begin+s+end;
19204 }
19205 
getDetailedDescription(Rules & r)19206 QString ConstraintActivitiesEndTeachersDay::getDetailedDescription(Rules& r)
19207 {
19208 	Q_UNUSED(r);
19209 
19210 	QString s=tr("Time constraint");s+="\n";
19211 	s+=tr("Activities with:");s+="\n";
19212 
19213 	if(this->teacherName!="")
19214 		s+=tr("Teacher=%1").arg(this->teacherName);
19215 	else
19216 		s+=tr("All teachers");
19217 	s+="\n";
19218 
19219 	if(this->studentsName!="")
19220 		s+=tr("Students=%1").arg(this->studentsName);
19221 	else
19222 		s+=tr("All students");
19223 	s+="\n";
19224 
19225 	if(this->subjectName!="")
19226 		s+=tr("Subject=%1").arg(this->subjectName);
19227 	else
19228 		s+=tr("All subjects");
19229 	s+="\n";
19230 
19231 	if(this->activityTagName!="")
19232 		s+=tr("Activity tag=%1").arg(this->activityTagName);
19233 	else
19234 		s+=tr("All activity tags");
19235 	s+="\n";
19236 
19237 	s+=tr("must end teachers' day");
19238 	s+="\n";
19239 
19240 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
19241 
19242 	if(!active){
19243 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
19244 		s+="\n";
19245 	}
19246 	if(!comments.isEmpty()){
19247 		s+=tr("Comments=%1").arg(comments);
19248 		s+="\n";
19249 	}
19250 
19251 	return s;
19252 }
19253 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)19254 double ConstraintActivitiesEndTeachersDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString> &dl, FakeString* conflictsString)
19255 {
19256 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
19257 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
19258 		c.teachersMatrixReady=true;
19259 		c.subgroupsMatrixReady=true;
19260 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
19261 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
19262 
19263 		c.changedForMatrixCalculation=false;
19264 	}
19265 
19266 	int nbroken=0;
19267 
19268 	assert(r.internalStructureComputed);
19269 
19270 	for(int kk=0; kk<this->nActivities; kk++){
19271 		int tmp=0;
19272 		int ai=this->activitiesIndices[kk];
19273 
19274 		if(c.times[ai]!=UNALLOCATED_TIME){
19275 			int d=c.times[ai]%r.nDaysPerWeek; //the day when this activity was scheduled
19276 			int h=c.times[ai]/r.nDaysPerWeek; //the hour
19277 
19278 			for(int j=0; j<r.internalActivitiesList[ai].iTeachersList.count(); j++){
19279 				int tch=r.internalActivitiesList[ai].iTeachersList.at(j);
19280 				for(int hh=h+r.internalActivitiesList[ai].duration; hh<r.nHoursPerDay; hh++)
19281 					if(teachersMatrix[tch][d][hh]>0){
19282 						nbroken++;
19283 						tmp=1;
19284 						break;
19285 					}
19286 				if(tmp>0)
19287 					break;
19288 			}
19289 
19290 			if(conflictsString!=nullptr && tmp>0){
19291 				QString s=tr("Time constraint activities end teachers' day broken for activity with id=%1 (%2), increases conflicts total by %3",
19292 				 "%1 is the id, %2 is the detailed description of the activity")
19293 				 .arg(r.internalActivitiesList[ai].id)
19294 				 .arg(getActivityDetailedDescription(r, r.internalActivitiesList[ai].id))
19295 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*tmp));
19296 
19297 				dl.append(s);
19298 				cl.append(weightPercentage/100*tmp);
19299 
19300 				*conflictsString+= s+"\n";
19301 			}
19302 		}
19303 	}
19304 
19305 	if(weightPercentage==100)
19306 		assert(nbroken==0);
19307 	return nbroken * weightPercentage/100;
19308 }
19309 
isRelatedToActivity(Rules & r,Activity * a)19310 bool ConstraintActivitiesEndTeachersDay::isRelatedToActivity(Rules& r, Activity* a)
19311 {
19312 	int it;
19313 
19314 	//check if this activity has the corresponding teacher
19315 	if(this->teacherName!=""){
19316 		it = a->teachersNames.indexOf(this->teacherName);
19317 		if(it==-1)
19318 			return false;
19319 	}
19320 	//check if this activity has the corresponding students
19321 	if(this->studentsName!=""){
19322 		bool commonStudents=false;
19323 		for(const QString& st : qAsConst(a->studentsNames)){
19324 			if(r.setsShareStudents(st, this->studentsName)){
19325 				commonStudents=true;
19326 				break;
19327 			}
19328 		}
19329 		if(!commonStudents)
19330 			return false;
19331 	}
19332 	//check if this activity has the corresponding subject
19333 	if(this->subjectName!="" && a->subjectName!=this->subjectName)
19334 		return false;
19335 	//check if this activity has the corresponding activity tag
19336 	if(this->activityTagName!="" && !a->activityTagsNames.contains(this->activityTagName))
19337 		return false;
19338 
19339 	return true;
19340 }
19341 
isRelatedToTeacher(Teacher * t)19342 bool ConstraintActivitiesEndTeachersDay::isRelatedToTeacher(Teacher* t)
19343 {
19344 	Q_UNUSED(t);
19345 
19346 	return false;
19347 }
19348 
isRelatedToSubject(Subject * s)19349 bool ConstraintActivitiesEndTeachersDay::isRelatedToSubject(Subject* s)
19350 {
19351 	Q_UNUSED(s);
19352 
19353 	return false;
19354 }
19355 
isRelatedToActivityTag(ActivityTag * s)19356 bool ConstraintActivitiesEndTeachersDay::isRelatedToActivityTag(ActivityTag* s)
19357 {
19358 	Q_UNUSED(s);
19359 
19360 	return false;
19361 }
19362 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)19363 bool ConstraintActivitiesEndTeachersDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
19364 {
19365 	Q_UNUSED(r);
19366 	Q_UNUSED(s);
19367 
19368 	return false;
19369 }
19370 
hasWrongDayOrHour(Rules & r)19371 bool ConstraintActivitiesEndTeachersDay::hasWrongDayOrHour(Rules& r)
19372 {
19373 	Q_UNUSED(r);
19374 	return false;
19375 }
19376 
canRepairWrongDayOrHour(Rules & r)19377 bool ConstraintActivitiesEndTeachersDay::canRepairWrongDayOrHour(Rules& r)
19378 {
19379 	Q_UNUSED(r);
19380 	assert(0);
19381 
19382 	return true;
19383 }
19384 
repairWrongDayOrHour(Rules & r)19385 bool ConstraintActivitiesEndTeachersDay::repairWrongDayOrHour(Rules& r)
19386 {
19387 	Q_UNUSED(r);
19388 	assert(0); //should check hasWrongDayOrHour, firstly
19389 
19390 	return true;
19391 }
19392 
19393 ///////////////////////////////////////////////////////////////////////////////////////////
19394 ///////////////////////////////////////////////////////////////////////////////////////////
19395 
ConstraintTeachersActivityTagMaxHoursDaily()19396 ConstraintTeachersActivityTagMaxHoursDaily::ConstraintTeachersActivityTagMaxHoursDaily()
19397 	: TimeConstraint()
19398 {
19399 	this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_DAILY;
19400 }
19401 
ConstraintTeachersActivityTagMaxHoursDaily(double wp,int maxhours,const QString & activityTag)19402 ConstraintTeachersActivityTagMaxHoursDaily::ConstraintTeachersActivityTagMaxHoursDaily(double wp, int maxhours, const QString& activityTag)
19403  : TimeConstraint(wp)
19404  {
19405 	assert(maxhours>0);
19406 	this->maxHoursDaily=maxhours;
19407 	this->activityTagName=activityTag;
19408 
19409 	this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_DAILY;
19410 }
19411 
computeInternalStructure(QWidget * parent,Rules & r)19412 bool ConstraintTeachersActivityTagMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
19413 {
19414 	Q_UNUSED(parent);
19415 
19416 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
19417 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
19418 	assert(this->activityTagIndex>=0);
19419 
19420 	this->canonicalTeachersList.clear();
19421 	for(int i=0; i<r.nInternalTeachers; i++){
19422 		bool found=false;
19423 
19424 		Teacher* tch=r.internalTeachersList[i];
19425 		for(int actIndex : qAsConst(tch->activitiesForTeacher)){
19426 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
19427 				found=true;
19428 				break;
19429 			}
19430 		}
19431 
19432 		if(found)
19433 			this->canonicalTeachersList.append(i);
19434 	}
19435 
19436 	return true;
19437 }
19438 
hasInactiveActivities(Rules & r)19439 bool ConstraintTeachersActivityTagMaxHoursDaily::hasInactiveActivities(Rules& r)
19440 {
19441 	Q_UNUSED(r);
19442 	return false;
19443 }
19444 
getXmlDescription(Rules & r)19445 QString ConstraintTeachersActivityTagMaxHoursDaily::getXmlDescription(Rules& r)
19446 {
19447 	Q_UNUSED(r);
19448 
19449 	QString s="<ConstraintTeachersActivityTagMaxHoursDaily>\n";
19450 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
19451 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
19452 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
19453 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
19454 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
19455 	s+="</ConstraintTeachersActivityTagMaxHoursDaily>\n";
19456 	return s;
19457 }
19458 
getDescription(Rules & r)19459 QString ConstraintTeachersActivityTagMaxHoursDaily::getDescription(Rules& r)
19460 {
19461 	Q_UNUSED(r);
19462 
19463 	QString begin=QString("");
19464 	if(!active)
19465 		begin="X - ";
19466 
19467 	QString end=QString("");
19468 	if(!comments.isEmpty())
19469 		end=", "+tr("C: %1", "Comments").arg(comments);
19470 
19471 	QString s;
19472 	s+="! ";
19473 	s+=tr("Teachers for activity tag %1 have max %2 hours daily").arg(this->activityTagName).arg(this->maxHoursDaily);s+=", ";
19474 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
19475 
19476 	return begin+s+end;
19477 }
19478 
getDetailedDescription(Rules & r)19479 QString ConstraintTeachersActivityTagMaxHoursDaily::getDetailedDescription(Rules& r)
19480 {
19481 	Q_UNUSED(r);
19482 
19483 	QString s=tr("Time constraint");s+="\n";
19484 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
19485 	s+=tr("All teachers, for an activity tag, must respect the maximum number of hours daily");s+="\n";
19486 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
19487 	s+=tr("Activity tag=%1").arg(this->activityTagName); s+="\n";
19488 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily); s+="\n";
19489 
19490 	if(!active){
19491 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
19492 		s+="\n";
19493 	}
19494 	if(!comments.isEmpty()){
19495 		s+=tr("Comments=%1").arg(comments);
19496 		s+="\n";
19497 	}
19498 
19499 	return s;
19500 }
19501 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)19502 double ConstraintTeachersActivityTagMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
19503 {
19504 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
19505 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
19506 		c.teachersMatrixReady=true;
19507 		c.subgroupsMatrixReady=true;
19508 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
19509 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
19510 
19511 		c.changedForMatrixCalculation=false;
19512 	}
19513 
19514 	int nbroken;
19515 
19516 	Matrix2D<int> crtTeacherTimetableActivityTag;
19517 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
19518 
19519 	nbroken=0;
19520 	for(int i : qAsConst(this->canonicalTeachersList)){
19521 		Teacher* tch=r.internalTeachersList[i];
19522 		for(int d=0; d<r.nDaysPerWeek; d++)
19523 			for(int h=0; h<r.nHoursPerDay; h++)
19524 				crtTeacherTimetableActivityTag[d][h]=-1;
19525 
19526 		for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
19527 			int d=c.times[ai]%r.nDaysPerWeek;
19528 			int h=c.times[ai]/r.nDaysPerWeek;
19529 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
19530 				assert(h+dur<r.nHoursPerDay);
19531 				assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
19532 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
19533 					crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
19534 			}
19535 		}
19536 
19537 		for(int d=0; d<r.nDaysPerWeek; d++){
19538 			int nd=0;
19539 			for(int h=0; h<r.nHoursPerDay; h++)
19540 				if(crtTeacherTimetableActivityTag[d][h]==this->activityTagIndex)
19541 					nd++;
19542 
19543 			if(nd>this->maxHoursDaily){
19544 				nbroken++;
19545 
19546 				if(conflictsString!=nullptr){
19547 					QString s=(tr("Time constraint teachers activity tag %1 max %2 hours daily broken for teacher %3, on day %4, length=%5.")
19548 					 .arg(this->activityTagName)
19549 					 .arg(CustomFETString::number(this->maxHoursDaily))
19550 					 .arg(r.internalTeachersList[i]->name)
19551 					 .arg(r.daysOfTheWeek[d])
19552 					 .arg(nd)
19553 					 )
19554 					 +
19555 					 " "
19556 					 +
19557 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
19558 
19559 					dl.append(s);
19560 					cl.append(weightPercentage/100.0);
19561 
19562 					*conflictsString+= s+"\n";
19563 				}
19564 			}
19565 		}
19566 	}
19567 
19568 	if(weightPercentage==100.0)
19569 		assert(nbroken==0);
19570 	return weightPercentage/100.0 * nbroken;
19571 }
19572 
isRelatedToActivity(Rules & r,Activity * a)19573 bool ConstraintTeachersActivityTagMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
19574 {
19575 	Q_UNUSED(r);
19576 	Q_UNUSED(a);
19577 
19578 	return false;
19579 }
19580 
isRelatedToTeacher(Teacher * t)19581 bool ConstraintTeachersActivityTagMaxHoursDaily::isRelatedToTeacher(Teacher* t)
19582 {
19583 	Q_UNUSED(t);
19584 
19585 	return true;
19586 }
19587 
isRelatedToSubject(Subject * s)19588 bool ConstraintTeachersActivityTagMaxHoursDaily::isRelatedToSubject(Subject* s)
19589 {
19590 	Q_UNUSED(s);
19591 
19592 	return false;
19593 }
19594 
isRelatedToActivityTag(ActivityTag * s)19595 bool ConstraintTeachersActivityTagMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
19596 {
19597 	return s->name==this->activityTagName;
19598 }
19599 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)19600 bool ConstraintTeachersActivityTagMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
19601 {
19602 	Q_UNUSED(r);
19603 	Q_UNUSED(s);
19604 
19605 	return false;
19606 }
19607 
hasWrongDayOrHour(Rules & r)19608 bool ConstraintTeachersActivityTagMaxHoursDaily::hasWrongDayOrHour(Rules& r)
19609 {
19610 	if(maxHoursDaily>r.nHoursPerDay)
19611 		return true;
19612 
19613 	return false;
19614 }
19615 
canRepairWrongDayOrHour(Rules & r)19616 bool ConstraintTeachersActivityTagMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
19617 {
19618 	assert(hasWrongDayOrHour(r));
19619 
19620 	return true;
19621 }
19622 
repairWrongDayOrHour(Rules & r)19623 bool ConstraintTeachersActivityTagMaxHoursDaily::repairWrongDayOrHour(Rules& r)
19624 {
19625 	assert(hasWrongDayOrHour(r));
19626 
19627 	if(maxHoursDaily>r.nHoursPerDay)
19628 		maxHoursDaily=r.nHoursPerDay;
19629 
19630 	return true;
19631 }
19632 
19633 ///////////////////////////////////////////////////////////////////////////////////////////
19634 ///////////////////////////////////////////////////////////////////////////////////////////
19635 
ConstraintTeacherActivityTagMaxHoursDaily()19636 ConstraintTeacherActivityTagMaxHoursDaily::ConstraintTeacherActivityTagMaxHoursDaily()
19637 	: TimeConstraint()
19638 {
19639 	this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_DAILY;
19640 }
19641 
ConstraintTeacherActivityTagMaxHoursDaily(double wp,int maxhours,const QString & teacher,const QString & activityTag)19642 ConstraintTeacherActivityTagMaxHoursDaily::ConstraintTeacherActivityTagMaxHoursDaily(double wp, int maxhours, const QString& teacher, const QString& activityTag)
19643  : TimeConstraint(wp)
19644  {
19645 	assert(maxhours>0);
19646 	this->maxHoursDaily=maxhours;
19647 	this->teacherName=teacher;
19648 	this->activityTagName=activityTag;
19649 
19650 	this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_DAILY;
19651 }
19652 
computeInternalStructure(QWidget * parent,Rules & r)19653 bool ConstraintTeacherActivityTagMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
19654 {
19655 	Q_UNUSED(parent);
19656 
19657 	//this->teacher_ID=r.searchTeacher(this->teacherName);
19658 	teacher_ID=r.teachersHash.value(teacherName, -1);
19659 	assert(this->teacher_ID>=0);
19660 
19661 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
19662 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
19663 	assert(this->activityTagIndex>=0);
19664 
19665 	this->canonicalTeachersList.clear();
19666 	int i=this->teacher_ID;
19667 	bool found=false;
19668 
19669 	Teacher* tch=r.internalTeachersList[i];
19670 	for(int actIndex : qAsConst(tch->activitiesForTeacher)){
19671 		if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
19672 			found=true;
19673 			break;
19674 		}
19675 	}
19676 
19677 	if(found)
19678 		this->canonicalTeachersList.append(i);
19679 
19680 	return true;
19681 }
19682 
hasInactiveActivities(Rules & r)19683 bool ConstraintTeacherActivityTagMaxHoursDaily::hasInactiveActivities(Rules& r)
19684 {
19685 	Q_UNUSED(r);
19686 	return false;
19687 }
19688 
getXmlDescription(Rules & r)19689 QString ConstraintTeacherActivityTagMaxHoursDaily::getXmlDescription(Rules& r)
19690 {
19691 	Q_UNUSED(r);
19692 
19693 	QString s="<ConstraintTeacherActivityTagMaxHoursDaily>\n";
19694 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
19695 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
19696 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
19697 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
19698 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
19699 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
19700 	s+="</ConstraintTeacherActivityTagMaxHoursDaily>\n";
19701 	return s;
19702 }
19703 
getDescription(Rules & r)19704 QString ConstraintTeacherActivityTagMaxHoursDaily::getDescription(Rules& r)
19705 {
19706 	Q_UNUSED(r);
19707 
19708 	QString begin=QString("");
19709 	if(!active)
19710 		begin="X - ";
19711 
19712 	QString end=QString("");
19713 	if(!comments.isEmpty())
19714 		end=", "+tr("C: %1", "Comments").arg(comments);
19715 
19716 	QString s;
19717 	s+="! ";
19718 	s+=tr("Teacher %1 for activity tag %2 has max %3 hours daily").arg(this->teacherName).arg(this->activityTagName).arg(this->maxHoursDaily);s+=", ";
19719 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
19720 
19721 	return begin+s+end;
19722 }
19723 
getDetailedDescription(Rules & r)19724 QString ConstraintTeacherActivityTagMaxHoursDaily::getDetailedDescription(Rules& r)
19725 {
19726 	Q_UNUSED(r);
19727 
19728 	QString s=tr("Time constraint");s+="\n";
19729 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
19730 	s+=tr("A teacher for an activity tag must respect the maximum number of hours daily");s+="\n";
19731 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
19732 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
19733 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
19734 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily); s+="\n";
19735 
19736 	if(!active){
19737 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
19738 		s+="\n";
19739 	}
19740 	if(!comments.isEmpty()){
19741 		s+=tr("Comments=%1").arg(comments);
19742 		s+="\n";
19743 	}
19744 
19745 	return s;
19746 }
19747 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)19748 double ConstraintTeacherActivityTagMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
19749 {
19750 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
19751 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
19752 		c.teachersMatrixReady=true;
19753 		c.subgroupsMatrixReady=true;
19754 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
19755 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
19756 
19757 		c.changedForMatrixCalculation=false;
19758 	}
19759 
19760 	int nbroken;
19761 
19762 	Matrix2D<int> crtTeacherTimetableActivityTag;
19763 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
19764 
19765 	nbroken=0;
19766 	for(int i : qAsConst(this->canonicalTeachersList)){
19767 		Teacher* tch=r.internalTeachersList[i];
19768 		for(int d=0; d<r.nDaysPerWeek; d++)
19769 			for(int h=0; h<r.nHoursPerDay; h++)
19770 				crtTeacherTimetableActivityTag[d][h]=-1;
19771 
19772 		for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
19773 			int d=c.times[ai]%r.nDaysPerWeek;
19774 			int h=c.times[ai]/r.nDaysPerWeek;
19775 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
19776 				assert(h+dur<r.nHoursPerDay);
19777 				assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
19778 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
19779 					crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
19780 			}
19781 		}
19782 
19783 		for(int d=0; d<r.nDaysPerWeek; d++){
19784 			int nd=0;
19785 			for(int h=0; h<r.nHoursPerDay; h++)
19786 				if(crtTeacherTimetableActivityTag[d][h]==this->activityTagIndex)
19787 					nd++;
19788 
19789 			if(nd>this->maxHoursDaily){
19790 				nbroken++;
19791 
19792 				if(conflictsString!=nullptr){
19793 					QString s=(tr("Time constraint teacher activity tag %1 max %2 hours daily broken for teacher %3, on day %4, length=%5.")
19794 					 .arg(this->activityTagName)
19795 					 .arg(CustomFETString::number(this->maxHoursDaily))
19796 					 .arg(r.internalTeachersList[i]->name)
19797 					 .arg(r.daysOfTheWeek[d])
19798 					 .arg(nd)
19799 					 )
19800 					 +
19801 					 " "
19802 					 +
19803 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
19804 
19805 					dl.append(s);
19806 					cl.append(weightPercentage/100.0);
19807 
19808 					*conflictsString+= s+"\n";
19809 				}
19810 			}
19811 		}
19812 	}
19813 
19814 	if(weightPercentage==100.0)
19815 		assert(nbroken==0);
19816 	return weightPercentage/100.0 * nbroken;
19817 }
19818 
isRelatedToActivity(Rules & r,Activity * a)19819 bool ConstraintTeacherActivityTagMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
19820 {
19821 	Q_UNUSED(r);
19822 	Q_UNUSED(a);
19823 
19824 	return false;
19825 }
19826 
isRelatedToTeacher(Teacher * t)19827 bool ConstraintTeacherActivityTagMaxHoursDaily::isRelatedToTeacher(Teacher* t)
19828 {
19829 	if(this->teacherName==t->name)
19830 		return true;
19831 	return false;
19832 }
19833 
isRelatedToSubject(Subject * s)19834 bool ConstraintTeacherActivityTagMaxHoursDaily::isRelatedToSubject(Subject* s)
19835 {
19836 	Q_UNUSED(s);
19837 
19838 	return false;
19839 }
19840 
isRelatedToActivityTag(ActivityTag * s)19841 bool ConstraintTeacherActivityTagMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
19842 {
19843 	return this->activityTagName==s->name;
19844 }
19845 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)19846 bool ConstraintTeacherActivityTagMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
19847 {
19848 	Q_UNUSED(r);
19849 	Q_UNUSED(s);
19850 
19851 	return false;
19852 }
19853 
hasWrongDayOrHour(Rules & r)19854 bool ConstraintTeacherActivityTagMaxHoursDaily::hasWrongDayOrHour(Rules& r)
19855 {
19856 	if(maxHoursDaily>r.nHoursPerDay)
19857 		return true;
19858 
19859 	return false;
19860 }
19861 
canRepairWrongDayOrHour(Rules & r)19862 bool ConstraintTeacherActivityTagMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
19863 {
19864 	assert(hasWrongDayOrHour(r));
19865 
19866 	return true;
19867 }
19868 
repairWrongDayOrHour(Rules & r)19869 bool ConstraintTeacherActivityTagMaxHoursDaily::repairWrongDayOrHour(Rules& r)
19870 {
19871 	assert(hasWrongDayOrHour(r));
19872 
19873 	if(maxHoursDaily>r.nHoursPerDay)
19874 		maxHoursDaily=r.nHoursPerDay;
19875 
19876 	return true;
19877 }
19878 
19879 ////////////////////////////////////////////////////////////////////////////////////////////
19880 ////////////////////////////////////////////////////////////////////////////////////////////
19881 
ConstraintStudentsActivityTagMaxHoursDaily()19882 ConstraintStudentsActivityTagMaxHoursDaily::ConstraintStudentsActivityTagMaxHoursDaily()
19883 	: TimeConstraint()
19884 {
19885 	this->type = CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_DAILY;
19886 	this->maxHoursDaily = -1;
19887 }
19888 
ConstraintStudentsActivityTagMaxHoursDaily(double wp,int maxnh,const QString & activityTag)19889 ConstraintStudentsActivityTagMaxHoursDaily::ConstraintStudentsActivityTagMaxHoursDaily(double wp, int maxnh, const QString& activityTag)
19890 	: TimeConstraint(wp)
19891 {
19892 	this->maxHoursDaily = maxnh;
19893 	this->activityTagName=activityTag;
19894 	this->type = CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_DAILY;
19895 }
19896 
computeInternalStructure(QWidget * parent,Rules & r)19897 bool ConstraintStudentsActivityTagMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
19898 {
19899 	Q_UNUSED(parent);
19900 
19901 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
19902 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
19903 	assert(this->activityTagIndex>=0);
19904 
19905 	this->canonicalSubgroupsList.clear();
19906 	for(int i=0; i<r.nInternalSubgroups; i++){
19907 		bool found=false;
19908 
19909 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
19910 		for(int actIndex : qAsConst(sbg->activitiesForSubgroup)){
19911 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
19912 				found=true;
19913 				break;
19914 			}
19915 		}
19916 
19917 		if(found)
19918 			this->canonicalSubgroupsList.append(i);
19919 	}
19920 
19921 	return true;
19922 }
19923 
hasInactiveActivities(Rules & r)19924 bool ConstraintStudentsActivityTagMaxHoursDaily::hasInactiveActivities(Rules& r)
19925 {
19926 	Q_UNUSED(r);
19927 	return false;
19928 }
19929 
getXmlDescription(Rules & r)19930 QString ConstraintStudentsActivityTagMaxHoursDaily::getXmlDescription(Rules& r)
19931 {
19932 	Q_UNUSED(r);
19933 
19934 	QString s="<ConstraintStudentsActivityTagMaxHoursDaily>\n";
19935 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
19936 
19937 	s+="	<Activity_Tag>"+protect(this->activityTagName)+"</Activity_Tag>\n";
19938 
19939 	if(this->maxHoursDaily>=0)
19940 		s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
19941 	else
19942 		assert(0);
19943 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
19944 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
19945 	s+="</ConstraintStudentsActivityTagMaxHoursDaily>\n";
19946 	return s;
19947 }
19948 
getDescription(Rules & r)19949 QString ConstraintStudentsActivityTagMaxHoursDaily::getDescription(Rules& r)
19950 {
19951 	Q_UNUSED(r);
19952 
19953 	QString begin=QString("");
19954 	if(!active)
19955 		begin="X - ";
19956 
19957 	QString end=QString("");
19958 	if(!comments.isEmpty())
19959 		end=", "+tr("C: %1", "Comments").arg(comments);
19960 
19961 	QString s;
19962 	s+="! ";
19963 	s+=tr("Students for activity tag %1 have max %2 hours daily")
19964 		.arg(this->activityTagName).arg(this->maxHoursDaily); s+=", ";
19965 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
19966 
19967 	return begin+s+end;
19968 }
19969 
getDetailedDescription(Rules & r)19970 QString ConstraintStudentsActivityTagMaxHoursDaily::getDetailedDescription(Rules& r)
19971 {
19972 	Q_UNUSED(r);
19973 
19974 	QString s=tr("Time constraint");s+="\n";
19975 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
19976 	s+=tr("All students, for an activity tag, must respect the maximum number of hours daily"); s+="\n";
19977 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
19978 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
19979 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
19980 
19981 	if(!active){
19982 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
19983 		s+="\n";
19984 	}
19985 	if(!comments.isEmpty()){
19986 		s+=tr("Comments=%1").arg(comments);
19987 		s+="\n";
19988 	}
19989 
19990 	return s;
19991 }
19992 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)19993 double ConstraintStudentsActivityTagMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
19994 {
19995 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
19996 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
19997 		c.teachersMatrixReady=true;
19998 		c.subgroupsMatrixReady=true;
19999 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
20000 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
20001 
20002 		c.changedForMatrixCalculation=false;
20003 	}
20004 
20005 	int nbroken;
20006 
20007 	nbroken=0;
20008 
20009 	Matrix2D<int> crtSubgroupTimetableActivityTag;
20010 	crtSubgroupTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
20011 
20012 	for(int i : qAsConst(this->canonicalSubgroupsList)){
20013 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
20014 		for(int d=0; d<r.nDaysPerWeek; d++)
20015 			for(int h=0; h<r.nHoursPerDay; h++)
20016 				crtSubgroupTimetableActivityTag[d][h]=-1;
20017 		for(int ai : qAsConst(sbg->activitiesForSubgroup)) if(c.times[ai]!=UNALLOCATED_TIME){
20018 			int d=c.times[ai]%r.nDaysPerWeek;
20019 			int h=c.times[ai]/r.nDaysPerWeek;
20020 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
20021 				assert(h+dur<r.nHoursPerDay);
20022 				assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
20023 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
20024 					crtSubgroupTimetableActivityTag[d][h+dur]=this->activityTagIndex;
20025 			}
20026 		}
20027 
20028 		for(int d=0; d<r.nDaysPerWeek; d++){
20029 			int nd=0;
20030 			for(int h=0; h<r.nHoursPerDay; h++)
20031 				if(crtSubgroupTimetableActivityTag[d][h]==this->activityTagIndex)
20032 					nd++;
20033 
20034 			if(nd>this->maxHoursDaily){
20035 				nbroken++;
20036 
20037 				if(conflictsString!=nullptr){
20038 					QString s=(tr(
20039 					 "Time constraint students, activity tag %1, max %2 hours daily, broken for subgroup %3, on day %4, length=%5.")
20040 					 .arg(this->activityTagName)
20041 					 .arg(CustomFETString::number(this->maxHoursDaily))
20042 					 .arg(r.internalSubgroupsList[i]->name)
20043 					 .arg(r.daysOfTheWeek[d])
20044 					 .arg(nd)
20045 					 )
20046 					 +
20047 					 " "
20048 					 +
20049 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
20050 
20051 					dl.append(s);
20052 					cl.append(weightPercentage/100);
20053 
20054 					*conflictsString+= s+"\n";
20055 				}
20056 			}
20057 		}
20058 	}
20059 
20060 	if(weightPercentage==100.0)
20061 		assert(nbroken==0);
20062 	return weightPercentage/100.0 * nbroken;
20063 }
20064 
isRelatedToActivity(Rules & r,Activity * a)20065 bool ConstraintStudentsActivityTagMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
20066 {
20067 	Q_UNUSED(r);
20068 	Q_UNUSED(a);
20069 
20070 	return false;
20071 }
20072 
isRelatedToTeacher(Teacher * t)20073 bool ConstraintStudentsActivityTagMaxHoursDaily::isRelatedToTeacher(Teacher* t)
20074 {
20075 	Q_UNUSED(t);
20076 
20077 	return false;
20078 }
20079 
isRelatedToSubject(Subject * s)20080 bool ConstraintStudentsActivityTagMaxHoursDaily::isRelatedToSubject(Subject* s)
20081 {
20082 	Q_UNUSED(s);
20083 
20084 	return false;
20085 }
20086 
isRelatedToActivityTag(ActivityTag * s)20087 bool ConstraintStudentsActivityTagMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
20088 {
20089 	return s->name==this->activityTagName;
20090 }
20091 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)20092 bool ConstraintStudentsActivityTagMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
20093 {
20094 	Q_UNUSED(r);
20095 	Q_UNUSED(s);
20096 
20097 	return true;
20098 }
20099 
hasWrongDayOrHour(Rules & r)20100 bool ConstraintStudentsActivityTagMaxHoursDaily::hasWrongDayOrHour(Rules& r)
20101 {
20102 	if(maxHoursDaily>r.nHoursPerDay)
20103 		return true;
20104 
20105 	return false;
20106 }
20107 
canRepairWrongDayOrHour(Rules & r)20108 bool ConstraintStudentsActivityTagMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
20109 {
20110 	assert(hasWrongDayOrHour(r));
20111 
20112 	return true;
20113 }
20114 
repairWrongDayOrHour(Rules & r)20115 bool ConstraintStudentsActivityTagMaxHoursDaily::repairWrongDayOrHour(Rules& r)
20116 {
20117 	assert(hasWrongDayOrHour(r));
20118 
20119 	if(maxHoursDaily>r.nHoursPerDay)
20120 		maxHoursDaily=r.nHoursPerDay;
20121 
20122 	return true;
20123 }
20124 
20125 ////////////////////////////////////////////////////////////////////////////////////////////
20126 ////////////////////////////////////////////////////////////////////////////////////////////
20127 
ConstraintStudentsSetActivityTagMaxHoursDaily()20128 ConstraintStudentsSetActivityTagMaxHoursDaily::ConstraintStudentsSetActivityTagMaxHoursDaily()
20129 	: TimeConstraint()
20130 {
20131 	this->type = CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_DAILY;
20132 	this->maxHoursDaily = -1;
20133 }
20134 
ConstraintStudentsSetActivityTagMaxHoursDaily(double wp,int maxnh,const QString & s,const QString & activityTag)20135 ConstraintStudentsSetActivityTagMaxHoursDaily::ConstraintStudentsSetActivityTagMaxHoursDaily(double wp, int maxnh, const QString& s, const QString& activityTag)
20136 	: TimeConstraint(wp)
20137 {
20138 	this->maxHoursDaily = maxnh;
20139 	this->students = s;
20140 	this->activityTagName=activityTag;
20141 	this->type = CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_DAILY;
20142 }
20143 
hasInactiveActivities(Rules & r)20144 bool ConstraintStudentsSetActivityTagMaxHoursDaily::hasInactiveActivities(Rules& r)
20145 {
20146 	Q_UNUSED(r);
20147 	return false;
20148 }
20149 
getXmlDescription(Rules & r)20150 QString ConstraintStudentsSetActivityTagMaxHoursDaily::getXmlDescription(Rules& r)
20151 {
20152 	Q_UNUSED(r);
20153 
20154 	QString s="<ConstraintStudentsSetActivityTagMaxHoursDaily>\n";
20155 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
20156 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
20157 	s+="	<Students>"+protect(this->students)+"</Students>\n";
20158 	s+="	<Activity_Tag>"+protect(this->activityTagName)+"</Activity_Tag>\n";
20159 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
20160 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
20161 	s+="</ConstraintStudentsSetActivityTagMaxHoursDaily>\n";
20162 	return s;
20163 }
20164 
getDescription(Rules & r)20165 QString ConstraintStudentsSetActivityTagMaxHoursDaily::getDescription(Rules& r)
20166 {
20167 	Q_UNUSED(r);
20168 
20169 	QString begin=QString("");
20170 	if(!active)
20171 		begin="X - ";
20172 
20173 	QString end=QString("");
20174 	if(!comments.isEmpty())
20175 		end=", "+tr("C: %1", "Comments").arg(comments);
20176 
20177 	QString s;
20178 	s+="! ";
20179 	s+=tr("Students set %1 for activity tag %2 has max %3 hours daily").arg(this->students).arg(this->activityTagName).arg(this->maxHoursDaily);
20180 	s+=", ";
20181 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
20182 
20183 	return begin+s+end;
20184 }
20185 
getDetailedDescription(Rules & r)20186 QString ConstraintStudentsSetActivityTagMaxHoursDaily::getDetailedDescription(Rules& r)
20187 {
20188 	Q_UNUSED(r);
20189 
20190 	QString s=tr("Time constraint");s+="\n";
20191 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
20192 	s+=tr("A students set, for an activity tag, must respect the maximum number of hours daily"); s+="\n";
20193 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
20194 	s+=tr("Students set=%1").arg(this->students);s+="\n";
20195 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
20196 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
20197 
20198 	if(!active){
20199 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
20200 		s+="\n";
20201 	}
20202 	if(!comments.isEmpty()){
20203 		s+=tr("Comments=%1").arg(comments);
20204 		s+="\n";
20205 	}
20206 
20207 	return s;
20208 }
20209 
computeInternalStructure(QWidget * parent,Rules & r)20210 bool ConstraintStudentsSetActivityTagMaxHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
20211 {
20212 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
20213 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
20214 	assert(this->activityTagIndex>=0);
20215 
20216 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
20217 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
20218 
20219 	if(ss==nullptr){
20220 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
20221 		 tr("Constraint students set max hours daily is wrong because it refers to inexistent students set."
20222 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
20223 
20224 		return false;
20225 	}
20226 
20227 	assert(ss!=nullptr);
20228 
20229 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
20230 	/*this->iSubgroupsList.clear();
20231 	if(ss->type==STUDENTS_SUBGROUP){
20232 		int tmp;
20233 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
20234 		assert(tmp>=0);
20235 		assert(tmp<r.nInternalSubgroups);
20236 		if(!this->iSubgroupsList.contains(tmp))
20237 			this->iSubgroupsList.append(tmp);
20238 	}
20239 	else if(ss->type==STUDENTS_GROUP){
20240 		StudentsGroup* stg=(StudentsGroup*)ss;
20241 		for(int i=0; i<stg->subgroupsList.size(); i++){
20242 			StudentsSubgroup* sts=stg->subgroupsList[i];
20243 			int tmp;
20244 			tmp=sts->indexInInternalSubgroupsList;
20245 			assert(tmp>=0);
20246 			assert(tmp<r.nInternalSubgroups);
20247 			if(!this->iSubgroupsList.contains(tmp))
20248 				this->iSubgroupsList.append(tmp);
20249 		}
20250 	}
20251 	else if(ss->type==STUDENTS_YEAR){
20252 		StudentsYear* sty=(StudentsYear*)ss;
20253 		for(int i=0; i<sty->groupsList.size(); i++){
20254 			StudentsGroup* stg=sty->groupsList[i];
20255 			for(int j=0; j<stg->subgroupsList.size(); j++){
20256 				StudentsSubgroup* sts=stg->subgroupsList[j];
20257 				int tmp;
20258 				tmp=sts->indexInInternalSubgroupsList;
20259 				assert(tmp>=0);
20260 				assert(tmp<r.nInternalSubgroups);
20261 				if(!this->iSubgroupsList.contains(tmp))
20262 					this->iSubgroupsList.append(tmp);
20263 			}
20264 		}
20265 	}
20266 	else
20267 		assert(0);*/
20268 
20269 	/////////////
20270 	this->canonicalSubgroupsList.clear();
20271 	for(int i : qAsConst(this->iSubgroupsList)){
20272 		bool found=false;
20273 
20274 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
20275 		for(int actIndex : qAsConst(sbg->activitiesForSubgroup)){
20276 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
20277 				found=true;
20278 				break;
20279 			}
20280 		}
20281 
20282 		if(found)
20283 			this->canonicalSubgroupsList.append(i);
20284 	}
20285 
20286 	return true;
20287 }
20288 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)20289 double ConstraintStudentsSetActivityTagMaxHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
20290 {
20291 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
20292 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
20293 		c.teachersMatrixReady=true;
20294 		c.subgroupsMatrixReady=true;
20295 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
20296 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
20297 
20298 		c.changedForMatrixCalculation=false;
20299 	}
20300 
20301 	int nbroken;
20302 
20303 	nbroken=0;
20304 
20305 	Matrix2D<int> crtSubgroupTimetableActivityTag;
20306 	crtSubgroupTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
20307 
20308 	for(int i : qAsConst(this->canonicalSubgroupsList)){
20309 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
20310 		for(int d=0; d<r.nDaysPerWeek; d++)
20311 			for(int h=0; h<r.nHoursPerDay; h++)
20312 				crtSubgroupTimetableActivityTag[d][h]=-1;
20313 		for(int ai : qAsConst(sbg->activitiesForSubgroup)) if(c.times[ai]!=UNALLOCATED_TIME){
20314 			int d=c.times[ai]%r.nDaysPerWeek;
20315 			int h=c.times[ai]/r.nDaysPerWeek;
20316 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
20317 				assert(h+dur<r.nHoursPerDay);
20318 				assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
20319 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
20320 					crtSubgroupTimetableActivityTag[d][h+dur]=this->activityTagIndex;
20321 			}
20322 		}
20323 
20324 		for(int d=0; d<r.nDaysPerWeek; d++){
20325 			int nd=0;
20326 			for(int h=0; h<r.nHoursPerDay; h++)
20327 				if(crtSubgroupTimetableActivityTag[d][h]==this->activityTagIndex)
20328 					nd++;
20329 
20330 			if(nd>this->maxHoursDaily){
20331 				nbroken++;
20332 
20333 				if(conflictsString!=nullptr){
20334 					QString s=(tr(
20335 					 "Time constraint students set, activity tag %1, max %2 hours daily, broken for subgroup %3, on day %4, length=%5.")
20336 					 .arg(this->activityTagName)
20337 					 .arg(CustomFETString::number(this->maxHoursDaily))
20338 					 .arg(r.internalSubgroupsList[i]->name)
20339 					 .arg(r.daysOfTheWeek[d])
20340 					 .arg(nd)
20341 					 )
20342 					 +
20343 					 " "
20344 					 +
20345 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
20346 
20347 					dl.append(s);
20348 					cl.append(weightPercentage/100);
20349 
20350 					*conflictsString+= s+"\n";
20351 				}
20352 			}
20353 		}
20354 	}
20355 
20356 	if(weightPercentage==100.0)
20357 		assert(nbroken==0);
20358 	return weightPercentage/100.0 * nbroken;
20359 }
20360 
isRelatedToActivity(Rules & r,Activity * a)20361 bool ConstraintStudentsSetActivityTagMaxHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
20362 {
20363 	Q_UNUSED(r);
20364 	Q_UNUSED(a);
20365 
20366 	return false;
20367 }
20368 
isRelatedToTeacher(Teacher * t)20369 bool ConstraintStudentsSetActivityTagMaxHoursDaily::isRelatedToTeacher(Teacher* t)
20370 {
20371 	Q_UNUSED(t);
20372 
20373 	return false;
20374 }
20375 
isRelatedToSubject(Subject * s)20376 bool ConstraintStudentsSetActivityTagMaxHoursDaily::isRelatedToSubject(Subject* s)
20377 {
20378 	Q_UNUSED(s);
20379 
20380 	return false;
20381 }
20382 
isRelatedToActivityTag(ActivityTag * s)20383 bool ConstraintStudentsSetActivityTagMaxHoursDaily::isRelatedToActivityTag(ActivityTag* s)
20384 {
20385 	Q_UNUSED(s);
20386 
20387 	return false;
20388 }
20389 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)20390 bool ConstraintStudentsSetActivityTagMaxHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
20391 {
20392 	return r.setsShareStudents(this->students, s->name);
20393 }
20394 
hasWrongDayOrHour(Rules & r)20395 bool ConstraintStudentsSetActivityTagMaxHoursDaily::hasWrongDayOrHour(Rules& r)
20396 {
20397 	if(maxHoursDaily>r.nHoursPerDay)
20398 		return true;
20399 
20400 	return false;
20401 }
20402 
canRepairWrongDayOrHour(Rules & r)20403 bool ConstraintStudentsSetActivityTagMaxHoursDaily::canRepairWrongDayOrHour(Rules& r)
20404 {
20405 	assert(hasWrongDayOrHour(r));
20406 
20407 	return true;
20408 }
20409 
repairWrongDayOrHour(Rules & r)20410 bool ConstraintStudentsSetActivityTagMaxHoursDaily::repairWrongDayOrHour(Rules& r)
20411 {
20412 	assert(hasWrongDayOrHour(r));
20413 
20414 	if(maxHoursDaily>r.nHoursPerDay)
20415 		maxHoursDaily=r.nHoursPerDay;
20416 
20417 	return true;
20418 }
20419 
20420 ///////////////////////////////////////////////////////////////////////////////////////////
20421 ///////////////////////////////////////////////////////////////////////////////////////////
20422 
ConstraintTeachersActivityTagMinHoursDaily()20423 ConstraintTeachersActivityTagMinHoursDaily::ConstraintTeachersActivityTagMinHoursDaily()
20424 	: TimeConstraint()
20425 {
20426 	this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MIN_HOURS_DAILY;
20427 }
20428 
ConstraintTeachersActivityTagMinHoursDaily(double wp,int minhours,bool allowemptydays,const QString & activityTag)20429 ConstraintTeachersActivityTagMinHoursDaily::ConstraintTeachersActivityTagMinHoursDaily(double wp, int minhours, bool allowemptydays, const QString& activityTag)
20430  : TimeConstraint(wp)
20431  {
20432 	assert(minhours>0);
20433 	this->minHoursDaily=minhours;
20434 	this->allowEmptyDays=allowemptydays;
20435 	this->activityTagName=activityTag;
20436 
20437 	this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MIN_HOURS_DAILY;
20438 }
20439 
computeInternalStructure(QWidget * parent,Rules & r)20440 bool ConstraintTeachersActivityTagMinHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
20441 {
20442 	Q_UNUSED(parent);
20443 
20444 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
20445 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
20446 	assert(this->activityTagIndex>=0);
20447 
20448 	this->canonicalTeachersList.clear();
20449 	for(int i=0; i<r.nInternalTeachers; i++){
20450 		bool found=false;
20451 
20452 		Teacher* tch=r.internalTeachersList[i];
20453 		for(int actIndex : qAsConst(tch->activitiesForTeacher)){
20454 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
20455 				found=true;
20456 				break;
20457 			}
20458 		}
20459 
20460 		if(found)
20461 			this->canonicalTeachersList.append(i);
20462 	}
20463 
20464 	return true;
20465 }
20466 
hasInactiveActivities(Rules & r)20467 bool ConstraintTeachersActivityTagMinHoursDaily::hasInactiveActivities(Rules& r)
20468 {
20469 	Q_UNUSED(r);
20470 	return false;
20471 }
20472 
getXmlDescription(Rules & r)20473 QString ConstraintTeachersActivityTagMinHoursDaily::getXmlDescription(Rules& r)
20474 {
20475 	Q_UNUSED(r);
20476 
20477 	QString s="<ConstraintTeachersActivityTagMinHoursDaily>\n";
20478 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
20479 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
20480 	s+="	<Minimum_Hours_Daily>"+CustomFETString::number(this->minHoursDaily)+"</Minimum_Hours_Daily>\n";
20481 	if(this->allowEmptyDays)
20482 		s+="	<Allow_Empty_Days>true</Allow_Empty_Days>\n";
20483 	else
20484 		s+="	<Allow_Empty_Days>false</Allow_Empty_Days>\n";
20485 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
20486 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
20487 	s+="</ConstraintTeachersActivityTagMinHoursDaily>\n";
20488 	return s;
20489 }
20490 
getDescription(Rules & r)20491 QString ConstraintTeachersActivityTagMinHoursDaily::getDescription(Rules& r)
20492 {
20493 	Q_UNUSED(r);
20494 
20495 	QString begin=QString("");
20496 	if(!active)
20497 		begin="X - ";
20498 
20499 	QString end=QString("");
20500 	if(!comments.isEmpty())
20501 		end=", "+tr("C: %1", "Comments").arg(comments);
20502 
20503 	QString s;
20504 	s+="! ";
20505 	s+=tr("Teachers for activity tag %1 have min %2 hours daily").arg(this->activityTagName).arg(this->minHoursDaily);s+=", ";
20506 	s+=tr("AED:%1", "Allow empty days").arg(yesNoTranslated(this->allowEmptyDays));s+=", ";
20507 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
20508 
20509 	return begin+s+end;
20510 }
20511 
getDetailedDescription(Rules & r)20512 QString ConstraintTeachersActivityTagMinHoursDaily::getDetailedDescription(Rules& r)
20513 {
20514 	Q_UNUSED(r);
20515 
20516 	QString s=tr("Time constraint");s+="\n";
20517 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
20518 	s+=tr("All teachers, for an activity tag, must respect the minimum number of hours daily");s+="\n";
20519 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
20520 	s+=tr("Activity tag=%1").arg(this->activityTagName); s+="\n";
20521 	s+=tr("Minimum hours daily=%1").arg(this->minHoursDaily); s+="\n";
20522 	s+=tr("Allow empty days=%1").arg(yesNoTranslated(this->allowEmptyDays));s+="\n";
20523 
20524 	if(!active){
20525 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
20526 		s+="\n";
20527 	}
20528 	if(!comments.isEmpty()){
20529 		s+=tr("Comments=%1").arg(comments);
20530 		s+="\n";
20531 	}
20532 
20533 	return s;
20534 }
20535 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)20536 double ConstraintTeachersActivityTagMinHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
20537 {
20538 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
20539 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
20540 		c.teachersMatrixReady=true;
20541 		c.subgroupsMatrixReady=true;
20542 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
20543 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
20544 
20545 		c.changedForMatrixCalculation=false;
20546 	}
20547 
20548 	int nbroken;
20549 
20550 	Matrix2D<int> crtTeacherTimetableActivityTag;
20551 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
20552 
20553 	nbroken=0;
20554 	for(int i : qAsConst(this->canonicalTeachersList)){
20555 		Teacher* tch=r.internalTeachersList[i];
20556 		for(int d=0; d<r.nDaysPerWeek; d++)
20557 			for(int h=0; h<r.nHoursPerDay; h++)
20558 				crtTeacherTimetableActivityTag[d][h]=-1;
20559 
20560 		for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
20561 			int d=c.times[ai]%r.nDaysPerWeek;
20562 			int h=c.times[ai]/r.nDaysPerWeek;
20563 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
20564 				assert(h+dur<r.nHoursPerDay);
20565 				assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
20566 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
20567 					crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
20568 			}
20569 		}
20570 
20571 		for(int d=0; d<r.nDaysPerWeek; d++){
20572 			int nd=0;
20573 			for(int h=0; h<r.nHoursPerDay; h++)
20574 				if(crtTeacherTimetableActivityTag[d][h]==this->activityTagIndex)
20575 					nd++;
20576 
20577 			if(nd==0 && this->allowEmptyDays)
20578 				continue;
20579 			if(nd<this->minHoursDaily){
20580 				nbroken++;
20581 
20582 				if(conflictsString!=nullptr){
20583 					QString s=(tr("Time constraint teachers activity tag %1 min %2 hours daily broken for teacher %3, on day %4, length=%5.")
20584 					 .arg(this->activityTagName)
20585 					 .arg(CustomFETString::number(this->minHoursDaily))
20586 					 .arg(r.internalTeachersList[i]->name)
20587 					 .arg(r.daysOfTheWeek[d])
20588 					 .arg(nd)
20589 					 )
20590 					 +
20591 					 " "
20592 					 +
20593 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
20594 
20595 					dl.append(s);
20596 					cl.append(weightPercentage/100.0);
20597 
20598 					*conflictsString+= s+"\n";
20599 				}
20600 			}
20601 		}
20602 	}
20603 
20604 	if(c.nPlacedActivities==r.nInternalActivities)
20605 		if(weightPercentage==100.0)
20606 			assert(nbroken==0);
20607 	return weightPercentage/100.0 * nbroken;
20608 }
20609 
isRelatedToActivity(Rules & r,Activity * a)20610 bool ConstraintTeachersActivityTagMinHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
20611 {
20612 	Q_UNUSED(r);
20613 	Q_UNUSED(a);
20614 
20615 	return false;
20616 }
20617 
isRelatedToTeacher(Teacher * t)20618 bool ConstraintTeachersActivityTagMinHoursDaily::isRelatedToTeacher(Teacher* t)
20619 {
20620 	Q_UNUSED(t);
20621 
20622 	return true;
20623 }
20624 
isRelatedToSubject(Subject * s)20625 bool ConstraintTeachersActivityTagMinHoursDaily::isRelatedToSubject(Subject* s)
20626 {
20627 	Q_UNUSED(s);
20628 
20629 	return false;
20630 }
20631 
isRelatedToActivityTag(ActivityTag * s)20632 bool ConstraintTeachersActivityTagMinHoursDaily::isRelatedToActivityTag(ActivityTag* s)
20633 {
20634 	return s->name==this->activityTagName;
20635 }
20636 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)20637 bool ConstraintTeachersActivityTagMinHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
20638 {
20639 	Q_UNUSED(r);
20640 	Q_UNUSED(s);
20641 
20642 	return false;
20643 }
20644 
hasWrongDayOrHour(Rules & r)20645 bool ConstraintTeachersActivityTagMinHoursDaily::hasWrongDayOrHour(Rules& r)
20646 {
20647 	if(minHoursDaily>r.nHoursPerDay)
20648 		return true;
20649 
20650 	return false;
20651 }
20652 
canRepairWrongDayOrHour(Rules & r)20653 bool ConstraintTeachersActivityTagMinHoursDaily::canRepairWrongDayOrHour(Rules& r)
20654 {
20655 	assert(hasWrongDayOrHour(r));
20656 
20657 	return true;
20658 }
20659 
repairWrongDayOrHour(Rules & r)20660 bool ConstraintTeachersActivityTagMinHoursDaily::repairWrongDayOrHour(Rules& r)
20661 {
20662 	assert(hasWrongDayOrHour(r));
20663 
20664 	if(minHoursDaily>r.nHoursPerDay)
20665 		minHoursDaily=r.nHoursPerDay;
20666 
20667 	return true;
20668 }
20669 
20670 ///////////////////////////////////////////////////////////////////////////////////////////
20671 ///////////////////////////////////////////////////////////////////////////////////////////
20672 
ConstraintTeacherActivityTagMinHoursDaily()20673 ConstraintTeacherActivityTagMinHoursDaily::ConstraintTeacherActivityTagMinHoursDaily()
20674 	: TimeConstraint()
20675 {
20676 	this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MIN_HOURS_DAILY;
20677 }
20678 
ConstraintTeacherActivityTagMinHoursDaily(double wp,int minhours,bool allowemptydays,const QString & teacher,const QString & activityTag)20679 ConstraintTeacherActivityTagMinHoursDaily::ConstraintTeacherActivityTagMinHoursDaily(double wp, int minhours, bool allowemptydays, const QString& teacher, const QString& activityTag)
20680  : TimeConstraint(wp)
20681  {
20682 	assert(minhours>0);
20683 	this->minHoursDaily=minhours;
20684 	this->allowEmptyDays=allowemptydays;
20685 	this->teacherName=teacher;
20686 	this->activityTagName=activityTag;
20687 
20688 	this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MIN_HOURS_DAILY;
20689 }
20690 
computeInternalStructure(QWidget * parent,Rules & r)20691 bool ConstraintTeacherActivityTagMinHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
20692 {
20693 	Q_UNUSED(parent);
20694 
20695 	//this->teacher_ID=r.searchTeacher(this->teacherName);
20696 	teacher_ID=r.teachersHash.value(teacherName, -1);
20697 	assert(this->teacher_ID>=0);
20698 
20699 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
20700 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
20701 	assert(this->activityTagIndex>=0);
20702 
20703 	this->canonicalTeachersList.clear();
20704 	int i=this->teacher_ID;
20705 	bool found=false;
20706 
20707 	Teacher* tch=r.internalTeachersList[i];
20708 	for(int actIndex : qAsConst(tch->activitiesForTeacher)){
20709 		if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
20710 			found=true;
20711 			break;
20712 		}
20713 	}
20714 
20715 	if(found)
20716 		this->canonicalTeachersList.append(i);
20717 
20718 	return true;
20719 }
20720 
hasInactiveActivities(Rules & r)20721 bool ConstraintTeacherActivityTagMinHoursDaily::hasInactiveActivities(Rules& r)
20722 {
20723 	Q_UNUSED(r);
20724 	return false;
20725 }
20726 
getXmlDescription(Rules & r)20727 QString ConstraintTeacherActivityTagMinHoursDaily::getXmlDescription(Rules& r)
20728 {
20729 	Q_UNUSED(r);
20730 
20731 	QString s="<ConstraintTeacherActivityTagMinHoursDaily>\n";
20732 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
20733 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
20734 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
20735 	s+="	<Minimum_Hours_Daily>"+CustomFETString::number(this->minHoursDaily)+"</Minimum_Hours_Daily>\n";
20736 	if(this->allowEmptyDays)
20737 		s+="	<Allow_Empty_Days>true</Allow_Empty_Days>\n";
20738 	else
20739 		s+="	<Allow_Empty_Days>false</Allow_Empty_Days>\n";
20740 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
20741 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
20742 	s+="</ConstraintTeacherActivityTagMinHoursDaily>\n";
20743 	return s;
20744 }
20745 
getDescription(Rules & r)20746 QString ConstraintTeacherActivityTagMinHoursDaily::getDescription(Rules& r)
20747 {
20748 	Q_UNUSED(r);
20749 
20750 	QString begin=QString("");
20751 	if(!active)
20752 		begin="X - ";
20753 
20754 	QString end=QString("");
20755 	if(!comments.isEmpty())
20756 		end=", "+tr("C: %1", "Comments").arg(comments);
20757 
20758 	QString s;
20759 	s+="! ";
20760 	s+=tr("Teacher %1 for activity tag %2 has min %3 hours daily").arg(this->teacherName).arg(this->activityTagName).arg(this->minHoursDaily);s+=", ";
20761 	s+=tr("AED:%1", "Allow empty days").arg(yesNoTranslated(this->allowEmptyDays));s+=", ";
20762 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
20763 
20764 	return begin+s+end;
20765 }
20766 
getDetailedDescription(Rules & r)20767 QString ConstraintTeacherActivityTagMinHoursDaily::getDetailedDescription(Rules& r)
20768 {
20769 	Q_UNUSED(r);
20770 
20771 	QString s=tr("Time constraint");s+="\n";
20772 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
20773 	s+=tr("A teacher for an activity tag must respect the minimum number of hours daily");s+="\n";
20774 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
20775 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
20776 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
20777 	s+=tr("Minimum hours daily=%1").arg(this->minHoursDaily); s+="\n";
20778 	s+=tr("Allow empty days=%1").arg(yesNoTranslated(this->allowEmptyDays));s+="\n";
20779 
20780 	if(!active){
20781 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
20782 		s+="\n";
20783 	}
20784 	if(!comments.isEmpty()){
20785 		s+=tr("Comments=%1").arg(comments);
20786 		s+="\n";
20787 	}
20788 
20789 	return s;
20790 }
20791 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)20792 double ConstraintTeacherActivityTagMinHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
20793 {
20794 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
20795 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
20796 		c.teachersMatrixReady=true;
20797 		c.subgroupsMatrixReady=true;
20798 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
20799 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
20800 
20801 		c.changedForMatrixCalculation=false;
20802 	}
20803 
20804 	int nbroken;
20805 
20806 	Matrix2D<int> crtTeacherTimetableActivityTag;
20807 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
20808 
20809 	nbroken=0;
20810 	for(int i : qAsConst(this->canonicalTeachersList)){
20811 		Teacher* tch=r.internalTeachersList[i];
20812 		for(int d=0; d<r.nDaysPerWeek; d++)
20813 			for(int h=0; h<r.nHoursPerDay; h++)
20814 				crtTeacherTimetableActivityTag[d][h]=-1;
20815 
20816 		for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
20817 			int d=c.times[ai]%r.nDaysPerWeek;
20818 			int h=c.times[ai]/r.nDaysPerWeek;
20819 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
20820 				assert(h+dur<r.nHoursPerDay);
20821 				assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
20822 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
20823 					crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
20824 			}
20825 		}
20826 
20827 		for(int d=0; d<r.nDaysPerWeek; d++){
20828 			int nd=0;
20829 			for(int h=0; h<r.nHoursPerDay; h++)
20830 				if(crtTeacherTimetableActivityTag[d][h]==this->activityTagIndex)
20831 					nd++;
20832 
20833 			if(nd==0 && this->allowEmptyDays)
20834 				continue;
20835 			if(nd<this->minHoursDaily){
20836 				nbroken++;
20837 
20838 				if(conflictsString!=nullptr){
20839 					QString s=(tr("Time constraint teacher activity tag %1 min %2 hours daily broken for teacher %3, on day %4, length=%5.")
20840 					 .arg(this->activityTagName)
20841 					 .arg(CustomFETString::number(this->minHoursDaily))
20842 					 .arg(r.internalTeachersList[i]->name)
20843 					 .arg(r.daysOfTheWeek[d])
20844 					 .arg(nd)
20845 					 )
20846 					 +
20847 					 " "
20848 					 +
20849 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
20850 
20851 					dl.append(s);
20852 					cl.append(weightPercentage/100.0);
20853 
20854 					*conflictsString+= s+"\n";
20855 				}
20856 			}
20857 		}
20858 	}
20859 
20860 	if(c.nPlacedActivities==r.nInternalActivities)
20861 		if(weightPercentage==100.0)
20862 			assert(nbroken==0);
20863 	return weightPercentage/100.0 * nbroken;
20864 }
20865 
isRelatedToActivity(Rules & r,Activity * a)20866 bool ConstraintTeacherActivityTagMinHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
20867 {
20868 	Q_UNUSED(r);
20869 	Q_UNUSED(a);
20870 
20871 	return false;
20872 }
20873 
isRelatedToTeacher(Teacher * t)20874 bool ConstraintTeacherActivityTagMinHoursDaily::isRelatedToTeacher(Teacher* t)
20875 {
20876 	if(this->teacherName==t->name)
20877 		return true;
20878 	return false;
20879 }
20880 
isRelatedToSubject(Subject * s)20881 bool ConstraintTeacherActivityTagMinHoursDaily::isRelatedToSubject(Subject* s)
20882 {
20883 	Q_UNUSED(s);
20884 
20885 	return false;
20886 }
20887 
isRelatedToActivityTag(ActivityTag * s)20888 bool ConstraintTeacherActivityTagMinHoursDaily::isRelatedToActivityTag(ActivityTag* s)
20889 {
20890 	return this->activityTagName==s->name;
20891 }
20892 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)20893 bool ConstraintTeacherActivityTagMinHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
20894 {
20895 	Q_UNUSED(r);
20896 	Q_UNUSED(s);
20897 
20898 	return false;
20899 }
20900 
hasWrongDayOrHour(Rules & r)20901 bool ConstraintTeacherActivityTagMinHoursDaily::hasWrongDayOrHour(Rules& r)
20902 {
20903 	if(minHoursDaily>r.nHoursPerDay)
20904 		return true;
20905 
20906 	return false;
20907 }
20908 
canRepairWrongDayOrHour(Rules & r)20909 bool ConstraintTeacherActivityTagMinHoursDaily::canRepairWrongDayOrHour(Rules& r)
20910 {
20911 	assert(hasWrongDayOrHour(r));
20912 
20913 	return true;
20914 }
20915 
repairWrongDayOrHour(Rules & r)20916 bool ConstraintTeacherActivityTagMinHoursDaily::repairWrongDayOrHour(Rules& r)
20917 {
20918 	assert(hasWrongDayOrHour(r));
20919 
20920 	if(minHoursDaily>r.nHoursPerDay)
20921 		minHoursDaily=r.nHoursPerDay;
20922 
20923 	return true;
20924 }
20925 
20926 ////////////////////////////////////////////////////////////////////////////////////////////
20927 ////////////////////////////////////////////////////////////////////////////////////////////
20928 
ConstraintStudentsActivityTagMinHoursDaily()20929 ConstraintStudentsActivityTagMinHoursDaily::ConstraintStudentsActivityTagMinHoursDaily()
20930 	: TimeConstraint()
20931 {
20932 	this->type = CONSTRAINT_STUDENTS_ACTIVITY_TAG_MIN_HOURS_DAILY;
20933 	this->minHoursDaily = -1;
20934 }
20935 
ConstraintStudentsActivityTagMinHoursDaily(double wp,int minnh,bool allowemptydays,const QString & activityTag)20936 ConstraintStudentsActivityTagMinHoursDaily::ConstraintStudentsActivityTagMinHoursDaily(double wp, int minnh, bool allowemptydays, const QString& activityTag)
20937 	: TimeConstraint(wp)
20938 {
20939 	this->minHoursDaily = minnh;
20940 	this->allowEmptyDays=allowemptydays;
20941 	this->activityTagName=activityTag;
20942 	this->type = CONSTRAINT_STUDENTS_ACTIVITY_TAG_MIN_HOURS_DAILY;
20943 }
20944 
computeInternalStructure(QWidget * parent,Rules & r)20945 bool ConstraintStudentsActivityTagMinHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
20946 {
20947 	Q_UNUSED(parent);
20948 
20949 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
20950 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
20951 	assert(this->activityTagIndex>=0);
20952 
20953 	this->canonicalSubgroupsList.clear();
20954 	for(int i=0; i<r.nInternalSubgroups; i++){
20955 		bool found=false;
20956 
20957 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
20958 		for(int actIndex : qAsConst(sbg->activitiesForSubgroup)){
20959 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
20960 				found=true;
20961 				break;
20962 			}
20963 		}
20964 
20965 		if(found)
20966 			this->canonicalSubgroupsList.append(i);
20967 	}
20968 
20969 	return true;
20970 }
20971 
hasInactiveActivities(Rules & r)20972 bool ConstraintStudentsActivityTagMinHoursDaily::hasInactiveActivities(Rules& r)
20973 {
20974 	Q_UNUSED(r);
20975 	return false;
20976 }
20977 
getXmlDescription(Rules & r)20978 QString ConstraintStudentsActivityTagMinHoursDaily::getXmlDescription(Rules& r)
20979 {
20980 	Q_UNUSED(r);
20981 
20982 	QString s="<ConstraintStudentsActivityTagMinHoursDaily>\n";
20983 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
20984 
20985 	s+="	<Activity_Tag>"+protect(this->activityTagName)+"</Activity_Tag>\n";
20986 
20987 	if(this->minHoursDaily>=0)
20988 		s+="	<Minimum_Hours_Daily>"+CustomFETString::number(this->minHoursDaily)+"</Minimum_Hours_Daily>\n";
20989 	else
20990 		assert(0);
20991 	if(this->allowEmptyDays)
20992 		s+="	<Allow_Empty_Days>true</Allow_Empty_Days>\n";
20993 	else
20994 		s+="	<Allow_Empty_Days>false</Allow_Empty_Days>\n";
20995 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
20996 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
20997 	s+="</ConstraintStudentsActivityTagMinHoursDaily>\n";
20998 	return s;
20999 }
21000 
getDescription(Rules & r)21001 QString ConstraintStudentsActivityTagMinHoursDaily::getDescription(Rules& r)
21002 {
21003 	Q_UNUSED(r);
21004 
21005 	QString begin=QString("");
21006 	if(!active)
21007 		begin="X - ";
21008 
21009 	QString end=QString("");
21010 	if(!comments.isEmpty())
21011 		end=", "+tr("C: %1", "Comments").arg(comments);
21012 
21013 	QString s;
21014 	s+="! ";
21015 	s+=tr("Students for activity tag %1 have min %2 hours daily")
21016 		.arg(this->activityTagName).arg(this->minHoursDaily); s+=", ";
21017 	s+=tr("AED:%1", "Allow empty days").arg(yesNoTranslated(this->allowEmptyDays));s+=", ";
21018 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
21019 
21020 	return begin+s+end;
21021 }
21022 
getDetailedDescription(Rules & r)21023 QString ConstraintStudentsActivityTagMinHoursDaily::getDetailedDescription(Rules& r)
21024 {
21025 	Q_UNUSED(r);
21026 
21027 	QString s=tr("Time constraint");s+="\n";
21028 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
21029 	s+=tr("All students, for an activity tag, must respect the minimum number of hours daily"); s+="\n";
21030 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
21031 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
21032 	s+=tr("Minimum hours daily=%1").arg(this->minHoursDaily);s+="\n";
21033 	s+=tr("Allow empty days=%1").arg(yesNoTranslated(this->allowEmptyDays));s+="\n";
21034 
21035 	if(!active){
21036 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
21037 		s+="\n";
21038 	}
21039 	if(!comments.isEmpty()){
21040 		s+=tr("Comments=%1").arg(comments);
21041 		s+="\n";
21042 	}
21043 
21044 	return s;
21045 }
21046 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)21047 double ConstraintStudentsActivityTagMinHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
21048 {
21049 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
21050 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
21051 		c.teachersMatrixReady=true;
21052 		c.subgroupsMatrixReady=true;
21053 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
21054 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
21055 
21056 		c.changedForMatrixCalculation=false;
21057 	}
21058 
21059 	int nbroken;
21060 
21061 	nbroken=0;
21062 
21063 	Matrix2D<int> crtSubgroupTimetableActivityTag;
21064 	crtSubgroupTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
21065 
21066 	for(int i : qAsConst(this->canonicalSubgroupsList)){
21067 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
21068 		for(int d=0; d<r.nDaysPerWeek; d++)
21069 			for(int h=0; h<r.nHoursPerDay; h++)
21070 				crtSubgroupTimetableActivityTag[d][h]=-1;
21071 		for(int ai : qAsConst(sbg->activitiesForSubgroup)) if(c.times[ai]!=UNALLOCATED_TIME){
21072 			int d=c.times[ai]%r.nDaysPerWeek;
21073 			int h=c.times[ai]/r.nDaysPerWeek;
21074 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
21075 				assert(h+dur<r.nHoursPerDay);
21076 				assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
21077 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
21078 					crtSubgroupTimetableActivityTag[d][h+dur]=this->activityTagIndex;
21079 			}
21080 		}
21081 
21082 		for(int d=0; d<r.nDaysPerWeek; d++){
21083 			int nd=0;
21084 			for(int h=0; h<r.nHoursPerDay; h++)
21085 				if(crtSubgroupTimetableActivityTag[d][h]==this->activityTagIndex)
21086 					nd++;
21087 
21088 			if(nd==0 && this->allowEmptyDays)
21089 				continue;
21090 			if(nd<this->minHoursDaily){
21091 				nbroken++;
21092 
21093 				if(conflictsString!=nullptr){
21094 					QString s=(tr(
21095 					 "Time constraint students, activity tag %1, min %2 hours daily, broken for subgroup %3, on day %4, length=%5.")
21096 					 .arg(this->activityTagName)
21097 					 .arg(CustomFETString::number(this->minHoursDaily))
21098 					 .arg(r.internalSubgroupsList[i]->name)
21099 					 .arg(r.daysOfTheWeek[d])
21100 					 .arg(nd)
21101 					 )
21102 					 +
21103 					 " "
21104 					 +
21105 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
21106 
21107 					dl.append(s);
21108 					cl.append(weightPercentage/100);
21109 
21110 					*conflictsString+= s+"\n";
21111 				}
21112 			}
21113 		}
21114 	}
21115 
21116 	if(c.nPlacedActivities==r.nInternalActivities)
21117 		if(weightPercentage==100.0)
21118 			assert(nbroken==0);
21119 	return weightPercentage/100.0 * nbroken;
21120 }
21121 
isRelatedToActivity(Rules & r,Activity * a)21122 bool ConstraintStudentsActivityTagMinHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
21123 {
21124 	Q_UNUSED(r);
21125 	Q_UNUSED(a);
21126 
21127 	return false;
21128 }
21129 
isRelatedToTeacher(Teacher * t)21130 bool ConstraintStudentsActivityTagMinHoursDaily::isRelatedToTeacher(Teacher* t)
21131 {
21132 	Q_UNUSED(t);
21133 
21134 	return false;
21135 }
21136 
isRelatedToSubject(Subject * s)21137 bool ConstraintStudentsActivityTagMinHoursDaily::isRelatedToSubject(Subject* s)
21138 {
21139 	Q_UNUSED(s);
21140 
21141 	return false;
21142 }
21143 
isRelatedToActivityTag(ActivityTag * s)21144 bool ConstraintStudentsActivityTagMinHoursDaily::isRelatedToActivityTag(ActivityTag* s)
21145 {
21146 	return s->name==this->activityTagName;
21147 }
21148 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)21149 bool ConstraintStudentsActivityTagMinHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
21150 {
21151 	Q_UNUSED(r);
21152 	Q_UNUSED(s);
21153 
21154 	return true;
21155 }
21156 
hasWrongDayOrHour(Rules & r)21157 bool ConstraintStudentsActivityTagMinHoursDaily::hasWrongDayOrHour(Rules& r)
21158 {
21159 	if(minHoursDaily>r.nHoursPerDay)
21160 		return true;
21161 
21162 	return false;
21163 }
21164 
canRepairWrongDayOrHour(Rules & r)21165 bool ConstraintStudentsActivityTagMinHoursDaily::canRepairWrongDayOrHour(Rules& r)
21166 {
21167 	assert(hasWrongDayOrHour(r));
21168 
21169 	return true;
21170 }
21171 
repairWrongDayOrHour(Rules & r)21172 bool ConstraintStudentsActivityTagMinHoursDaily::repairWrongDayOrHour(Rules& r)
21173 {
21174 	assert(hasWrongDayOrHour(r));
21175 
21176 	if(minHoursDaily>r.nHoursPerDay)
21177 		minHoursDaily=r.nHoursPerDay;
21178 
21179 	return true;
21180 }
21181 
21182 ////////////////////////////////////////////////////////////////////////////////////////////
21183 ////////////////////////////////////////////////////////////////////////////////////////////
21184 
ConstraintStudentsSetActivityTagMinHoursDaily()21185 ConstraintStudentsSetActivityTagMinHoursDaily::ConstraintStudentsSetActivityTagMinHoursDaily()
21186 	: TimeConstraint()
21187 {
21188 	this->type = CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MIN_HOURS_DAILY;
21189 	this->minHoursDaily = -1;
21190 }
21191 
ConstraintStudentsSetActivityTagMinHoursDaily(double wp,int minnh,bool allowemptydays,const QString & s,const QString & activityTag)21192 ConstraintStudentsSetActivityTagMinHoursDaily::ConstraintStudentsSetActivityTagMinHoursDaily(double wp, int minnh, bool allowemptydays, const QString& s, const QString& activityTag)
21193 	: TimeConstraint(wp)
21194 {
21195 	this->minHoursDaily = minnh;
21196 	this->allowEmptyDays=allowemptydays;
21197 	this->students = s;
21198 	this->activityTagName=activityTag;
21199 	this->type = CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MIN_HOURS_DAILY;
21200 }
21201 
hasInactiveActivities(Rules & r)21202 bool ConstraintStudentsSetActivityTagMinHoursDaily::hasInactiveActivities(Rules& r)
21203 {
21204 	Q_UNUSED(r);
21205 	return false;
21206 }
21207 
getXmlDescription(Rules & r)21208 QString ConstraintStudentsSetActivityTagMinHoursDaily::getXmlDescription(Rules& r)
21209 {
21210 	Q_UNUSED(r);
21211 
21212 	QString s="<ConstraintStudentsSetActivityTagMinHoursDaily>\n";
21213 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
21214 	s+="	<Minimum_Hours_Daily>"+CustomFETString::number(this->minHoursDaily)+"</Minimum_Hours_Daily>\n";
21215 	if(this->allowEmptyDays)
21216 		s+="	<Allow_Empty_Days>true</Allow_Empty_Days>\n";
21217 	else
21218 		s+="	<Allow_Empty_Days>false</Allow_Empty_Days>\n";
21219 	s+="	<Students>"+protect(this->students)+"</Students>\n";
21220 	s+="	<Activity_Tag>"+protect(this->activityTagName)+"</Activity_Tag>\n";
21221 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
21222 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
21223 	s+="</ConstraintStudentsSetActivityTagMinHoursDaily>\n";
21224 	return s;
21225 }
21226 
getDescription(Rules & r)21227 QString ConstraintStudentsSetActivityTagMinHoursDaily::getDescription(Rules& r)
21228 {
21229 	Q_UNUSED(r);
21230 
21231 	QString begin=QString("");
21232 	if(!active)
21233 		begin="X - ";
21234 
21235 	QString end=QString("");
21236 	if(!comments.isEmpty())
21237 		end=", "+tr("C: %1", "Comments").arg(comments);
21238 
21239 	QString s;
21240 	s+="! ";
21241 	s+=tr("Students set %1 for activity tag %2 has min %3 hours daily").arg(this->students).arg(this->activityTagName).arg(this->minHoursDaily);
21242 	s+=", ";
21243 	s+=tr("AED:%1", "Allow empty days").arg(yesNoTranslated(this->allowEmptyDays));
21244 	s+=", ";
21245 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
21246 
21247 	return begin+s+end;
21248 }
21249 
getDetailedDescription(Rules & r)21250 QString ConstraintStudentsSetActivityTagMinHoursDaily::getDetailedDescription(Rules& r)
21251 {
21252 	Q_UNUSED(r);
21253 
21254 	QString s=tr("Time constraint");s+="\n";
21255 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
21256 	s+=tr("A students set, for an activity tag, must respect the minimum number of hours daily"); s+="\n";
21257 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
21258 	s+=tr("Students set=%1").arg(this->students);s+="\n";
21259 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
21260 	s+=tr("Minimum hours daily=%1").arg(this->minHoursDaily);s+="\n";
21261 	s+=tr("Allow empty days=%1").arg(yesNoTranslated(this->allowEmptyDays));s+="\n";
21262 
21263 	if(!active){
21264 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
21265 		s+="\n";
21266 	}
21267 	if(!comments.isEmpty()){
21268 		s+=tr("Comments=%1").arg(comments);
21269 		s+="\n";
21270 	}
21271 
21272 	return s;
21273 }
21274 
computeInternalStructure(QWidget * parent,Rules & r)21275 bool ConstraintStudentsSetActivityTagMinHoursDaily::computeInternalStructure(QWidget* parent, Rules& r)
21276 {
21277 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
21278 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
21279 	assert(this->activityTagIndex>=0);
21280 
21281 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
21282 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
21283 
21284 	if(ss==nullptr){
21285 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
21286 		 tr("Constraint students set min hours daily is wrong because it refers to inexistent students set."
21287 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
21288 
21289 		return false;
21290 	}
21291 
21292 	assert(ss!=nullptr);
21293 
21294 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
21295 	/*this->iSubgroupsList.clear();
21296 	if(ss->type==STUDENTS_SUBGROUP){
21297 		int tmp;
21298 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
21299 		assert(tmp>=0);
21300 		assert(tmp<r.nInternalSubgroups);
21301 		if(!this->iSubgroupsList.contains(tmp))
21302 			this->iSubgroupsList.append(tmp);
21303 	}
21304 	else if(ss->type==STUDENTS_GROUP){
21305 		StudentsGroup* stg=(StudentsGroup*)ss;
21306 		for(int i=0; i<stg->subgroupsList.size(); i++){
21307 			StudentsSubgroup* sts=stg->subgroupsList[i];
21308 			int tmp;
21309 			tmp=sts->indexInInternalSubgroupsList;
21310 			assert(tmp>=0);
21311 			assert(tmp<r.nInternalSubgroups);
21312 			if(!this->iSubgroupsList.contains(tmp))
21313 				this->iSubgroupsList.append(tmp);
21314 		}
21315 	}
21316 	else if(ss->type==STUDENTS_YEAR){
21317 		StudentsYear* sty=(StudentsYear*)ss;
21318 		for(int i=0; i<sty->groupsList.size(); i++){
21319 			StudentsGroup* stg=sty->groupsList[i];
21320 			for(int j=0; j<stg->subgroupsList.size(); j++){
21321 				StudentsSubgroup* sts=stg->subgroupsList[j];
21322 				int tmp;
21323 				tmp=sts->indexInInternalSubgroupsList;
21324 				assert(tmp>=0);
21325 				assert(tmp<r.nInternalSubgroups);
21326 				if(!this->iSubgroupsList.contains(tmp))
21327 					this->iSubgroupsList.append(tmp);
21328 			}
21329 		}
21330 	}
21331 	else
21332 		assert(0);*/
21333 
21334 	/////////////
21335 	this->canonicalSubgroupsList.clear();
21336 	for(int i : qAsConst(this->iSubgroupsList)){
21337 		bool found=false;
21338 
21339 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
21340 		for(int actIndex : qAsConst(sbg->activitiesForSubgroup)){
21341 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
21342 				found=true;
21343 				break;
21344 			}
21345 		}
21346 
21347 		if(found)
21348 			this->canonicalSubgroupsList.append(i);
21349 	}
21350 
21351 	return true;
21352 }
21353 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)21354 double ConstraintStudentsSetActivityTagMinHoursDaily::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
21355 {
21356 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
21357 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
21358 		c.teachersMatrixReady=true;
21359 		c.subgroupsMatrixReady=true;
21360 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
21361 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
21362 
21363 		c.changedForMatrixCalculation=false;
21364 	}
21365 
21366 	int nbroken;
21367 
21368 	Matrix2D<int> crtSubgroupTimetableActivityTag;
21369 	crtSubgroupTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
21370 
21371 	nbroken=0;
21372 
21373 	for(int i : qAsConst(this->canonicalSubgroupsList)){
21374 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
21375 		for(int d=0; d<r.nDaysPerWeek; d++)
21376 			for(int h=0; h<r.nHoursPerDay; h++)
21377 				crtSubgroupTimetableActivityTag[d][h]=-1;
21378 		for(int ai : qAsConst(sbg->activitiesForSubgroup)) if(c.times[ai]!=UNALLOCATED_TIME){
21379 			int d=c.times[ai]%r.nDaysPerWeek;
21380 			int h=c.times[ai]/r.nDaysPerWeek;
21381 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
21382 				assert(h+dur<r.nHoursPerDay);
21383 				assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
21384 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
21385 					crtSubgroupTimetableActivityTag[d][h+dur]=this->activityTagIndex;
21386 			}
21387 		}
21388 
21389 		for(int d=0; d<r.nDaysPerWeek; d++){
21390 			int nd=0;
21391 			for(int h=0; h<r.nHoursPerDay; h++)
21392 				if(crtSubgroupTimetableActivityTag[d][h]==this->activityTagIndex)
21393 					nd++;
21394 
21395 			if(nd==0 && this->allowEmptyDays)
21396 				continue;
21397 			if(nd<this->minHoursDaily){
21398 				nbroken++;
21399 
21400 				if(conflictsString!=nullptr){
21401 					QString s=(tr(
21402 					 "Time constraint students set, activity tag %1, min %2 hours daily, broken for subgroup %3, on day %4, length=%5.")
21403 					 .arg(this->activityTagName)
21404 					 .arg(CustomFETString::number(this->minHoursDaily))
21405 					 .arg(r.internalSubgroupsList[i]->name)
21406 					 .arg(r.daysOfTheWeek[d])
21407 					 .arg(nd)
21408 					 )
21409 					 +
21410 					 " "
21411 					 +
21412 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
21413 
21414 					dl.append(s);
21415 					cl.append(weightPercentage/100);
21416 
21417 					*conflictsString+= s+"\n";
21418 				}
21419 			}
21420 		}
21421 	}
21422 
21423 	if(c.nPlacedActivities==r.nInternalActivities)
21424 		if(weightPercentage==100.0)
21425 			assert(nbroken==0);
21426 	return weightPercentage/100.0 * nbroken;
21427 }
21428 
isRelatedToActivity(Rules & r,Activity * a)21429 bool ConstraintStudentsSetActivityTagMinHoursDaily::isRelatedToActivity(Rules& r, Activity* a)
21430 {
21431 	Q_UNUSED(r);
21432 	Q_UNUSED(a);
21433 
21434 	return false;
21435 }
21436 
isRelatedToTeacher(Teacher * t)21437 bool ConstraintStudentsSetActivityTagMinHoursDaily::isRelatedToTeacher(Teacher* t)
21438 {
21439 	Q_UNUSED(t);
21440 
21441 	return false;
21442 }
21443 
isRelatedToSubject(Subject * s)21444 bool ConstraintStudentsSetActivityTagMinHoursDaily::isRelatedToSubject(Subject* s)
21445 {
21446 	Q_UNUSED(s);
21447 
21448 	return false;
21449 }
21450 
isRelatedToActivityTag(ActivityTag * s)21451 bool ConstraintStudentsSetActivityTagMinHoursDaily::isRelatedToActivityTag(ActivityTag* s)
21452 {
21453 	Q_UNUSED(s);
21454 
21455 	return false;
21456 }
21457 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)21458 bool ConstraintStudentsSetActivityTagMinHoursDaily::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
21459 {
21460 	return r.setsShareStudents(this->students, s->name);
21461 }
21462 
hasWrongDayOrHour(Rules & r)21463 bool ConstraintStudentsSetActivityTagMinHoursDaily::hasWrongDayOrHour(Rules& r)
21464 {
21465 	if(minHoursDaily>r.nHoursPerDay)
21466 		return true;
21467 
21468 	return false;
21469 }
21470 
canRepairWrongDayOrHour(Rules & r)21471 bool ConstraintStudentsSetActivityTagMinHoursDaily::canRepairWrongDayOrHour(Rules& r)
21472 {
21473 	assert(hasWrongDayOrHour(r));
21474 
21475 	return true;
21476 }
21477 
repairWrongDayOrHour(Rules & r)21478 bool ConstraintStudentsSetActivityTagMinHoursDaily::repairWrongDayOrHour(Rules& r)
21479 {
21480 	assert(hasWrongDayOrHour(r));
21481 
21482 	if(minHoursDaily>r.nHoursPerDay)
21483 		minHoursDaily=r.nHoursPerDay;
21484 
21485 	return true;
21486 }
21487 
21488 ////////////////////////////////////////////////////////////////////////////////////////////
21489 ////////////////////////////////////////////////////////////////////////////////////////////
21490 
ConstraintStudentsMaxGapsPerDay()21491 ConstraintStudentsMaxGapsPerDay::ConstraintStudentsMaxGapsPerDay()
21492 	: TimeConstraint()
21493 {
21494 	this->type = CONSTRAINT_STUDENTS_MAX_GAPS_PER_DAY;
21495 }
21496 
ConstraintStudentsMaxGapsPerDay(double wp,int mg)21497 ConstraintStudentsMaxGapsPerDay::ConstraintStudentsMaxGapsPerDay(double wp, int mg)
21498 	: TimeConstraint(wp)
21499 {
21500 	this->type = CONSTRAINT_STUDENTS_MAX_GAPS_PER_DAY;
21501 	this->maxGaps=mg;
21502 }
21503 
computeInternalStructure(QWidget * parent,Rules & r)21504 bool ConstraintStudentsMaxGapsPerDay::computeInternalStructure(QWidget* parent, Rules& r)
21505 {
21506 	Q_UNUSED(parent);
21507 	Q_UNUSED(r);
21508 
21509 	return true;
21510 }
21511 
hasInactiveActivities(Rules & r)21512 bool ConstraintStudentsMaxGapsPerDay::hasInactiveActivities(Rules& r)
21513 {
21514 	Q_UNUSED(r);
21515 	return false;
21516 }
21517 
getXmlDescription(Rules & r)21518 QString ConstraintStudentsMaxGapsPerDay::getXmlDescription(Rules& r)
21519 {
21520 	Q_UNUSED(r);
21521 
21522 	QString s="<ConstraintStudentsMaxGapsPerDay>\n";
21523 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
21524 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
21525 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
21526 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
21527 	s+="</ConstraintStudentsMaxGapsPerDay>\n";
21528 	return s;
21529 }
21530 
getDescription(Rules & r)21531 QString ConstraintStudentsMaxGapsPerDay::getDescription(Rules& r)
21532 {
21533 	Q_UNUSED(r);
21534 
21535 	QString begin=QString("");
21536 	if(!active)
21537 		begin="X - ";
21538 
21539 	QString end=QString("");
21540 	if(!comments.isEmpty())
21541 		end=", "+tr("C: %1", "Comments").arg(comments);
21542 
21543 	QString s;
21544 	s+="! ";
21545 	s+=tr("Students max gaps per day");s+=", ";
21546 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
21547 	s+=tr("MG:%1", "Max gaps (per day)").arg(this->maxGaps);
21548 
21549 	return begin+s+end;
21550 }
21551 
getDetailedDescription(Rules & r)21552 QString ConstraintStudentsMaxGapsPerDay::getDetailedDescription(Rules& r)
21553 {
21554 	Q_UNUSED(r);
21555 
21556 	QString s=tr("Time constraint");s+="\n";
21557 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
21558 	s+=tr("All students must respect the maximum number of gaps per day");s+="\n";
21559 	s+=tr("(breaks and students set not available not counted)");s+="\n";
21560 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
21561 	s+=tr("Maximum gaps per day=%1").arg(this->maxGaps);s+="\n";
21562 
21563 	if(!active){
21564 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
21565 		s+="\n";
21566 	}
21567 	if(!comments.isEmpty()){
21568 		s+=tr("Comments=%1").arg(comments);
21569 		s+="\n";
21570 	}
21571 
21572 	return s;
21573 }
21574 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)21575 double ConstraintStudentsMaxGapsPerDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
21576 {
21577 	//returns a number equal to the number of gaps of the subgroups (in hours)
21578 
21579 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
21580 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
21581 		c.teachersMatrixReady=true;
21582 		c.subgroupsMatrixReady=true;
21583 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
21584 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
21585 
21586 		c.changedForMatrixCalculation=false;
21587 	}
21588 
21589 	int nGaps;
21590 	int tmp;
21591 	int i;
21592 
21593 	int tIllegalGaps=0;
21594 
21595 	for(i=0; i<r.nInternalSubgroups; i++){
21596 		for(int j=0; j<r.nDaysPerWeek; j++){
21597 			nGaps=0;
21598 
21599 			int k;
21600 			tmp=0;
21601 			for(k=0; k<r.nHoursPerDay; k++)
21602 				if(subgroupsMatrix[i][j][k]>0){
21603 					assert(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]);
21604 					break;
21605 				}
21606 			for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
21607 				if(subgroupsMatrix[i][j][k]>0){
21608 					nGaps+=tmp;
21609 					tmp=0;
21610 				}
21611 				else
21612 					tmp++;
21613 			}
21614 
21615 			int illegalGaps=nGaps-this->maxGaps;
21616 			if(illegalGaps<0)
21617 				illegalGaps=0;
21618 
21619 			if(illegalGaps>0 && conflictsString!=nullptr){
21620 				QString s=tr("Time constraint students max gaps per day broken for subgroup: %1, it has %2 extra gaps, on day %3, conflicts increase=%4")
21621 				 .arg(r.internalSubgroupsList[i]->name)
21622 				 .arg(illegalGaps)
21623 				 .arg(r.daysOfTheWeek[j])
21624 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision(illegalGaps*weightPercentage/100));
21625 
21626 				dl.append(s);
21627 				cl.append(illegalGaps*weightPercentage/100);
21628 
21629 				*conflictsString+= s+"\n";
21630 			}
21631 
21632 			tIllegalGaps+=illegalGaps;
21633 		}
21634 	}
21635 
21636 	if(c.nPlacedActivities==r.nInternalActivities)
21637 		if(weightPercentage==100)    //for partial solutions it might be broken
21638 			assert(tIllegalGaps==0);
21639 	return weightPercentage/100 * tIllegalGaps;
21640 }
21641 
isRelatedToActivity(Rules & r,Activity * a)21642 bool ConstraintStudentsMaxGapsPerDay::isRelatedToActivity(Rules& r, Activity* a)
21643 {
21644 	Q_UNUSED(r);
21645 	Q_UNUSED(a);
21646 
21647 	return false;
21648 }
21649 
isRelatedToTeacher(Teacher * t)21650 bool ConstraintStudentsMaxGapsPerDay::isRelatedToTeacher(Teacher* t)
21651 {
21652 	Q_UNUSED(t);
21653 
21654 	return false;
21655 }
21656 
isRelatedToSubject(Subject * s)21657 bool ConstraintStudentsMaxGapsPerDay::isRelatedToSubject(Subject* s)
21658 {
21659 	Q_UNUSED(s);
21660 
21661 	return false;
21662 }
21663 
isRelatedToActivityTag(ActivityTag * s)21664 bool ConstraintStudentsMaxGapsPerDay::isRelatedToActivityTag(ActivityTag* s)
21665 {
21666 	Q_UNUSED(s);
21667 
21668 	return false;
21669 }
21670 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)21671 bool ConstraintStudentsMaxGapsPerDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
21672 {
21673 	Q_UNUSED(r);
21674 	Q_UNUSED(s);
21675 
21676 	return true;
21677 }
21678 
hasWrongDayOrHour(Rules & r)21679 bool ConstraintStudentsMaxGapsPerDay::hasWrongDayOrHour(Rules& r)
21680 {
21681 	if(maxGaps>r.nHoursPerDay)
21682 		return true;
21683 
21684 	return false;
21685 }
21686 
canRepairWrongDayOrHour(Rules & r)21687 bool ConstraintStudentsMaxGapsPerDay::canRepairWrongDayOrHour(Rules& r)
21688 {
21689 	assert(hasWrongDayOrHour(r));
21690 
21691 	return true;
21692 }
21693 
repairWrongDayOrHour(Rules & r)21694 bool ConstraintStudentsMaxGapsPerDay::repairWrongDayOrHour(Rules& r)
21695 {
21696 	assert(hasWrongDayOrHour(r));
21697 
21698 	if(maxGaps>r.nHoursPerDay)
21699 		maxGaps=r.nHoursPerDay;
21700 
21701 	return true;
21702 }
21703 
21704 ////////////////////////////////////////////////////////////////////////////////////////////
21705 ////////////////////////////////////////////////////////////////////////////////////////////
21706 
ConstraintStudentsSetMaxGapsPerDay()21707 ConstraintStudentsSetMaxGapsPerDay::ConstraintStudentsSetMaxGapsPerDay()
21708 	: TimeConstraint()
21709 {
21710 	this->type = CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_DAY;
21711 }
21712 
ConstraintStudentsSetMaxGapsPerDay(double wp,int mg,const QString & st)21713 ConstraintStudentsSetMaxGapsPerDay::ConstraintStudentsSetMaxGapsPerDay(double wp, int mg, const QString& st )
21714 	: TimeConstraint(wp)
21715 {
21716 	this->type = CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_DAY;
21717 	this->maxGaps=mg;
21718 	this->students = st;
21719 }
21720 
computeInternalStructure(QWidget * parent,Rules & r)21721 bool ConstraintStudentsSetMaxGapsPerDay::computeInternalStructure(QWidget* parent, Rules& r){
21722 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
21723 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
21724 
21725 	if(ss==nullptr){
21726 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
21727 		 tr("Constraint students set max gaps per day is wrong because it refers to inexistent students set."
21728 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
21729 
21730 		return false;
21731 	}
21732 
21733 	assert(ss!=nullptr);
21734 
21735 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
21736 	/*this->iSubgroupsList.clear();
21737 	if(ss->type==STUDENTS_SUBGROUP){
21738 		int tmp;
21739 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
21740 		assert(tmp>=0);
21741 		assert(tmp<r.nInternalSubgroups);
21742 		if(!this->iSubgroupsList.contains(tmp))
21743 			this->iSubgroupsList.append(tmp);
21744 	}
21745 	else if(ss->type==STUDENTS_GROUP){
21746 		StudentsGroup* stg=(StudentsGroup*)ss;
21747 		for(int i=0; i<stg->subgroupsList.size(); i++){
21748 			StudentsSubgroup* sts=stg->subgroupsList[i];
21749 			int tmp;
21750 			tmp=sts->indexInInternalSubgroupsList;
21751 			assert(tmp>=0);
21752 			assert(tmp<r.nInternalSubgroups);
21753 			if(!this->iSubgroupsList.contains(tmp))
21754 				this->iSubgroupsList.append(tmp);
21755 		}
21756 	}
21757 	else if(ss->type==STUDENTS_YEAR){
21758 		StudentsYear* sty=(StudentsYear*)ss;
21759 		for(int i=0; i<sty->groupsList.size(); i++){
21760 			StudentsGroup* stg=sty->groupsList[i];
21761 			for(int j=0; j<stg->subgroupsList.size(); j++){
21762 				StudentsSubgroup* sts=stg->subgroupsList[j];
21763 				int tmp;
21764 				tmp=sts->indexInInternalSubgroupsList;
21765 				assert(tmp>=0);
21766 				assert(tmp<r.nInternalSubgroups);
21767 				if(!this->iSubgroupsList.contains(tmp))
21768 					this->iSubgroupsList.append(tmp);
21769 			}
21770 		}
21771 	}
21772 	else
21773 		assert(0);*/
21774 
21775 	return true;
21776 }
21777 
hasInactiveActivities(Rules & r)21778 bool ConstraintStudentsSetMaxGapsPerDay::hasInactiveActivities(Rules& r)
21779 {
21780 	Q_UNUSED(r);
21781 	return false;
21782 }
21783 
getXmlDescription(Rules & r)21784 QString ConstraintStudentsSetMaxGapsPerDay::getXmlDescription(Rules& r)
21785 {
21786 	Q_UNUSED(r);
21787 
21788 	QString s="<ConstraintStudentsSetMaxGapsPerDay>\n";
21789 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
21790 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
21791 	s+="	<Students>"; s+=protect(this->students); s+="</Students>\n";
21792 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
21793 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
21794 	s+="</ConstraintStudentsSetMaxGapsPerDay>\n";
21795 	return s;
21796 }
21797 
getDescription(Rules & r)21798 QString ConstraintStudentsSetMaxGapsPerDay::getDescription(Rules& r)
21799 {
21800 	Q_UNUSED(r);
21801 
21802 	QString begin=QString("");
21803 	if(!active)
21804 		begin="X - ";
21805 
21806 	QString end=QString("");
21807 	if(!comments.isEmpty())
21808 		end=", "+tr("C: %1", "Comments").arg(comments);
21809 
21810 	QString s;
21811 	s+="! ";
21812 	s+=tr("Students set max gaps per day"); s+=", ";
21813 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage)); s+=", ";
21814 	s+=tr("MG:%1", "Max gaps (per day)").arg(this->maxGaps);s+=", ";
21815 	s+=tr("St:%1", "Students").arg(this->students);
21816 
21817 	return begin+s+end;
21818 }
21819 
getDetailedDescription(Rules & r)21820 QString ConstraintStudentsSetMaxGapsPerDay::getDetailedDescription(Rules& r)
21821 {
21822 	Q_UNUSED(r);
21823 
21824 	QString s=tr("Time constraint");s+="\n";
21825 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
21826 	s+=tr("A students set must respect the maximum number of gaps per day");s+="\n";
21827 	s+=tr("(breaks and students set not available not counted)");s+="\n";
21828 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
21829 	s+=tr("Maximum gaps per day=%1").arg(this->maxGaps);s+="\n";
21830 	s+=tr("Students=%1").arg(this->students); s+="\n";
21831 
21832 	if(!active){
21833 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
21834 		s+="\n";
21835 	}
21836 	if(!comments.isEmpty()){
21837 		s+=tr("Comments=%1").arg(comments);
21838 		s+="\n";
21839 	}
21840 
21841 	return s;
21842 }
21843 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)21844 double ConstraintStudentsSetMaxGapsPerDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
21845 {
21846 	//OLD COMMENT
21847 	//returns a number equal to the number of gaps of the subgroups (in hours)
21848 
21849 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
21850 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
21851 		c.teachersMatrixReady=true;
21852 		c.subgroupsMatrixReady=true;
21853 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
21854 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
21855 
21856 		c.changedForMatrixCalculation=false;
21857 	}
21858 
21859 	int nGaps;
21860 	int tmp;
21861 
21862 	int tIllegalGaps=0;
21863 
21864 	for(int sg=0; sg<this->iSubgroupsList.count(); sg++){
21865 		int i=this->iSubgroupsList.at(sg);
21866 		for(int j=0; j<r.nDaysPerWeek; j++){
21867 			nGaps=0;
21868 
21869 			int k;
21870 			tmp=0;
21871 			for(k=0; k<r.nHoursPerDay; k++)
21872 				if(subgroupsMatrix[i][j][k]>0){
21873 					assert(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]);
21874 					break;
21875 				}
21876 			for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
21877 				if(subgroupsMatrix[i][j][k]>0){
21878 					nGaps+=tmp;
21879 					tmp=0;
21880 				}
21881 				else
21882 					tmp++;
21883 			}
21884 
21885 			int illegalGaps=nGaps-this->maxGaps;
21886 			if(illegalGaps<0)
21887 				illegalGaps=0;
21888 
21889 			if(illegalGaps>0 && conflictsString!=nullptr){
21890 				QString s=tr("Time constraint students set max gaps per day broken for subgroup: %1, extra gaps=%2, on day %3, conflicts increase=%4")
21891 				 .arg(r.internalSubgroupsList[i]->name)
21892 				 .arg(illegalGaps)
21893 				 .arg(r.daysOfTheWeek[j])
21894 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*illegalGaps));
21895 
21896 				dl.append(s);
21897 				cl.append(weightPercentage/100*illegalGaps);
21898 
21899 				*conflictsString+= s+"\n";
21900 			}
21901 
21902 			tIllegalGaps+=illegalGaps;
21903 		}
21904 	}
21905 
21906 	if(c.nPlacedActivities==r.nInternalActivities)
21907 		if(weightPercentage==100)     //for partial solutions it might be broken
21908 			assert(tIllegalGaps==0);
21909 	return weightPercentage/100 * tIllegalGaps;
21910 }
21911 
isRelatedToActivity(Rules & r,Activity * a)21912 bool ConstraintStudentsSetMaxGapsPerDay::isRelatedToActivity(Rules& r, Activity* a)
21913 {
21914 	Q_UNUSED(r);
21915 	Q_UNUSED(a);
21916 
21917 	return false;
21918 }
21919 
isRelatedToTeacher(Teacher * t)21920 bool ConstraintStudentsSetMaxGapsPerDay::isRelatedToTeacher(Teacher* t)
21921 {
21922 	Q_UNUSED(t);
21923 
21924 	return false;
21925 }
21926 
isRelatedToSubject(Subject * s)21927 bool ConstraintStudentsSetMaxGapsPerDay::isRelatedToSubject(Subject* s)
21928 {
21929 	Q_UNUSED(s);
21930 
21931 	return false;
21932 }
21933 
isRelatedToActivityTag(ActivityTag * s)21934 bool ConstraintStudentsSetMaxGapsPerDay::isRelatedToActivityTag(ActivityTag* s)
21935 {
21936 	Q_UNUSED(s);
21937 
21938 	return false;
21939 }
21940 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)21941 bool ConstraintStudentsSetMaxGapsPerDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
21942 {
21943 	return r.setsShareStudents(this->students, s->name);
21944 }
21945 
hasWrongDayOrHour(Rules & r)21946 bool ConstraintStudentsSetMaxGapsPerDay::hasWrongDayOrHour(Rules& r)
21947 {
21948 	if(maxGaps>r.nHoursPerDay)
21949 		return true;
21950 
21951 	return false;
21952 }
21953 
canRepairWrongDayOrHour(Rules & r)21954 bool ConstraintStudentsSetMaxGapsPerDay::canRepairWrongDayOrHour(Rules& r)
21955 {
21956 	assert(hasWrongDayOrHour(r));
21957 
21958 	return true;
21959 }
21960 
repairWrongDayOrHour(Rules & r)21961 bool ConstraintStudentsSetMaxGapsPerDay::repairWrongDayOrHour(Rules& r)
21962 {
21963 	assert(hasWrongDayOrHour(r));
21964 
21965 	if(maxGaps>r.nHoursPerDay)
21966 		maxGaps=r.nHoursPerDay;
21967 
21968 	return true;
21969 }
21970 
21971 ////////////////////////////////////////////////////////////////////////////////////////////
21972 ////////////////////////////////////////////////////////////////////////////////////////////
21973 
ConstraintActivitiesOccupyMaxTimeSlotsFromSelection()21974 ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::ConstraintActivitiesOccupyMaxTimeSlotsFromSelection()
21975 	: TimeConstraint()
21976 {
21977 	this->type = CONSTRAINT_ACTIVITIES_OCCUPY_MAX_TIME_SLOTS_FROM_SELECTION;
21978 }
21979 
ConstraintActivitiesOccupyMaxTimeSlotsFromSelection(double wp,QList<int> a_L,QList<int> d_L,QList<int> h_L,int max_slots)21980 ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::ConstraintActivitiesOccupyMaxTimeSlotsFromSelection(double wp,
21981 	QList<int> a_L, QList<int> d_L, QList<int> h_L, int max_slots)
21982 	: TimeConstraint(wp)
21983 {
21984 	assert(d_L.count()==h_L.count());
21985 
21986 	this->activitiesIds=a_L;
21987 	this->selectedDays=d_L;
21988 	this->selectedHours=h_L;
21989 	this->maxOccupiedTimeSlots=max_slots;
21990 
21991 	this->type=CONSTRAINT_ACTIVITIES_OCCUPY_MAX_TIME_SLOTS_FROM_SELECTION;
21992 }
21993 
computeInternalStructure(QWidget * parent,Rules & r)21994 bool ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::computeInternalStructure(QWidget* parent, Rules& r)
21995 {
21996 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
21997 	_activitiesIndices.clear();
21998 	for(int id : qAsConst(activitiesIds)){
21999 		int i=r.activitiesHash.value(id, -1);
22000 		if(i>=0)
22001 			_activitiesIndices.append(i);
22002 	}
22003 
22004 	/*this->_activitiesIndices.clear();
22005 
22006 	QSet<int> req=this->activitiesIds.toSet();
22007 	assert(req.count()==this->activitiesIds.count());
22008 
22009 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
22010 	int i;
22011 	for(i=0; i<r.nInternalActivities; i++)
22012 		if(req.contains(r.internalActivitiesList[i].id))
22013 			this->_activitiesIndices.append(i);*/
22014 
22015 	//////////////////////
22016 	assert(this->selectedDays.count()==this->selectedHours.count());
22017 
22018 	for(int k=0; k<this->selectedDays.count(); k++){
22019 		if(this->selectedDays.at(k) >= r.nDaysPerWeek){
22020 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22021 			 tr("Constraint activities occupy max time slots from selection is wrong because it refers to removed day. Please correct"
22022 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22023 
22024 			return false;
22025 		}
22026 		if(this->selectedHours.at(k) == r.nHoursPerDay){
22027 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22028 			 tr("Constraint activities occupy max time slots from selection is wrong because a preferred hour is too late (after the last acceptable slot). Please correct"
22029 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22030 
22031 			return false;
22032 		}
22033 		if(this->selectedHours.at(k) > r.nHoursPerDay){
22034 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22035 			 tr("Constraint activities occupy max time slots from selection is wrong because it refers to removed hour. Please correct"
22036 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22037 
22038 			return false;
22039 		}
22040 		if(this->selectedDays.at(k)<0 || this->selectedHours.at(k)<0){
22041 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22042 			 tr("Constraint activities occupy max time slots from selection is wrong because hour or day is not specified for a slot (-1). Please correct"
22043 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22044 
22045 			return false;
22046 		}
22047 	}
22048 	///////////////////////
22049 
22050 	if(this->_activitiesIndices.count()>0)
22051 		return true;
22052 	else{
22053 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
22054 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
22055 		return false;
22056 	}
22057 }
22058 
hasInactiveActivities(Rules & r)22059 bool ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::hasInactiveActivities(Rules& r)
22060 {
22061 	//returns true if all activities are inactive
22062 
22063 	for(int aid : qAsConst(this->activitiesIds))
22064 		if(!r.inactiveActivities.contains(aid))
22065 			return false;
22066 
22067 	return true;
22068 }
22069 
getXmlDescription(Rules & r)22070 QString ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::getXmlDescription(Rules& r)
22071 {
22072 	assert(this->selectedDays.count()==this->selectedHours.count());
22073 
22074 	QString s="<ConstraintActivitiesOccupyMaxTimeSlotsFromSelection>\n";
22075 
22076 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
22077 
22078 	s+="	<Number_of_Activities>"+QString::number(this->activitiesIds.count())+"</Number_of_Activities>\n";
22079 	for(int aid : qAsConst(this->activitiesIds))
22080 		s+="	<Activity_Id>"+CustomFETString::number(aid)+"</Activity_Id>\n";
22081 
22082 	s+="	<Number_of_Selected_Time_Slots>"+QString::number(this->selectedDays.count())+"</Number_of_Selected_Time_Slots>\n";
22083 	for(int i=0; i<this->selectedDays.count(); i++){
22084 		s+="	<Selected_Time_Slot>\n";
22085 		s+="		<Selected_Day>"+protect(r.daysOfTheWeek[this->selectedDays.at(i)])+"</Selected_Day>\n";
22086 		s+="		<Selected_Hour>"+protect(r.hoursOfTheDay[this->selectedHours.at(i)])+"</Selected_Hour>\n";
22087 		s+="	</Selected_Time_Slot>\n";
22088 	}
22089 	s+="	<Max_Number_of_Occupied_Time_Slots>"+CustomFETString::number(this->maxOccupiedTimeSlots)+"</Max_Number_of_Occupied_Time_Slots>\n";
22090 
22091 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
22092 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
22093 	s+="</ConstraintActivitiesOccupyMaxTimeSlotsFromSelection>\n";
22094 	return s;
22095 }
22096 
getDescription(Rules & r)22097 QString ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::getDescription(Rules& r)
22098 {
22099 	QString begin=QString("");
22100 	if(!active)
22101 		begin="X - ";
22102 
22103 	QString end=QString("");
22104 	if(!comments.isEmpty())
22105 		end=", "+tr("C: %1", "Comments").arg(comments);
22106 
22107 	assert(this->selectedDays.count()==this->selectedHours.count());
22108 
22109 	QString actids=QString("");
22110 	for(int aid : qAsConst(this->activitiesIds))
22111 		actids+=CustomFETString::number(aid)+QString(", ");
22112 	actids.chop(2);
22113 
22114 	QString timeslots=QString("");
22115 	for(int i=0; i<this->selectedDays.count(); i++)
22116 		timeslots+=r.daysOfTheWeek[selectedDays.at(i)]+QString(" ")+r.hoursOfTheDay[selectedHours.at(i)]+QString(", ");
22117 	timeslots.chop(2);
22118 
22119 	QString s=tr("Activities occupy max time slots from selection, WP:%1%, NA:%2, A: %3, STS: %4, MTS:%5", "Constraint description. WP means weight percentage, "
22120 	 "NA means the number of activities, A means activities list, STS means selected time slots, MTS means max time slots")
22121 	 .arg(CustomFETString::number(this->weightPercentage))
22122 	 .arg(QString::number(this->activitiesIds.count()))
22123 	 .arg(actids)
22124 	 .arg(timeslots)
22125 	 .arg(CustomFETString::number(this->maxOccupiedTimeSlots));
22126 
22127 	return begin+s+end;
22128 }
22129 
getDetailedDescription(Rules & r)22130 QString ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::getDetailedDescription(Rules& r)
22131 {
22132 	assert(this->selectedDays.count()==this->selectedHours.count());
22133 
22134 	QString actids=QString("");
22135 	for(int aid : qAsConst(this->activitiesIds))
22136 		actids+=CustomFETString::number(aid)+QString(", ");
22137 	actids.chop(2);
22138 
22139 	QString timeslots=QString("");
22140 	for(int i=0; i<this->selectedDays.count(); i++)
22141 		timeslots+=r.daysOfTheWeek[selectedDays.at(i)]+QString(" ")+r.hoursOfTheDay[selectedHours.at(i)]+QString(", ");
22142 	timeslots.chop(2);
22143 
22144 	QString s=tr("Time constraint"); s+="\n";
22145 	s+=tr("Activities occupy max time slots from selection"); s+="\n";
22146 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
22147 	s+=tr("Number of activities=%1").arg(QString::number(this->activitiesIds.count())); s+="\n";
22148 	for(int id : qAsConst(this->activitiesIds)){
22149 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
22150 		 .arg(id)
22151 		 .arg(getActivityDetailedDescription(r, id));
22152 		s+="\n";
22153 	}
22154 	s+=tr("Selected time slots: %1").arg(timeslots); s+="\n";
22155 	s+=tr("Maximum number of occupied slots from selection=%1").arg(CustomFETString::number(this->maxOccupiedTimeSlots)); s+="\n";
22156 
22157 	if(!active){
22158 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
22159 		s+="\n";
22160 	}
22161 	if(!comments.isEmpty()){
22162 		s+=tr("Comments=%1").arg(comments);
22163 		s+="\n";
22164 	}
22165 
22166 	return s;
22167 }
22168 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)22169 double ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
22170 {
22171 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
22172 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
22173 		c.teachersMatrixReady=true;
22174 		c.subgroupsMatrixReady=true;
22175 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
22176 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
22177 
22178 		c.changedForMatrixCalculation=false;
22179 	}
22180 
22181 	int nbroken;
22182 
22183 	assert(r.internalStructureComputed);
22184 
22185 	///////////////////
22186 	Matrix2D<bool> used;
22187 	used.resize(r.nDaysPerWeek, r.nHoursPerDay);
22188 	for(int d=0; d<r.nDaysPerWeek; d++)
22189 		for(int h=0; h<r.nHoursPerDay; h++)
22190 			used[d][h]=false;
22191 
22192 	for(int ai : qAsConst(this->_activitiesIndices)){
22193 		if(c.times[ai]!=UNALLOCATED_TIME){
22194 			Activity* act=&r.internalActivitiesList[ai];
22195 			int d=c.times[ai]%r.nDaysPerWeek;
22196 			int h=c.times[ai]/r.nDaysPerWeek;
22197 			for(int dur=0; dur<act->duration; dur++){
22198 				assert(h+dur<r.nHoursPerDay);
22199 				used[d][h+dur]=true;
22200 			}
22201 		}
22202 	}
22203 
22204 	int cnt=0;
22205 	assert(this->selectedDays.count()==this->selectedHours.count());
22206 	for(int t=0; t<this->selectedDays.count(); t++){
22207 		int d=this->selectedDays.at(t);
22208 		int h=this->selectedHours.at(t);
22209 
22210 		if(used[d][h])
22211 			cnt++;
22212 	}
22213 
22214 	nbroken=0;
22215 
22216 	if(cnt > this->maxOccupiedTimeSlots){
22217 		nbroken=1;
22218 
22219 		if(conflictsString!=nullptr){
22220 			QString s=tr("Time constraint %1 broken - this should not happen, as this kind of constraint should "
22221 			 "have only 100.0% weight. Please report error!").arg(this->getDescription(r));
22222 
22223 			dl.append(s);
22224 			cl.append(weightPercentage/100.0);
22225 
22226 			*conflictsString+= s+"\n";
22227 		}
22228 	}
22229 
22230 	if(weightPercentage==100.0)
22231 		assert(nbroken==0);
22232 	return nbroken * weightPercentage / 100.0;
22233 }
22234 
removeUseless(Rules & r)22235 void ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::removeUseless(Rules& r)
22236 {
22237 	QList<int> newActs;
22238 
22239 	for(int aid : qAsConst(activitiesIds)){
22240 		Activity* act=r.activitiesPointerHash.value(aid, nullptr);
22241 		if(act!=nullptr)
22242 			newActs.append(aid);
22243 	}
22244 
22245 	activitiesIds=newActs;
22246 }
22247 
isRelatedToActivity(Rules & r,Activity * a)22248 bool ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::isRelatedToActivity(Rules& r, Activity* a)
22249 {
22250 	Q_UNUSED(r);
22251 
22252 	return this->activitiesIds.contains(a->id);
22253 }
22254 
isRelatedToTeacher(Teacher * t)22255 bool ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::isRelatedToTeacher(Teacher* t)
22256 {
22257 	Q_UNUSED(t);
22258 
22259 	return false;
22260 }
22261 
isRelatedToSubject(Subject * s)22262 bool ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::isRelatedToSubject(Subject* s)
22263 {
22264 	Q_UNUSED(s);
22265 
22266 	return false;
22267 }
22268 
isRelatedToActivityTag(ActivityTag * s)22269 bool ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::isRelatedToActivityTag(ActivityTag* s)
22270 {
22271 	Q_UNUSED(s);
22272 
22273 	return false;
22274 }
22275 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)22276 bool ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
22277 {
22278 	Q_UNUSED(r);
22279 	Q_UNUSED(s);
22280 
22281 	return false;
22282 }
22283 
hasWrongDayOrHour(Rules & r)22284 bool ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::hasWrongDayOrHour(Rules& r)
22285 {
22286 	assert(selectedDays.count()==selectedHours.count());
22287 
22288 	for(int i=0; i<selectedDays.count(); i++)
22289 		if(selectedDays.at(i)<0 || selectedDays.at(i)>=r.nDaysPerWeek
22290 		 || selectedHours.at(i)<0 || selectedHours.at(i)>=r.nHoursPerDay)
22291 			return true;
22292 
22293 	if(maxOccupiedTimeSlots>r.nDaysPerWeek*r.nHoursPerDay)
22294 		return true;
22295 
22296 	return false;
22297 }
22298 
canRepairWrongDayOrHour(Rules & r)22299 bool ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::canRepairWrongDayOrHour(Rules& r)
22300 {
22301 	assert(hasWrongDayOrHour(r));
22302 
22303 	return true;
22304 }
22305 
repairWrongDayOrHour(Rules & r)22306 bool ConstraintActivitiesOccupyMaxTimeSlotsFromSelection::repairWrongDayOrHour(Rules& r)
22307 {
22308 	assert(hasWrongDayOrHour(r));
22309 
22310 	assert(selectedDays.count()==selectedHours.count());
22311 
22312 	QList<int> newDays;
22313 	QList<int> newHours;
22314 
22315 	for(int i=0; i<selectedDays.count(); i++)
22316 		if(selectedDays.at(i)>=0 && selectedDays.at(i)<r.nDaysPerWeek
22317 		 && selectedHours.at(i)>=0 && selectedHours.at(i)<r.nHoursPerDay){
22318 			newDays.append(selectedDays.at(i));
22319 			newHours.append(selectedHours.at(i));
22320 		}
22321 
22322 	selectedDays=newDays;
22323 	selectedHours=newHours;
22324 
22325 	if(maxOccupiedTimeSlots>r.nDaysPerWeek*r.nHoursPerDay)
22326 		maxOccupiedTimeSlots=r.nDaysPerWeek*r.nHoursPerDay;
22327 
22328 	r.internalStructureComputed=false;
22329 	setRulesModifiedAndOtherThings(&r);
22330 
22331 	return true;
22332 }
22333 
22334 ////////////////////////////////////////////////////////////////////////////////////////////
22335 ////////////////////////////////////////////////////////////////////////////////////////////
22336 
ConstraintActivitiesOccupyMinTimeSlotsFromSelection()22337 ConstraintActivitiesOccupyMinTimeSlotsFromSelection::ConstraintActivitiesOccupyMinTimeSlotsFromSelection()
22338 	: TimeConstraint()
22339 {
22340 	this->type = CONSTRAINT_ACTIVITIES_OCCUPY_MIN_TIME_SLOTS_FROM_SELECTION;
22341 }
22342 
ConstraintActivitiesOccupyMinTimeSlotsFromSelection(double wp,QList<int> a_L,QList<int> d_L,QList<int> h_L,int min_slots)22343 ConstraintActivitiesOccupyMinTimeSlotsFromSelection::ConstraintActivitiesOccupyMinTimeSlotsFromSelection(double wp,
22344 	QList<int> a_L, QList<int> d_L, QList<int> h_L, int min_slots)
22345 	: TimeConstraint(wp)
22346 {
22347 	assert(d_L.count()==h_L.count());
22348 
22349 	this->activitiesIds=a_L;
22350 	this->selectedDays=d_L;
22351 	this->selectedHours=h_L;
22352 	this->minOccupiedTimeSlots=min_slots;
22353 
22354 	this->type=CONSTRAINT_ACTIVITIES_OCCUPY_MIN_TIME_SLOTS_FROM_SELECTION;
22355 }
22356 
computeInternalStructure(QWidget * parent,Rules & r)22357 bool ConstraintActivitiesOccupyMinTimeSlotsFromSelection::computeInternalStructure(QWidget* parent, Rules& r)
22358 {
22359 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
22360 	_activitiesIndices.clear();
22361 	for(int id : qAsConst(activitiesIds)){
22362 		int i=r.activitiesHash.value(id, -1);
22363 		if(i>=0)
22364 			_activitiesIndices.append(i);
22365 	}
22366 
22367 	/*this->_activitiesIndices.clear();
22368 
22369 	QSet<int> req=this->activitiesIds.toSet();
22370 	assert(req.count()==this->activitiesIds.count());
22371 
22372 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
22373 	int i;
22374 	for(i=0; i<r.nInternalActivities; i++)
22375 		if(req.contains(r.internalActivitiesList[i].id))
22376 			this->_activitiesIndices.append(i);*/
22377 
22378 	//////////////////////
22379 	assert(this->selectedDays.count()==this->selectedHours.count());
22380 
22381 	for(int k=0; k<this->selectedDays.count(); k++){
22382 		if(this->selectedDays.at(k) >= r.nDaysPerWeek){
22383 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22384 			 tr("Constraint activities occupy min time slots from selection is wrong because it refers to removed day. Please correct"
22385 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22386 
22387 			return false;
22388 		}
22389 		if(this->selectedHours.at(k) == r.nHoursPerDay){
22390 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22391 			 tr("Constraint activities occupy min time slots from selection is wrong because a preferred hour is too late (after the last acceptable slot). Please correct"
22392 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22393 
22394 			return false;
22395 		}
22396 		if(this->selectedHours.at(k) > r.nHoursPerDay){
22397 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22398 			 tr("Constraint activities occupy min time slots from selection is wrong because it refers to removed hour. Please correct"
22399 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22400 
22401 			return false;
22402 		}
22403 		if(this->selectedDays.at(k)<0 || this->selectedHours.at(k)<0){
22404 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22405 			 tr("Constraint activities occupy min time slots from selection is wrong because hour or day is not specified for a slot (-1). Please correct"
22406 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22407 
22408 			return false;
22409 		}
22410 	}
22411 	///////////////////////
22412 
22413 	if(this->_activitiesIndices.count()>0)
22414 		return true;
22415 	else{
22416 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
22417 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
22418 		return false;
22419 	}
22420 }
22421 
hasInactiveActivities(Rules & r)22422 bool ConstraintActivitiesOccupyMinTimeSlotsFromSelection::hasInactiveActivities(Rules& r)
22423 {
22424 	//returns true if all activities are inactive
22425 
22426 	for(int aid : qAsConst(this->activitiesIds))
22427 		if(!r.inactiveActivities.contains(aid))
22428 			return false;
22429 
22430 	return true;
22431 }
22432 
getXmlDescription(Rules & r)22433 QString ConstraintActivitiesOccupyMinTimeSlotsFromSelection::getXmlDescription(Rules& r)
22434 {
22435 	assert(this->selectedDays.count()==this->selectedHours.count());
22436 
22437 	QString s="<ConstraintActivitiesOccupyMinTimeSlotsFromSelection>\n";
22438 
22439 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
22440 
22441 	s+="	<Number_of_Activities>"+QString::number(this->activitiesIds.count())+"</Number_of_Activities>\n";
22442 	for(int aid : qAsConst(this->activitiesIds))
22443 		s+="	<Activity_Id>"+CustomFETString::number(aid)+"</Activity_Id>\n";
22444 
22445 	s+="	<Number_of_Selected_Time_Slots>"+QString::number(this->selectedDays.count())+"</Number_of_Selected_Time_Slots>\n";
22446 	for(int i=0; i<this->selectedDays.count(); i++){
22447 		s+="	<Selected_Time_Slot>\n";
22448 		s+="		<Selected_Day>"+protect(r.daysOfTheWeek[this->selectedDays.at(i)])+"</Selected_Day>\n";
22449 		s+="		<Selected_Hour>"+protect(r.hoursOfTheDay[this->selectedHours.at(i)])+"</Selected_Hour>\n";
22450 		s+="	</Selected_Time_Slot>\n";
22451 	}
22452 	s+="	<Min_Number_of_Occupied_Time_Slots>"+CustomFETString::number(this->minOccupiedTimeSlots)+"</Min_Number_of_Occupied_Time_Slots>\n";
22453 
22454 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
22455 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
22456 	s+="</ConstraintActivitiesOccupyMinTimeSlotsFromSelection>\n";
22457 	return s;
22458 }
22459 
getDescription(Rules & r)22460 QString ConstraintActivitiesOccupyMinTimeSlotsFromSelection::getDescription(Rules& r)
22461 {
22462 	QString begin=QString("");
22463 	if(!active)
22464 		begin="X - ";
22465 
22466 	QString end=QString("");
22467 	if(!comments.isEmpty())
22468 		end=", "+tr("C: %1", "Comments").arg(comments);
22469 
22470 	assert(this->selectedDays.count()==this->selectedHours.count());
22471 
22472 	QString actids=QString("");
22473 	for(int aid : qAsConst(this->activitiesIds))
22474 		actids+=CustomFETString::number(aid)+QString(", ");
22475 	actids.chop(2);
22476 
22477 	QString timeslots=QString("");
22478 	for(int i=0; i<this->selectedDays.count(); i++)
22479 		timeslots+=r.daysOfTheWeek[selectedDays.at(i)]+QString(" ")+r.hoursOfTheDay[selectedHours.at(i)]+QString(", ");
22480 	timeslots.chop(2);
22481 
22482 	QString s=tr("Activities occupy min time slots from selection, WP:%1%, NA:%2, A: %3, STS: %4, mTS:%5", "Constraint description. WP means weight percentage, "
22483 	 "NA means the number of activities, A means activities list, STS means selected time slots, mTS means min time slots")
22484 	 .arg(CustomFETString::number(this->weightPercentage))
22485 	 .arg(QString::number(this->activitiesIds.count()))
22486 	 .arg(actids)
22487 	 .arg(timeslots)
22488 	 .arg(CustomFETString::number(this->minOccupiedTimeSlots));
22489 
22490 	return begin+s+end;
22491 }
22492 
getDetailedDescription(Rules & r)22493 QString ConstraintActivitiesOccupyMinTimeSlotsFromSelection::getDetailedDescription(Rules& r)
22494 {
22495 	assert(this->selectedDays.count()==this->selectedHours.count());
22496 
22497 	QString actids=QString("");
22498 	for(int aid : qAsConst(this->activitiesIds))
22499 		actids+=CustomFETString::number(aid)+QString(", ");
22500 	actids.chop(2);
22501 
22502 	QString timeslots=QString("");
22503 	for(int i=0; i<this->selectedDays.count(); i++)
22504 		timeslots+=r.daysOfTheWeek[selectedDays.at(i)]+QString(" ")+r.hoursOfTheDay[selectedHours.at(i)]+QString(", ");
22505 	timeslots.chop(2);
22506 
22507 	QString s=tr("Time constraint"); s+="\n";
22508 	s+=tr("Activities occupy min time slots from selection"); s+="\n";
22509 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
22510 	s+=tr("Number of activities=%1").arg(QString::number(this->activitiesIds.count())); s+="\n";
22511 	for(int id : qAsConst(this->activitiesIds)){
22512 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
22513 		 .arg(id)
22514 		 .arg(getActivityDetailedDescription(r, id));
22515 		s+="\n";
22516 	}
22517 	s+=tr("Selected time slots: %1").arg(timeslots); s+="\n";
22518 	s+=tr("Minimum number of occupied slots from selection=%1").arg(CustomFETString::number(this->minOccupiedTimeSlots)); s+="\n";
22519 
22520 	if(!active){
22521 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
22522 		s+="\n";
22523 	}
22524 	if(!comments.isEmpty()){
22525 		s+=tr("Comments=%1").arg(comments);
22526 		s+="\n";
22527 	}
22528 
22529 	return s;
22530 }
22531 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)22532 double ConstraintActivitiesOccupyMinTimeSlotsFromSelection::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
22533 {
22534 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
22535 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
22536 		c.teachersMatrixReady=true;
22537 		c.subgroupsMatrixReady=true;
22538 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
22539 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
22540 
22541 		c.changedForMatrixCalculation=false;
22542 	}
22543 
22544 	int nbroken;
22545 
22546 	assert(r.internalStructureComputed);
22547 
22548 	///////////////////
22549 	Matrix2D<bool> used;
22550 	used.resize(r.nDaysPerWeek, r.nHoursPerDay);
22551 	for(int d=0; d<r.nDaysPerWeek; d++)
22552 		for(int h=0; h<r.nHoursPerDay; h++)
22553 			used[d][h]=false;
22554 
22555 	for(int ai : qAsConst(this->_activitiesIndices)){
22556 		if(c.times[ai]!=UNALLOCATED_TIME){
22557 			Activity* act=&r.internalActivitiesList[ai];
22558 			int d=c.times[ai]%r.nDaysPerWeek;
22559 			int h=c.times[ai]/r.nDaysPerWeek;
22560 			for(int dur=0; dur<act->duration; dur++){
22561 				assert(h+dur<r.nHoursPerDay);
22562 				used[d][h+dur]=true;
22563 			}
22564 		}
22565 	}
22566 
22567 	int cnt=0;
22568 	assert(this->selectedDays.count()==this->selectedHours.count());
22569 	for(int t=0; t<this->selectedDays.count(); t++){
22570 		int d=this->selectedDays.at(t);
22571 		int h=this->selectedHours.at(t);
22572 
22573 		if(used[d][h])
22574 			cnt++;
22575 	}
22576 
22577 	nbroken=0;
22578 
22579 	if(cnt < this->minOccupiedTimeSlots){
22580 		nbroken=1;
22581 
22582 		if(conflictsString!=nullptr){
22583 			QString s;
22584 			if(c.nPlacedActivities==r.nInternalActivities){
22585 				s=tr("Time constraint %1 broken - this should not happen, as this kind of constraint should "
22586 				 "have only 100.0% weight. Please report error!").arg(this->getDescription(r));
22587 			}
22588 			else{
22589 				s=tr("Time constraint %1 broken for the partial timetable.").arg(this->getDescription(r));
22590 				s+=" ";
22591 				s+=tr("Conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
22592 			}
22593 
22594 			dl.append(s);
22595 			cl.append(weightPercentage/100.0);
22596 
22597 			*conflictsString+= s+"\n";
22598 		}
22599 	}
22600 
22601 	if(c.nPlacedActivities==r.nInternalActivities)
22602 		if(weightPercentage==100.0)
22603 			assert(nbroken==0);
22604 	return nbroken * weightPercentage / 100.0;
22605 }
22606 
removeUseless(Rules & r)22607 void ConstraintActivitiesOccupyMinTimeSlotsFromSelection::removeUseless(Rules& r)
22608 {
22609 	QList<int> newActs;
22610 
22611 	for(int aid : qAsConst(activitiesIds)){
22612 		Activity* act=r.activitiesPointerHash.value(aid, nullptr);
22613 		if(act!=nullptr)
22614 			newActs.append(aid);
22615 	}
22616 
22617 	activitiesIds=newActs;
22618 }
22619 
isRelatedToActivity(Rules & r,Activity * a)22620 bool ConstraintActivitiesOccupyMinTimeSlotsFromSelection::isRelatedToActivity(Rules& r, Activity* a)
22621 {
22622 	Q_UNUSED(r);
22623 
22624 	return this->activitiesIds.contains(a->id);
22625 }
22626 
isRelatedToTeacher(Teacher * t)22627 bool ConstraintActivitiesOccupyMinTimeSlotsFromSelection::isRelatedToTeacher(Teacher* t)
22628 {
22629 	Q_UNUSED(t);
22630 
22631 	return false;
22632 }
22633 
isRelatedToSubject(Subject * s)22634 bool ConstraintActivitiesOccupyMinTimeSlotsFromSelection::isRelatedToSubject(Subject* s)
22635 {
22636 	Q_UNUSED(s);
22637 
22638 	return false;
22639 }
22640 
isRelatedToActivityTag(ActivityTag * s)22641 bool ConstraintActivitiesOccupyMinTimeSlotsFromSelection::isRelatedToActivityTag(ActivityTag* s)
22642 {
22643 	Q_UNUSED(s);
22644 
22645 	return false;
22646 }
22647 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)22648 bool ConstraintActivitiesOccupyMinTimeSlotsFromSelection::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
22649 {
22650 	Q_UNUSED(r);
22651 	Q_UNUSED(s);
22652 
22653 	return false;
22654 }
22655 
hasWrongDayOrHour(Rules & r)22656 bool ConstraintActivitiesOccupyMinTimeSlotsFromSelection::hasWrongDayOrHour(Rules& r)
22657 {
22658 	assert(selectedDays.count()==selectedHours.count());
22659 
22660 	for(int i=0; i<selectedDays.count(); i++)
22661 		if(selectedDays.at(i)<0 || selectedDays.at(i)>=r.nDaysPerWeek
22662 		 || selectedHours.at(i)<0 || selectedHours.at(i)>=r.nHoursPerDay)
22663 			return true;
22664 
22665 	if(minOccupiedTimeSlots>r.nDaysPerWeek*r.nHoursPerDay)
22666 		return true;
22667 
22668 	return false;
22669 }
22670 
canRepairWrongDayOrHour(Rules & r)22671 bool ConstraintActivitiesOccupyMinTimeSlotsFromSelection::canRepairWrongDayOrHour(Rules& r)
22672 {
22673 	assert(hasWrongDayOrHour(r));
22674 
22675 	return true;
22676 }
22677 
repairWrongDayOrHour(Rules & r)22678 bool ConstraintActivitiesOccupyMinTimeSlotsFromSelection::repairWrongDayOrHour(Rules& r)
22679 {
22680 	assert(hasWrongDayOrHour(r));
22681 
22682 	assert(selectedDays.count()==selectedHours.count());
22683 
22684 	QList<int> newDays;
22685 	QList<int> newHours;
22686 
22687 	for(int i=0; i<selectedDays.count(); i++)
22688 		if(selectedDays.at(i)>=0 && selectedDays.at(i)<r.nDaysPerWeek
22689 		 && selectedHours.at(i)>=0 && selectedHours.at(i)<r.nHoursPerDay){
22690 			newDays.append(selectedDays.at(i));
22691 			newHours.append(selectedHours.at(i));
22692 		}
22693 
22694 	selectedDays=newDays;
22695 	selectedHours=newHours;
22696 
22697 	if(minOccupiedTimeSlots>r.nDaysPerWeek*r.nHoursPerDay)
22698 		minOccupiedTimeSlots=r.nDaysPerWeek*r.nHoursPerDay;
22699 
22700 	r.internalStructureComputed=false;
22701 	setRulesModifiedAndOtherThings(&r);
22702 
22703 	return true;
22704 }
22705 
22706 ////////////////////////////////////////////////////////////////////////////////////////////
22707 ////////////////////////////////////////////////////////////////////////////////////////////
22708 
ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots()22709 ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots()
22710 	: TimeConstraint()
22711 {
22712 	this->type = CONSTRAINT_ACTIVITIES_MAX_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS;
22713 }
22714 
ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots(double wp,QList<int> a_L,QList<int> d_L,QList<int> h_L,int max_simultaneous)22715 ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots(double wp,
22716 	QList<int> a_L, QList<int> d_L, QList<int> h_L, int max_simultaneous)
22717 	: TimeConstraint(wp)
22718 {
22719 	assert(d_L.count()==h_L.count());
22720 
22721 	this->activitiesIds=a_L;
22722 	this->selectedDays=d_L;
22723 	this->selectedHours=h_L;
22724 	this->maxSimultaneous=max_simultaneous;
22725 
22726 	this->type=CONSTRAINT_ACTIVITIES_MAX_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS;
22727 }
22728 
computeInternalStructure(QWidget * parent,Rules & r)22729 bool ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::computeInternalStructure(QWidget* parent, Rules& r)
22730 {
22731 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
22732 	_activitiesIndices.clear();
22733 	for(int id : qAsConst(activitiesIds)){
22734 		int i=r.activitiesHash.value(id, -1);
22735 		if(i>=0)
22736 			_activitiesIndices.append(i);
22737 	}
22738 
22739 	/*this->_activitiesIndices.clear();
22740 
22741 	QSet<int> req=this->activitiesIds.toSet();
22742 	assert(req.count()==this->activitiesIds.count());
22743 
22744 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
22745 	int i;
22746 	for(i=0; i<r.nInternalActivities; i++)
22747 		if(req.contains(r.internalActivitiesList[i].id))
22748 			this->_activitiesIndices.append(i);*/
22749 
22750 	//////////////////////
22751 	assert(this->selectedDays.count()==this->selectedHours.count());
22752 
22753 	for(int k=0; k<this->selectedDays.count(); k++){
22754 		if(this->selectedDays.at(k) >= r.nDaysPerWeek){
22755 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22756 			 tr("Constraint activities max simultaneous in selected time slots is wrong because it refers to removed day. Please correct"
22757 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22758 
22759 			return false;
22760 		}
22761 		if(this->selectedHours.at(k) == r.nHoursPerDay){
22762 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22763 			 tr("Constraint activities max simultaneous in selected time slots is wrong because a preferred hour is too late (after the last acceptable slot). Please correct"
22764 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22765 
22766 			return false;
22767 		}
22768 		if(this->selectedHours.at(k) > r.nHoursPerDay){
22769 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22770 			 tr("Constraint activities max simultaneous in selected time slots is wrong because it refers to removed hour. Please correct"
22771 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22772 
22773 			return false;
22774 		}
22775 		if(this->selectedDays.at(k)<0 || this->selectedHours.at(k)<0){
22776 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
22777 			 tr("Constraint activities max simultaneous in selected time slots is wrong because hour or day is not specified for a slot (-1). Please correct"
22778 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
22779 
22780 			return false;
22781 		}
22782 	}
22783 	///////////////////////
22784 
22785 	if(this->_activitiesIndices.count()>0)
22786 		return true;
22787 	else{
22788 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
22789 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
22790 		return false;
22791 	}
22792 }
22793 
hasInactiveActivities(Rules & r)22794 bool ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::hasInactiveActivities(Rules& r)
22795 {
22796 	//returns true if all activities are inactive
22797 
22798 	for(int aid : qAsConst(this->activitiesIds))
22799 		if(!r.inactiveActivities.contains(aid))
22800 			return false;
22801 
22802 	return true;
22803 }
22804 
getXmlDescription(Rules & r)22805 QString ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::getXmlDescription(Rules& r)
22806 {
22807 	assert(this->selectedDays.count()==this->selectedHours.count());
22808 
22809 	QString s="<ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots>\n";
22810 
22811 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
22812 
22813 	s+="	<Number_of_Activities>"+QString::number(this->activitiesIds.count())+"</Number_of_Activities>\n";
22814 	for(int aid : qAsConst(this->activitiesIds))
22815 		s+="	<Activity_Id>"+CustomFETString::number(aid)+"</Activity_Id>\n";
22816 
22817 	s+="	<Number_of_Selected_Time_Slots>"+QString::number(this->selectedDays.count())+"</Number_of_Selected_Time_Slots>\n";
22818 	for(int i=0; i<this->selectedDays.count(); i++){
22819 		s+="	<Selected_Time_Slot>\n";
22820 		s+="		<Selected_Day>"+protect(r.daysOfTheWeek[this->selectedDays.at(i)])+"</Selected_Day>\n";
22821 		s+="		<Selected_Hour>"+protect(r.hoursOfTheDay[this->selectedHours.at(i)])+"</Selected_Hour>\n";
22822 		s+="	</Selected_Time_Slot>\n";
22823 	}
22824 	s+="	<Max_Number_of_Simultaneous_Activities>"+CustomFETString::number(this->maxSimultaneous)+"</Max_Number_of_Simultaneous_Activities>\n";
22825 
22826 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
22827 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
22828 	s+="</ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots>\n";
22829 	return s;
22830 }
22831 
getDescription(Rules & r)22832 QString ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::getDescription(Rules& r)
22833 {
22834 	QString begin=QString("");
22835 	if(!active)
22836 		begin="X - ";
22837 
22838 	QString end=QString("");
22839 	if(!comments.isEmpty())
22840 		end=", "+tr("C: %1", "Comments").arg(comments);
22841 
22842 	assert(this->selectedDays.count()==this->selectedHours.count());
22843 
22844 	QString actids=QString("");
22845 	for(int aid : qAsConst(this->activitiesIds))
22846 		actids+=CustomFETString::number(aid)+QString(", ");
22847 	actids.chop(2);
22848 
22849 	QString timeslots=QString("");
22850 	for(int i=0; i<this->selectedDays.count(); i++)
22851 		timeslots+=r.daysOfTheWeek[selectedDays.at(i)]+QString(" ")+r.hoursOfTheDay[selectedHours.at(i)]+QString(", ");
22852 	timeslots.chop(2);
22853 
22854 	QString s=tr("Activities max simultaneous in selected time slots, WP:%1%, NA:%2, A: %3, STS: %4, MS:%5", "Constraint description. WP means weight percentage, "
22855 	 "NA means the number of activities, A means activities list, STS means selected time slots, MS means max simultaneous (number of activities in each selected time slot)")
22856 	 .arg(CustomFETString::number(this->weightPercentage))
22857 	 .arg(QString::number(this->activitiesIds.count()))
22858 	 .arg(actids)
22859 	 .arg(timeslots)
22860 	 .arg(CustomFETString::number(this->maxSimultaneous));
22861 
22862 	return begin+s+end;
22863 }
22864 
getDetailedDescription(Rules & r)22865 QString ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::getDetailedDescription(Rules& r)
22866 {
22867 	assert(this->selectedDays.count()==this->selectedHours.count());
22868 
22869 	QString actids=QString("");
22870 	for(int aid : qAsConst(this->activitiesIds))
22871 		actids+=CustomFETString::number(aid)+QString(", ");
22872 	actids.chop(2);
22873 
22874 	QString timeslots=QString("");
22875 	for(int i=0; i<this->selectedDays.count(); i++)
22876 		timeslots+=r.daysOfTheWeek[selectedDays.at(i)]+QString(" ")+r.hoursOfTheDay[selectedHours.at(i)]+QString(", ");
22877 	timeslots.chop(2);
22878 
22879 	QString s=tr("Time constraint"); s+="\n";
22880 	s+=tr("Activities max simultaneous in selected time slots"); s+="\n";
22881 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
22882 	s+=tr("Number of activities=%1").arg(QString::number(this->activitiesIds.count())); s+="\n";
22883 	for(int id : qAsConst(this->activitiesIds)){
22884 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
22885 		 .arg(id)
22886 		 .arg(getActivityDetailedDescription(r, id));
22887 		s+="\n";
22888 	}
22889 	s+=tr("Selected time slots: %1").arg(timeslots); s+="\n";
22890 	s+=tr("Maximum number of simultaneous activities in each selected time slot=%1").arg(CustomFETString::number(this->maxSimultaneous)); s+="\n";
22891 
22892 	if(!active){
22893 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
22894 		s+="\n";
22895 	}
22896 	if(!comments.isEmpty()){
22897 		s+=tr("Comments=%1").arg(comments);
22898 		s+="\n";
22899 	}
22900 
22901 	return s;
22902 }
22903 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)22904 double ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
22905 {
22906 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
22907 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
22908 		c.teachersMatrixReady=true;
22909 		c.subgroupsMatrixReady=true;
22910 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
22911 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
22912 
22913 		c.changedForMatrixCalculation=false;
22914 	}
22915 
22916 	int nbroken;
22917 
22918 	assert(r.internalStructureComputed);
22919 
22920 ///////////////////
22921 
22922 	Matrix2D<int> count;
22923 	count.resize(r.nDaysPerWeek, r.nHoursPerDay);
22924 	for(int d=0; d<r.nDaysPerWeek; d++)
22925 		for(int h=0; h<r.nHoursPerDay; h++)
22926 			count[d][h]=0;
22927 
22928 	for(int ai : qAsConst(this->_activitiesIndices)){
22929 		if(c.times[ai]!=UNALLOCATED_TIME){
22930 			Activity* act=&r.internalActivitiesList[ai];
22931 			int d=c.times[ai]%r.nDaysPerWeek;
22932 			int h=c.times[ai]/r.nDaysPerWeek;
22933 			for(int dur=0; dur<act->duration; dur++){
22934 				assert(h+dur<r.nHoursPerDay);
22935 				count[d][h+dur]++;
22936 			}
22937 		}
22938 	}
22939 
22940 	nbroken=0;
22941 
22942 	assert(this->selectedDays.count()==this->selectedHours.count());
22943 	for(int t=0; t<this->selectedDays.count(); t++){
22944 		int d=this->selectedDays.at(t);
22945 		int h=this->selectedHours.at(t);
22946 
22947 		if(count[d][h] > this->maxSimultaneous)
22948 			nbroken++;
22949 	}
22950 
22951 	if(nbroken>0){
22952 		if(conflictsString!=nullptr){
22953 			QString s=tr("Time constraint %1 broken - this should not happen, as this kind of constraint should "
22954 			 "have only 100.0% weight. Please report error!").arg(this->getDescription(r));
22955 
22956 			dl.append(s);
22957 			cl.append(weightPercentage/100.0);
22958 
22959 			*conflictsString+= s+"\n";
22960 		}
22961 	}
22962 
22963 	if(weightPercentage==100.0)
22964 		assert(nbroken==0);
22965 	return nbroken * weightPercentage / 100.0;
22966 }
22967 
removeUseless(Rules & r)22968 void ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::removeUseless(Rules& r)
22969 {
22970 	QList<int> newActs;
22971 
22972 	for(int aid : qAsConst(activitiesIds)){
22973 		Activity* act=r.activitiesPointerHash.value(aid, nullptr);
22974 		if(act!=nullptr)
22975 			newActs.append(aid);
22976 	}
22977 
22978 	activitiesIds=newActs;
22979 }
22980 
isRelatedToActivity(Rules & r,Activity * a)22981 bool ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::isRelatedToActivity(Rules& r, Activity* a)
22982 {
22983 	Q_UNUSED(r);
22984 
22985 	return this->activitiesIds.contains(a->id);
22986 }
22987 
isRelatedToTeacher(Teacher * t)22988 bool ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::isRelatedToTeacher(Teacher* t)
22989 {
22990 	Q_UNUSED(t);
22991 
22992 	return false;
22993 }
22994 
isRelatedToSubject(Subject * s)22995 bool ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::isRelatedToSubject(Subject* s)
22996 {
22997 	Q_UNUSED(s);
22998 
22999 	return false;
23000 }
23001 
isRelatedToActivityTag(ActivityTag * s)23002 bool ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::isRelatedToActivityTag(ActivityTag* s)
23003 {
23004 	Q_UNUSED(s);
23005 
23006 	return false;
23007 }
23008 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)23009 bool ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
23010 {
23011 	Q_UNUSED(r);
23012 	Q_UNUSED(s);
23013 
23014 	return false;
23015 }
23016 
hasWrongDayOrHour(Rules & r)23017 bool ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::hasWrongDayOrHour(Rules& r)
23018 {
23019 	assert(selectedDays.count()==selectedHours.count());
23020 
23021 	for(int i=0; i<selectedDays.count(); i++)
23022 		if(selectedDays.at(i)<0 || selectedDays.at(i)>=r.nDaysPerWeek
23023 		 || selectedHours.at(i)<0 || selectedHours.at(i)>=r.nHoursPerDay)
23024 			return true;
23025 
23026 	//Do not care about maxSimultaneous, which can be as high as MAX_ACTIVITIES
23027 
23028 	return false;
23029 }
23030 
canRepairWrongDayOrHour(Rules & r)23031 bool ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::canRepairWrongDayOrHour(Rules& r)
23032 {
23033 	assert(hasWrongDayOrHour(r));
23034 
23035 	return true;
23036 }
23037 
repairWrongDayOrHour(Rules & r)23038 bool ConstraintActivitiesMaxSimultaneousInSelectedTimeSlots::repairWrongDayOrHour(Rules& r)
23039 {
23040 	assert(hasWrongDayOrHour(r));
23041 
23042 	assert(selectedDays.count()==selectedHours.count());
23043 
23044 	QList<int> newDays;
23045 	QList<int> newHours;
23046 
23047 	for(int i=0; i<selectedDays.count(); i++)
23048 		if(selectedDays.at(i)>=0 && selectedDays.at(i)<r.nDaysPerWeek
23049 		 && selectedHours.at(i)>=0 && selectedHours.at(i)<r.nHoursPerDay){
23050 			newDays.append(selectedDays.at(i));
23051 			newHours.append(selectedHours.at(i));
23052 		}
23053 
23054 	selectedDays=newDays;
23055 	selectedHours=newHours;
23056 
23057 	//Do not modify maxSimultaneous, which can be as high as MAX_ACTIVITIES
23058 
23059 	r.internalStructureComputed=false;
23060 	setRulesModifiedAndOtherThings(&r);
23061 
23062 	return true;
23063 }
23064 
23065 ////////////////////////////////////////////////////////////////////////////////////////////
23066 ////////////////////////////////////////////////////////////////////////////////////////////
23067 
ConstraintActivitiesMinSimultaneousInSelectedTimeSlots()23068 ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::ConstraintActivitiesMinSimultaneousInSelectedTimeSlots()
23069 	: TimeConstraint()
23070 {
23071 	this->type = CONSTRAINT_ACTIVITIES_MIN_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS;
23072 }
23073 
ConstraintActivitiesMinSimultaneousInSelectedTimeSlots(double wp,QList<int> a_L,QList<int> d_L,QList<int> h_L,int min_simultaneous,bool allow_empty_slots)23074 ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::ConstraintActivitiesMinSimultaneousInSelectedTimeSlots(double wp,
23075 	QList<int> a_L, QList<int> d_L, QList<int> h_L, int min_simultaneous, bool allow_empty_slots)
23076 	: TimeConstraint(wp)
23077 {
23078 	assert(d_L.count()==h_L.count());
23079 
23080 	this->activitiesIds=a_L;
23081 	this->selectedDays=d_L;
23082 	this->selectedHours=h_L;
23083 	this->minSimultaneous=min_simultaneous;
23084 	this->allowEmptySlots=allow_empty_slots;
23085 
23086 	this->type=CONSTRAINT_ACTIVITIES_MIN_SIMULTANEOUS_IN_SELECTED_TIME_SLOTS;
23087 }
23088 
computeInternalStructure(QWidget * parent,Rules & r)23089 bool ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::computeInternalStructure(QWidget* parent, Rules& r)
23090 {
23091 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
23092 	_activitiesIndices.clear();
23093 	for(int id : qAsConst(activitiesIds)){
23094 		int i=r.activitiesHash.value(id, -1);
23095 		if(i>=0)
23096 			_activitiesIndices.append(i);
23097 	}
23098 
23099 	/*this->_activitiesIndices.clear();
23100 
23101 	QSet<int> req=this->activitiesIds.toSet();
23102 	assert(req.count()==this->activitiesIds.count());
23103 
23104 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
23105 	int i;
23106 	for(i=0; i<r.nInternalActivities; i++)
23107 		if(req.contains(r.internalActivitiesList[i].id))
23108 			this->_activitiesIndices.append(i);*/
23109 
23110 	//////////////////////
23111 	assert(this->selectedDays.count()==this->selectedHours.count());
23112 
23113 	for(int k=0; k<this->selectedDays.count(); k++){
23114 		if(this->selectedDays.at(k) >= r.nDaysPerWeek){
23115 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
23116 			 tr("Constraint activities min simultaneous in selected time slots is wrong because it refers to removed day. Please correct"
23117 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
23118 
23119 			return false;
23120 		}
23121 		if(this->selectedHours.at(k) == r.nHoursPerDay){
23122 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
23123 			 tr("Constraint activities min simultaneous in selected time slots is wrong because a preferred hour is too late (after the last acceptable slot). Please correct"
23124 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
23125 
23126 			return false;
23127 		}
23128 		if(this->selectedHours.at(k) > r.nHoursPerDay){
23129 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
23130 			 tr("Constraint activities min simultaneous in selected time slots is wrong because it refers to removed hour. Please correct"
23131 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
23132 
23133 			return false;
23134 		}
23135 		if(this->selectedDays.at(k)<0 || this->selectedHours.at(k)<0){
23136 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
23137 			 tr("Constraint activities min simultaneous in selected time slots is wrong because hour or day is not specified for a slot (-1). Please correct"
23138 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
23139 
23140 			return false;
23141 		}
23142 	}
23143 	///////////////////////
23144 
23145 	if(this->_activitiesIndices.count()>0)
23146 		return true;
23147 	else{
23148 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
23149 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
23150 		return false;
23151 	}
23152 }
23153 
hasInactiveActivities(Rules & r)23154 bool ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::hasInactiveActivities(Rules& r)
23155 {
23156 	//returns true if all activities are inactive
23157 
23158 	for(int aid : qAsConst(this->activitiesIds))
23159 		if(!r.inactiveActivities.contains(aid))
23160 			return false;
23161 
23162 	return true;
23163 }
23164 
getXmlDescription(Rules & r)23165 QString ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::getXmlDescription(Rules& r)
23166 {
23167 	assert(this->selectedDays.count()==this->selectedHours.count());
23168 
23169 	QString s="<ConstraintActivitiesMinSimultaneousInSelectedTimeSlots>\n";
23170 
23171 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
23172 
23173 	s+="	<Number_of_Activities>"+QString::number(this->activitiesIds.count())+"</Number_of_Activities>\n";
23174 	for(int aid : qAsConst(this->activitiesIds))
23175 		s+="	<Activity_Id>"+CustomFETString::number(aid)+"</Activity_Id>\n";
23176 
23177 	s+="	<Number_of_Selected_Time_Slots>"+QString::number(this->selectedDays.count())+"</Number_of_Selected_Time_Slots>\n";
23178 	for(int i=0; i<this->selectedDays.count(); i++){
23179 		s+="	<Selected_Time_Slot>\n";
23180 		s+="		<Selected_Day>"+protect(r.daysOfTheWeek[this->selectedDays.at(i)])+"</Selected_Day>\n";
23181 		s+="		<Selected_Hour>"+protect(r.hoursOfTheDay[this->selectedHours.at(i)])+"</Selected_Hour>\n";
23182 		s+="	</Selected_Time_Slot>\n";
23183 	}
23184 	s+="	<Min_Number_of_Simultaneous_Activities>"+CustomFETString::number(this->minSimultaneous)+"</Min_Number_of_Simultaneous_Activities>\n";
23185 	s+="	<Allow_Empty_Slots>"+trueFalse(allowEmptySlots)+"</Allow_Empty_Slots>\n";
23186 
23187 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
23188 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
23189 	s+="</ConstraintActivitiesMinSimultaneousInSelectedTimeSlots>\n";
23190 	return s;
23191 }
23192 
getDescription(Rules & r)23193 QString ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::getDescription(Rules& r)
23194 {
23195 	QString begin=QString("");
23196 	if(!active)
23197 		begin="X - ";
23198 
23199 	QString end=QString("");
23200 	if(!comments.isEmpty())
23201 		end=", "+tr("C: %1", "Comments").arg(comments);
23202 
23203 	assert(this->selectedDays.count()==this->selectedHours.count());
23204 
23205 	QString actids=QString("");
23206 	for(int aid : qAsConst(this->activitiesIds))
23207 		actids+=CustomFETString::number(aid)+QString(", ");
23208 	actids.chop(2);
23209 
23210 	QString timeslots=QString("");
23211 	for(int i=0; i<this->selectedDays.count(); i++)
23212 		timeslots+=r.daysOfTheWeek[selectedDays.at(i)]+QString(" ")+r.hoursOfTheDay[selectedHours.at(i)]+QString(", ");
23213 	timeslots.chop(2);
23214 
23215 	QString s=tr("Activities min simultaneous in selected time slots, WP:%1%, NA:%2, A: %3, STS: %4, mS:%5, AES=%6", "Constraint description. WP means weight percentage, "
23216 	 "NA means the number of activities, A means activities list, STS means selected time slots, mS means min simultaneous (number of activities in each selected time slot), "
23217 	 "AES means allow empty slots.")
23218 	 .arg(CustomFETString::number(this->weightPercentage))
23219 	 .arg(QString::number(this->activitiesIds.count()))
23220 	 .arg(actids)
23221 	 .arg(timeslots)
23222 	 .arg(CustomFETString::number(this->minSimultaneous))
23223 	 .arg(yesNoTranslated(allowEmptySlots));
23224 
23225 	return begin+s+end;
23226 }
23227 
getDetailedDescription(Rules & r)23228 QString ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::getDetailedDescription(Rules& r)
23229 {
23230 	assert(this->selectedDays.count()==this->selectedHours.count());
23231 
23232 	QString actids=QString("");
23233 	for(int aid : qAsConst(this->activitiesIds))
23234 		actids+=CustomFETString::number(aid)+QString(", ");
23235 	actids.chop(2);
23236 
23237 	QString timeslots=QString("");
23238 	for(int i=0; i<this->selectedDays.count(); i++)
23239 		timeslots+=r.daysOfTheWeek[selectedDays.at(i)]+QString(" ")+r.hoursOfTheDay[selectedHours.at(i)]+QString(", ");
23240 	timeslots.chop(2);
23241 
23242 	QString s=tr("Time constraint"); s+="\n";
23243 	s+=tr("Activities min simultaneous in selected time slots"); s+="\n";
23244 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
23245 	s+=tr("Number of activities=%1").arg(QString::number(this->activitiesIds.count())); s+="\n";
23246 	for(int id : qAsConst(this->activitiesIds)){
23247 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
23248 		 .arg(id)
23249 		 .arg(getActivityDetailedDescription(r, id));
23250 		s+="\n";
23251 	}
23252 	s+=tr("Selected time slots: %1").arg(timeslots); s+="\n";
23253 	s+=tr("Minimum number of simultaneous activities in each selected time slot=%1").arg(CustomFETString::number(this->minSimultaneous)); s+="\n";
23254 	s+=tr("Allow empty slots=%1").arg(yesNoTranslated(allowEmptySlots)); s+="\n";
23255 
23256 	if(!active){
23257 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
23258 		s+="\n";
23259 	}
23260 	if(!comments.isEmpty()){
23261 		s+=tr("Comments=%1").arg(comments);
23262 		s+="\n";
23263 	}
23264 
23265 	return s;
23266 }
23267 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)23268 double ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
23269 {
23270 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
23271 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
23272 		c.teachersMatrixReady=true;
23273 		c.subgroupsMatrixReady=true;
23274 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
23275 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
23276 
23277 		c.changedForMatrixCalculation=false;
23278 	}
23279 
23280 	int nbroken;
23281 
23282 	assert(r.internalStructureComputed);
23283 
23284 ///////////////////
23285 
23286 	Matrix2D<int> count;
23287 	count.resize(r.nDaysPerWeek, r.nHoursPerDay);
23288 	for(int d=0; d<r.nDaysPerWeek; d++)
23289 		for(int h=0; h<r.nHoursPerDay; h++)
23290 			count[d][h]=0;
23291 
23292 	for(int ai : qAsConst(this->_activitiesIndices)){
23293 		if(c.times[ai]!=UNALLOCATED_TIME){
23294 			Activity* act=&r.internalActivitiesList[ai];
23295 			int d=c.times[ai]%r.nDaysPerWeek;
23296 			int h=c.times[ai]/r.nDaysPerWeek;
23297 			for(int dur=0; dur<act->duration; dur++){
23298 				assert(h+dur<r.nHoursPerDay);
23299 				count[d][h+dur]++;
23300 			}
23301 		}
23302 	}
23303 
23304 	nbroken=0;
23305 
23306 	assert(this->selectedDays.count()==this->selectedHours.count());
23307 	for(int t=0; t<this->selectedDays.count(); t++){
23308 		int d=this->selectedDays.at(t);
23309 		int h=this->selectedHours.at(t);
23310 
23311 		if(allowEmptySlots && count[d][h]>0 && count[d][h] < this->minSimultaneous)
23312 			nbroken++;
23313 		else if(!allowEmptySlots && count[d][h] < this->minSimultaneous)
23314 			nbroken++;
23315 	}
23316 
23317 	if(nbroken>0){
23318 		if(conflictsString!=nullptr){
23319 			QString s;
23320 			if(c.nPlacedActivities==r.nInternalActivities){
23321 				s=tr("Time constraint %1 broken - this should not happen, as this kind of constraint should "
23322 				 "have only 100.0% weight. Please report error!").arg(this->getDescription(r));
23323 			}
23324 			else{
23325 				s=tr("Time constraint %1 broken for the partial timetable.").arg(this->getDescription(r));
23326 				s+=" ";
23327 				s+=tr("Conflicts factor increase=%1").arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
23328 			}
23329 
23330 			dl.append(s);
23331 			cl.append(weightPercentage/100.0);
23332 
23333 			*conflictsString+= s+"\n";
23334 		}
23335 	}
23336 
23337 	if(c.nPlacedActivities==r.nInternalActivities)
23338 		if(weightPercentage==100.0)
23339 			assert(nbroken==0);
23340 	return nbroken * weightPercentage / 100.0;
23341 }
23342 
removeUseless(Rules & r)23343 void ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::removeUseless(Rules& r)
23344 {
23345 	QList<int> newActs;
23346 
23347 	for(int aid : qAsConst(activitiesIds)){
23348 		Activity* act=r.activitiesPointerHash.value(aid, nullptr);
23349 		if(act!=nullptr)
23350 			newActs.append(aid);
23351 	}
23352 
23353 	activitiesIds=newActs;
23354 }
23355 
isRelatedToActivity(Rules & r,Activity * a)23356 bool ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::isRelatedToActivity(Rules& r, Activity* a)
23357 {
23358 	Q_UNUSED(r);
23359 
23360 	return this->activitiesIds.contains(a->id);
23361 }
23362 
isRelatedToTeacher(Teacher * t)23363 bool ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::isRelatedToTeacher(Teacher* t)
23364 {
23365 	Q_UNUSED(t);
23366 
23367 	return false;
23368 }
23369 
isRelatedToSubject(Subject * s)23370 bool ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::isRelatedToSubject(Subject* s)
23371 {
23372 	Q_UNUSED(s);
23373 
23374 	return false;
23375 }
23376 
isRelatedToActivityTag(ActivityTag * s)23377 bool ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::isRelatedToActivityTag(ActivityTag* s)
23378 {
23379 	Q_UNUSED(s);
23380 
23381 	return false;
23382 }
23383 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)23384 bool ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
23385 {
23386 	Q_UNUSED(r);
23387 	Q_UNUSED(s);
23388 
23389 	return false;
23390 }
23391 
hasWrongDayOrHour(Rules & r)23392 bool ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::hasWrongDayOrHour(Rules& r)
23393 {
23394 	assert(selectedDays.count()==selectedHours.count());
23395 
23396 	for(int i=0; i<selectedDays.count(); i++)
23397 		if(selectedDays.at(i)<0 || selectedDays.at(i)>=r.nDaysPerWeek
23398 		 || selectedHours.at(i)<0 || selectedHours.at(i)>=r.nHoursPerDay)
23399 			return true;
23400 
23401 	//Do not care about minSimultaneous, which can be as high as MAX_ACTIVITIES
23402 
23403 	return false;
23404 }
23405 
canRepairWrongDayOrHour(Rules & r)23406 bool ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::canRepairWrongDayOrHour(Rules& r)
23407 {
23408 	assert(hasWrongDayOrHour(r));
23409 
23410 	return true;
23411 }
23412 
repairWrongDayOrHour(Rules & r)23413 bool ConstraintActivitiesMinSimultaneousInSelectedTimeSlots::repairWrongDayOrHour(Rules& r)
23414 {
23415 	assert(hasWrongDayOrHour(r));
23416 
23417 	assert(selectedDays.count()==selectedHours.count());
23418 
23419 	QList<int> newDays;
23420 	QList<int> newHours;
23421 
23422 	for(int i=0; i<selectedDays.count(); i++)
23423 		if(selectedDays.at(i)>=0 && selectedDays.at(i)<r.nDaysPerWeek
23424 		 && selectedHours.at(i)>=0 && selectedHours.at(i)<r.nHoursPerDay){
23425 			newDays.append(selectedDays.at(i));
23426 			newHours.append(selectedHours.at(i));
23427 		}
23428 
23429 	selectedDays=newDays;
23430 	selectedHours=newHours;
23431 
23432 	//Do not modify minSimultaneous, which can be as high as MAX_ACTIVITIES
23433 
23434 	r.internalStructureComputed=false;
23435 	setRulesModifiedAndOtherThings(&r);
23436 
23437 	return true;
23438 }
23439 
23440 ///////////////////////////////////////////////////////////////////////////////////////////
23441 ///////////////////////////////////////////////////////////////////////////////////////////
23442 
ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots()23443 ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots()
23444 	: TimeConstraint()
23445 {
23446 	this->type = CONSTRAINT_MAX_TOTAL_ACTIVITIES_FROM_SET_IN_SELECTED_TIME_SLOTS;
23447 }
23448 
ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots(double wp,QList<int> a_L,QList<int> d_L,QList<int> h_L,int max_activities)23449 ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots(double wp,
23450 	QList<int> a_L, QList<int> d_L, QList<int> h_L, int max_activities)
23451 	: TimeConstraint(wp)
23452 {
23453 	assert(d_L.count()==h_L.count());
23454 
23455 	this->activitiesIds=a_L;
23456 	this->selectedDays=d_L;
23457 	this->selectedHours=h_L;
23458 	this->maxActivities=max_activities;
23459 
23460 	this->type=CONSTRAINT_MAX_TOTAL_ACTIVITIES_FROM_SET_IN_SELECTED_TIME_SLOTS;
23461 }
23462 
computeInternalStructure(QWidget * parent,Rules & r)23463 bool ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::computeInternalStructure(QWidget* parent, Rules& r)
23464 {
23465 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
23466 	_activitiesIndices.clear();
23467 	for(int id : qAsConst(activitiesIds)){
23468 		int i=r.activitiesHash.value(id, -1);
23469 		if(i>=0)
23470 			_activitiesIndices.append(i);
23471 	}
23472 
23473 	/*this->_activitiesIndices.clear();
23474 
23475 	QSet<int> req=this->activitiesIds.toSet();
23476 	assert(req.count()==this->activitiesIds.count());
23477 
23478 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
23479 	int i;
23480 	for(i=0; i<r.nInternalActivities; i++)
23481 		if(req.contains(r.internalActivitiesList[i].id))
23482 			this->_activitiesIndices.append(i);*/
23483 
23484 	//////////////////////
23485 	assert(this->selectedDays.count()==this->selectedHours.count());
23486 
23487 	for(int k=0; k<this->selectedDays.count(); k++){
23488 		if(this->selectedDays.at(k) >= r.nDaysPerWeek){
23489 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
23490 			 tr("Constraint max total activities from set in selected time slots is wrong because it refers to removed day. Please correct"
23491 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
23492 
23493 			return false;
23494 		}
23495 		if(this->selectedHours.at(k) == r.nHoursPerDay){
23496 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
23497 			 tr("Constraint max total activities from set in selected time slots is wrong because a preferred hour is too late (after the last acceptable slot). Please correct"
23498 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
23499 
23500 			return false;
23501 		}
23502 		if(this->selectedHours.at(k) > r.nHoursPerDay){
23503 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
23504 			 tr("Constraint max total activities from set in selected time slots is wrong because it refers to removed hour. Please correct"
23505 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
23506 
23507 			return false;
23508 		}
23509 		if(this->selectedDays.at(k)<0 || this->selectedHours.at(k)<0){
23510 			TimeConstraintIrreconcilableMessage::information(parent, tr("FET information"),
23511 			 tr("Constraint max total activities from set in selected time slots is wrong because hour or day is not specified for a slot (-1). Please correct"
23512 			 " and try again. Correcting means editing the constraint and updating information. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
23513 
23514 			return false;
23515 		}
23516 	}
23517 	///////////////////////
23518 
23519 	if(this->_activitiesIndices.count()>0)
23520 		return true;
23521 	else{
23522 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
23523 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
23524 		return false;
23525 	}
23526 }
23527 
hasInactiveActivities(Rules & r)23528 bool ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::hasInactiveActivities(Rules& r)
23529 {
23530 	//returns true if all activities are inactive
23531 
23532 	for(int aid : qAsConst(this->activitiesIds))
23533 		if(!r.inactiveActivities.contains(aid))
23534 			return false;
23535 
23536 	return true;
23537 }
23538 
getXmlDescription(Rules & r)23539 QString ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::getXmlDescription(Rules& r)
23540 {
23541 	assert(this->selectedDays.count()==this->selectedHours.count());
23542 
23543 	QString s="<ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots>\n";
23544 
23545 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
23546 
23547 	s+="	<Number_of_Activities>"+QString::number(this->activitiesIds.count())+"</Number_of_Activities>\n";
23548 	for(int aid : qAsConst(this->activitiesIds))
23549 		s+="	<Activity_Id>"+CustomFETString::number(aid)+"</Activity_Id>\n";
23550 
23551 	s+="	<Number_of_Selected_Time_Slots>"+QString::number(this->selectedDays.count())+"</Number_of_Selected_Time_Slots>\n";
23552 	for(int i=0; i<this->selectedDays.count(); i++){
23553 		s+="	<Selected_Time_Slot>\n";
23554 		s+="		<Selected_Day>"+protect(r.daysOfTheWeek[this->selectedDays.at(i)])+"</Selected_Day>\n";
23555 		s+="		<Selected_Hour>"+protect(r.hoursOfTheDay[this->selectedHours.at(i)])+"</Selected_Hour>\n";
23556 		s+="	</Selected_Time_Slot>\n";
23557 	}
23558 	s+="	<Max_Total_Number_of_Activities>"+CustomFETString::number(this->maxActivities)+"</Max_Total_Number_of_Activities>\n";
23559 
23560 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
23561 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
23562 	s+="</ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots>\n";
23563 	return s;
23564 }
23565 
getDescription(Rules & r)23566 QString ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::getDescription(Rules& r)
23567 {
23568 	QString begin=QString("");
23569 	if(!active)
23570 		begin="X - ";
23571 
23572 	QString end=QString("");
23573 	if(!comments.isEmpty())
23574 		end=", "+tr("C: %1", "Comments").arg(comments);
23575 
23576 	assert(this->selectedDays.count()==this->selectedHours.count());
23577 
23578 	QString actids=QString("");
23579 	for(int aid : qAsConst(this->activitiesIds))
23580 		actids+=CustomFETString::number(aid)+QString(", ");
23581 	actids.chop(2);
23582 
23583 	QString timeslots=QString("");
23584 	for(int i=0; i<this->selectedDays.count(); i++)
23585 		timeslots+=r.daysOfTheWeek[selectedDays.at(i)]+QString(" ")+r.hoursOfTheDay[selectedHours.at(i)]+QString(", ");
23586 	timeslots.chop(2);
23587 
23588 	QString s=tr("Max total activities from set in selected time slots, WP:%1%, NA:%2, A: %3, STS: %4, MA:%5", "Constraint description. WP means weight percentage, "
23589 	 "NA means the number of activities, A means activities list, STS means selected time slots, MA means max number of activities.")
23590 	 .arg(CustomFETString::number(this->weightPercentage))
23591 	 .arg(QString::number(this->activitiesIds.count()))
23592 	 .arg(actids)
23593 	 .arg(timeslots)
23594 	 .arg(CustomFETString::number(this->maxActivities));
23595 
23596 	return begin+s+end;
23597 }
23598 
getDetailedDescription(Rules & r)23599 QString ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::getDetailedDescription(Rules& r)
23600 {
23601 	assert(this->selectedDays.count()==this->selectedHours.count());
23602 
23603 	/*QString actids=QString("");
23604 	for(int aid : qAsConst(this->activitiesIds))
23605 		actids+=CustomFETString::number(aid)+QString(", ");
23606 	actids.chop(2);*/
23607 
23608 	QString timeslots=QString("");
23609 	for(int i=0; i<this->selectedDays.count(); i++)
23610 		timeslots+=r.daysOfTheWeek[selectedDays.at(i)]+QString(" ")+r.hoursOfTheDay[selectedHours.at(i)]+QString(", ");
23611 	timeslots.chop(2);
23612 
23613 	QString s=tr("Time constraint"); s+="\n";
23614 	s+=tr("Max total activities from set in selected time slots"); s+="\n";
23615 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
23616 	s+=tr("Number of activities=%1").arg(QString::number(this->activitiesIds.count())); s+="\n";
23617 	for(int id : qAsConst(this->activitiesIds)){
23618 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
23619 		 .arg(id)
23620 		 .arg(getActivityDetailedDescription(r, id));
23621 		s+="\n";
23622 	}
23623 	s+=tr("Selected time slots: %1").arg(timeslots); s+="\n";
23624 	s+=tr("Maximum total number of activities in selected time slots=%1").arg(CustomFETString::number(this->maxActivities)); s+="\n";
23625 
23626 	if(!active){
23627 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
23628 		s+="\n";
23629 	}
23630 	if(!comments.isEmpty()){
23631 		s+=tr("Comments=%1").arg(comments);
23632 		s+="\n";
23633 	}
23634 
23635 	return s;
23636 }
23637 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)23638 double ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
23639 {
23640 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
23641 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
23642 		c.teachersMatrixReady=true;
23643 		c.subgroupsMatrixReady=true;
23644 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
23645 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
23646 
23647 		c.changedForMatrixCalculation=false;
23648 	}
23649 
23650 	int nbroken;
23651 
23652 	assert(r.internalStructureComputed);
23653 
23654 ///////////////////
23655 
23656 	QSet<int> selectedTimeSlotsSet;
23657 	for(int i=0; i<this->selectedDays.count(); i++)
23658 		selectedTimeSlotsSet.insert(selectedDays.at(i)+selectedHours.at(i)*r.nDaysPerWeek);
23659 
23660 	int cnt=0;
23661 	for(int ai : qAsConst(this->_activitiesIndices)){
23662 		if(c.times[ai]!=UNALLOCATED_TIME){
23663 			Activity* act=&r.internalActivitiesList[ai];
23664 			for(int dur=0; dur<act->duration; dur++){
23665 				if(selectedTimeSlotsSet.contains(c.times[ai]+dur*r.nDaysPerWeek)){
23666 					cnt++;
23667 					break;
23668 				}
23669 			}
23670 		}
23671 	}
23672 
23673 	nbroken=0;
23674 
23675 	if(cnt > this->maxActivities)
23676 		nbroken++;
23677 
23678 	if(nbroken>0){
23679 		if(conflictsString!=nullptr){
23680 			QString s;
23681 			s=tr("Time constraint %1 broken - this should not happen, as this kind of constraint should "
23682 			 "have only 100.0% weight. Please report error!").arg(this->getDescription(r));
23683 
23684 			dl.append(s);
23685 			cl.append(weightPercentage/100.0);
23686 
23687 			*conflictsString+= s+"\n";
23688 		}
23689 	}
23690 
23691 	if(weightPercentage==100.0)
23692 		assert(nbroken==0);
23693 	return nbroken * weightPercentage / 100.0;
23694 }
23695 
removeUseless(Rules & r)23696 void ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::removeUseless(Rules& r)
23697 {
23698 	QList<int> newActs;
23699 
23700 	for(int aid : qAsConst(activitiesIds)){
23701 		Activity* act=r.activitiesPointerHash.value(aid, nullptr);
23702 		if(act!=nullptr)
23703 			newActs.append(aid);
23704 	}
23705 
23706 	activitiesIds=newActs;
23707 }
23708 
isRelatedToActivity(Rules & r,Activity * a)23709 bool ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::isRelatedToActivity(Rules& r, Activity* a)
23710 {
23711 	Q_UNUSED(r);
23712 
23713 	return this->activitiesIds.contains(a->id);
23714 }
23715 
isRelatedToTeacher(Teacher * t)23716 bool ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::isRelatedToTeacher(Teacher* t)
23717 {
23718 	Q_UNUSED(t);
23719 
23720 	return false;
23721 }
23722 
isRelatedToSubject(Subject * s)23723 bool ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::isRelatedToSubject(Subject* s)
23724 {
23725 	Q_UNUSED(s);
23726 
23727 	return false;
23728 }
23729 
isRelatedToActivityTag(ActivityTag * s)23730 bool ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::isRelatedToActivityTag(ActivityTag* s)
23731 {
23732 	Q_UNUSED(s);
23733 
23734 	return false;
23735 }
23736 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)23737 bool ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
23738 {
23739 	Q_UNUSED(r);
23740 	Q_UNUSED(s);
23741 
23742 	return false;
23743 }
23744 
hasWrongDayOrHour(Rules & r)23745 bool ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::hasWrongDayOrHour(Rules& r)
23746 {
23747 	assert(selectedDays.count()==selectedHours.count());
23748 
23749 	for(int i=0; i<selectedDays.count(); i++)
23750 		if(selectedDays.at(i)<0 || selectedDays.at(i)>=r.nDaysPerWeek
23751 		 || selectedHours.at(i)<0 || selectedHours.at(i)>=r.nHoursPerDay)
23752 			return true;
23753 
23754 	//Do not care about minSimultaneous, which can be as high as MAX_ACTIVITIES
23755 
23756 	return false;
23757 }
23758 
canRepairWrongDayOrHour(Rules & r)23759 bool ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::canRepairWrongDayOrHour(Rules& r)
23760 {
23761 	assert(hasWrongDayOrHour(r));
23762 
23763 	return true;
23764 }
23765 
repairWrongDayOrHour(Rules & r)23766 bool ConstraintMaxTotalActivitiesFromSetInSelectedTimeSlots::repairWrongDayOrHour(Rules& r)
23767 {
23768 	assert(hasWrongDayOrHour(r));
23769 
23770 	assert(selectedDays.count()==selectedHours.count());
23771 
23772 	QList<int> newDays;
23773 	QList<int> newHours;
23774 
23775 	for(int i=0; i<selectedDays.count(); i++)
23776 		if(selectedDays.at(i)>=0 && selectedDays.at(i)<r.nDaysPerWeek
23777 		 && selectedHours.at(i)>=0 && selectedHours.at(i)<r.nHoursPerDay){
23778 			newDays.append(selectedDays.at(i));
23779 			newHours.append(selectedHours.at(i));
23780 		}
23781 
23782 	selectedDays=newDays;
23783 	selectedHours=newHours;
23784 
23785 	//Do not modify minSimultaneous, which can be as high as MAX_ACTIVITIES
23786 
23787 	r.internalStructureComputed=false;
23788 	setRulesModifiedAndOtherThings(&r);
23789 
23790 	return true;
23791 }
23792 
23793 ////////////////////////////////////////////////////////////////////////////////////////////
23794 ////////////////////////////////////////////////////////////////////////////////////////////
23795 
ConstraintActivitiesMaxInATerm()23796 ConstraintActivitiesMaxInATerm::ConstraintActivitiesMaxInATerm()
23797 	: TimeConstraint()
23798 {
23799 	this->type = CONSTRAINT_ACTIVITIES_MAX_IN_A_TERM;
23800 }
23801 
ConstraintActivitiesMaxInATerm(double wp,QList<int> a_L,int max_acts)23802 ConstraintActivitiesMaxInATerm::ConstraintActivitiesMaxInATerm(double wp,
23803 	QList<int> a_L, int max_acts)
23804 	: TimeConstraint(wp)
23805 {
23806 	this->activitiesIds=a_L;
23807 	this->maxActivitiesInATerm=max_acts;
23808 
23809 	this->type=CONSTRAINT_ACTIVITIES_MAX_IN_A_TERM;
23810 }
23811 
computeInternalStructure(QWidget * parent,Rules & r)23812 bool ConstraintActivitiesMaxInATerm::computeInternalStructure(QWidget* parent, Rules& r)
23813 {
23814 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
23815 	_activitiesIndices.clear();
23816 	for(int id : qAsConst(activitiesIds)){
23817 		int i=r.activitiesHash.value(id, -1);
23818 		if(i>=0)
23819 			_activitiesIndices.append(i);
23820 	}
23821 
23822 	/*this->_activitiesIndices.clear();
23823 
23824 	QSet<int> req=this->activitiesIds.toSet();
23825 	assert(req.count()==this->activitiesIds.count());
23826 
23827 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
23828 	int i;
23829 	for(i=0; i<r.nInternalActivities; i++)
23830 		if(req.contains(r.internalActivitiesList[i].id))
23831 			this->_activitiesIndices.append(i);*/
23832 
23833 	if(this->_activitiesIndices.count()>0)
23834 		return true;
23835 	else{
23836 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
23837 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
23838 		return false;
23839 	}
23840 }
23841 
hasInactiveActivities(Rules & r)23842 bool ConstraintActivitiesMaxInATerm::hasInactiveActivities(Rules& r)
23843 {
23844 	//returns true if all activities are inactive
23845 
23846 	for(int aid : qAsConst(this->activitiesIds))
23847 		if(!r.inactiveActivities.contains(aid))
23848 			return false;
23849 
23850 	return true;
23851 }
23852 
getXmlDescription(Rules & r)23853 QString ConstraintActivitiesMaxInATerm::getXmlDescription(Rules& r)
23854 {
23855 	Q_UNUSED(r);
23856 
23857 	QString s="<ConstraintActivitiesMaxInATerm>\n";
23858 
23859 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
23860 
23861 	s+="	<Number_of_Activities>"+QString::number(this->activitiesIds.count())+"</Number_of_Activities>\n";
23862 	for(int aid : qAsConst(this->activitiesIds))
23863 		s+="	<Activity_Id>"+CustomFETString::number(aid)+"</Activity_Id>\n";
23864 
23865 	s+="	<Max_Number_of_Activities_in_A_Term>"+CustomFETString::number(this->maxActivitiesInATerm)+"</Max_Number_of_Activities_in_A_Term>\n";
23866 
23867 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
23868 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
23869 	s+="</ConstraintActivitiesMaxInATerm>\n";
23870 	return s;
23871 }
23872 
getDescription(Rules & r)23873 QString ConstraintActivitiesMaxInATerm::getDescription(Rules& r)
23874 {
23875 	Q_UNUSED(r);
23876 
23877 	QString begin=QString("");
23878 	if(!active)
23879 		begin="X - ";
23880 
23881 	QString end=QString("");
23882 	if(!comments.isEmpty())
23883 		end=", "+tr("C: %1", "Comments").arg(comments);
23884 
23885 	QString actids=QString("");
23886 	for(int aid : qAsConst(this->activitiesIds))
23887 		actids+=CustomFETString::number(aid)+QString(", ");
23888 	actids.chop(2);
23889 
23890 	QString s=tr("Activities max in a term, WP:%1%, NA:%2, A: %3, MAIAT:%4", "Constraint description. WP means weight percentage, "
23891 	 "NA means the number of activities, A means activities list, MAIAT means max activities in a term")
23892 	 .arg(CustomFETString::number(this->weightPercentage))
23893 	 .arg(QString::number(this->activitiesIds.count()))
23894 	 .arg(actids)
23895 	 .arg(CustomFETString::number(this->maxActivitiesInATerm));
23896 
23897 	return begin+s+end;
23898 }
23899 
getDetailedDescription(Rules & r)23900 QString ConstraintActivitiesMaxInATerm::getDetailedDescription(Rules& r)
23901 {
23902 	QString actids=QString("");
23903 	for(int aid : qAsConst(this->activitiesIds))
23904 		actids+=CustomFETString::number(aid)+QString(", ");
23905 	actids.chop(2);
23906 
23907 	QString s=tr("Time constraint"); s+="\n";
23908 	s+=tr("Activities max in a term"); s+="\n";
23909 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
23910 	s+=tr("Number of activities=%1").arg(QString::number(this->activitiesIds.count())); s+="\n";
23911 	for(int id : qAsConst(this->activitiesIds)){
23912 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
23913 		 .arg(id)
23914 		 .arg(getActivityDetailedDescription(r, id));
23915 		s+="\n";
23916 	}
23917 	s+=tr("Maximum number of activities in a term=%1").arg(CustomFETString::number(this->maxActivitiesInATerm)); s+="\n";
23918 
23919 	if(!active){
23920 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
23921 		s+="\n";
23922 	}
23923 	if(!comments.isEmpty()){
23924 		s+=tr("Comments=%1").arg(comments);
23925 		s+="\n";
23926 	}
23927 
23928 	return s;
23929 }
23930 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)23931 double ConstraintActivitiesMaxInATerm::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
23932 {
23933 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
23934 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
23935 		c.teachersMatrixReady=true;
23936 		c.subgroupsMatrixReady=true;
23937 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
23938 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
23939 
23940 		c.changedForMatrixCalculation=false;
23941 	}
23942 
23943 	int nbroken;
23944 
23945 	assert(r.internalStructureComputed);
23946 
23947 	Matrix1D<int> cnt;
23948 	cnt.resize(r.nTerms);
23949 	for(int i=0; i<r.nTerms; i++)
23950 		cnt[i]=0;
23951 	for(int ai : qAsConst(this->_activitiesIndices))
23952 		if(c.times[ai]!=UNALLOCATED_TIME){
23953 			int d=c.times[ai]%r.nDaysPerWeek;
23954 			int term=d/r.nDaysPerTerm;
23955 			cnt[term]++;
23956 		}
23957 
23958 	nbroken=0;
23959 
23960 	for(int i=0; i<r.nTerms; i++)
23961 		if(cnt[i]>maxActivitiesInATerm)
23962 			nbroken++;
23963 
23964 	if(nbroken>0){
23965 		if(conflictsString!=nullptr){
23966 			QString s=tr("Time constraint %1 broken - this should not happen, as this kind of constraint should "
23967 			 "have only 100.0% weight. Please report error!").arg(this->getDescription(r));
23968 
23969 			dl.append(s);
23970 			cl.append(weightPercentage/100.0);
23971 
23972 			*conflictsString+= s+"\n";
23973 		}
23974 	}
23975 
23976 	if(weightPercentage==100.0)
23977 		assert(nbroken==0);
23978 	return nbroken * weightPercentage / 100.0;
23979 }
23980 
removeUseless(Rules & r)23981 void ConstraintActivitiesMaxInATerm::removeUseless(Rules& r)
23982 {
23983 	QList<int> newActs;
23984 
23985 	for(int aid : qAsConst(activitiesIds)){
23986 		Activity* act=r.activitiesPointerHash.value(aid, nullptr);
23987 		if(act!=nullptr)
23988 			newActs.append(aid);
23989 	}
23990 
23991 	activitiesIds=newActs;
23992 }
23993 
isRelatedToActivity(Rules & r,Activity * a)23994 bool ConstraintActivitiesMaxInATerm::isRelatedToActivity(Rules& r, Activity* a)
23995 {
23996 	Q_UNUSED(r);
23997 
23998 	return this->activitiesIds.contains(a->id);
23999 }
24000 
isRelatedToTeacher(Teacher * t)24001 bool ConstraintActivitiesMaxInATerm::isRelatedToTeacher(Teacher* t)
24002 {
24003 	Q_UNUSED(t);
24004 
24005 	return false;
24006 }
24007 
isRelatedToSubject(Subject * s)24008 bool ConstraintActivitiesMaxInATerm::isRelatedToSubject(Subject* s)
24009 {
24010 	Q_UNUSED(s);
24011 
24012 	return false;
24013 }
24014 
isRelatedToActivityTag(ActivityTag * s)24015 bool ConstraintActivitiesMaxInATerm::isRelatedToActivityTag(ActivityTag* s)
24016 {
24017 	Q_UNUSED(s);
24018 
24019 	return false;
24020 }
24021 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)24022 bool ConstraintActivitiesMaxInATerm::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
24023 {
24024 	Q_UNUSED(r);
24025 	Q_UNUSED(s);
24026 
24027 	return false;
24028 }
24029 
hasWrongDayOrHour(Rules & r)24030 bool ConstraintActivitiesMaxInATerm::hasWrongDayOrHour(Rules& r)
24031 {
24032 	Q_UNUSED(r);
24033 
24034 	return false;
24035 }
24036 
canRepairWrongDayOrHour(Rules & r)24037 bool ConstraintActivitiesMaxInATerm::canRepairWrongDayOrHour(Rules& r)
24038 {
24039 	assert(hasWrongDayOrHour(r));
24040 
24041 	return true;
24042 }
24043 
repairWrongDayOrHour(Rules & r)24044 bool ConstraintActivitiesMaxInATerm::repairWrongDayOrHour(Rules& r)
24045 {
24046 	assert(hasWrongDayOrHour(r));
24047 
24048 	return true;
24049 }
24050 
24051 ////////////////////////////////////////////////////////////////////////////////////////////
24052 ////////////////////////////////////////////////////////////////////////////////////////////
24053 
ConstraintActivitiesOccupyMaxTerms()24054 ConstraintActivitiesOccupyMaxTerms::ConstraintActivitiesOccupyMaxTerms()
24055 	: TimeConstraint()
24056 {
24057 	this->type = CONSTRAINT_ACTIVITIES_OCCUPY_MAX_TERMS;
24058 }
24059 
ConstraintActivitiesOccupyMaxTerms(double wp,QList<int> a_L,int max_occupied)24060 ConstraintActivitiesOccupyMaxTerms::ConstraintActivitiesOccupyMaxTerms(double wp,
24061 	QList<int> a_L, int max_occupied)
24062 	: TimeConstraint(wp)
24063 {
24064 	this->activitiesIds=a_L;
24065 	this->maxOccupiedTerms=max_occupied;
24066 
24067 	this->type=CONSTRAINT_ACTIVITIES_OCCUPY_MAX_TERMS;
24068 }
24069 
computeInternalStructure(QWidget * parent,Rules & r)24070 bool ConstraintActivitiesOccupyMaxTerms::computeInternalStructure(QWidget* parent, Rules& r)
24071 {
24072 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
24073 	_activitiesIndices.clear();
24074 	for(int id : qAsConst(activitiesIds)){
24075 		int i=r.activitiesHash.value(id, -1);
24076 		if(i>=0)
24077 			_activitiesIndices.append(i);
24078 	}
24079 
24080 	/*this->_activitiesIndices.clear();
24081 
24082 	QSet<int> req=this->activitiesIds.toSet();
24083 	assert(req.count()==this->activitiesIds.count());
24084 
24085 	//this cares about inactive activities, also, so do not assert this->_actIndices.count()==this->actIds.count()
24086 	int i;
24087 	for(i=0; i<r.nInternalActivities; i++)
24088 		if(req.contains(r.internalActivitiesList[i].id))
24089 			this->_activitiesIndices.append(i);*/
24090 
24091 	if(this->_activitiesIndices.count()>0)
24092 		return true;
24093 	else{
24094 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET error in data"),
24095 			tr("Following constraint is wrong (refers to no activities). Please correct it:\n%1").arg(this->getDetailedDescription(r)));
24096 		return false;
24097 	}
24098 }
24099 
hasInactiveActivities(Rules & r)24100 bool ConstraintActivitiesOccupyMaxTerms::hasInactiveActivities(Rules& r)
24101 {
24102 	//returns true if all activities are inactive
24103 
24104 	for(int aid : qAsConst(this->activitiesIds))
24105 		if(!r.inactiveActivities.contains(aid))
24106 			return false;
24107 
24108 	return true;
24109 }
24110 
getXmlDescription(Rules & r)24111 QString ConstraintActivitiesOccupyMaxTerms::getXmlDescription(Rules& r)
24112 {
24113 	Q_UNUSED(r);
24114 
24115 	QString s="<ConstraintActivitiesOccupyMaxTerms>\n";
24116 
24117 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
24118 
24119 	s+="	<Number_of_Activities>"+QString::number(this->activitiesIds.count())+"</Number_of_Activities>\n";
24120 	for(int aid : qAsConst(this->activitiesIds))
24121 		s+="	<Activity_Id>"+CustomFETString::number(aid)+"</Activity_Id>\n";
24122 
24123 	s+="	<Max_Number_of_Occupied_Terms>"+CustomFETString::number(this->maxOccupiedTerms)+"</Max_Number_of_Occupied_Terms>\n";
24124 
24125 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
24126 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
24127 	s+="</ConstraintActivitiesOccupyMaxTerms>\n";
24128 	return s;
24129 }
24130 
getDescription(Rules & r)24131 QString ConstraintActivitiesOccupyMaxTerms::getDescription(Rules& r)
24132 {
24133 	Q_UNUSED(r);
24134 
24135 	QString begin=QString("");
24136 	if(!active)
24137 		begin="X - ";
24138 
24139 	QString end=QString("");
24140 	if(!comments.isEmpty())
24141 		end=", "+tr("C: %1", "Comments").arg(comments);
24142 
24143 	QString actids=QString("");
24144 	for(int aid : qAsConst(this->activitiesIds))
24145 		actids+=CustomFETString::number(aid)+QString(", ");
24146 	actids.chop(2);
24147 
24148 	QString s=tr("Activities occupy max terms, WP:%1%, NA:%2, A: %3, MOT:%4", "Constraint description. WP means weight percentage, "
24149 	 "NA means the number of activities, A means activities list, MOT means max occupied terms")
24150 	 .arg(CustomFETString::number(this->weightPercentage))
24151 	 .arg(QString::number(this->activitiesIds.count()))
24152 	 .arg(actids)
24153 	 .arg(CustomFETString::number(this->maxOccupiedTerms));
24154 
24155 	return begin+s+end;
24156 }
24157 
getDetailedDescription(Rules & r)24158 QString ConstraintActivitiesOccupyMaxTerms::getDetailedDescription(Rules& r)
24159 {
24160 	QString actids=QString("");
24161 	for(int aid : qAsConst(this->activitiesIds))
24162 		actids+=CustomFETString::number(aid)+QString(", ");
24163 	actids.chop(2);
24164 
24165 	QString s=tr("Time constraint"); s+="\n";
24166 	s+=tr("Activities occupy max terms"); s+="\n";
24167 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
24168 	s+=tr("Number of activities=%1").arg(QString::number(this->activitiesIds.count())); s+="\n";
24169 	for(int id : qAsConst(this->activitiesIds)){
24170 		s+=tr("Activity with id=%1 (%2)", "%1 is the id, %2 is the detailed description of the activity")
24171 		 .arg(id)
24172 		 .arg(getActivityDetailedDescription(r, id));
24173 		s+="\n";
24174 	}
24175 	s+=tr("Maximum number of occupied terms=%1").arg(CustomFETString::number(this->maxOccupiedTerms)); s+="\n";
24176 
24177 	if(!active){
24178 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
24179 		s+="\n";
24180 	}
24181 	if(!comments.isEmpty()){
24182 		s+=tr("Comments=%1").arg(comments);
24183 		s+="\n";
24184 	}
24185 
24186 	return s;
24187 }
24188 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)24189 double ConstraintActivitiesOccupyMaxTerms::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
24190 {
24191 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
24192 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
24193 		c.teachersMatrixReady=true;
24194 		c.subgroupsMatrixReady=true;
24195 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
24196 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
24197 
24198 		c.changedForMatrixCalculation=false;
24199 	}
24200 
24201 	int nbroken;
24202 
24203 	assert(r.internalStructureComputed);
24204 
24205 	Matrix1D<bool> occupiedTerm;
24206 	occupiedTerm.resize(r.nTerms);
24207 	for(int i=0; i<r.nTerms; i++)
24208 		occupiedTerm[i]=false;
24209 	for(int ai : qAsConst(this->_activitiesIndices))
24210 		if(c.times[ai]!=UNALLOCATED_TIME){
24211 			int d=c.times[ai]%r.nDaysPerWeek;
24212 			int term=d/r.nDaysPerTerm;
24213 			occupiedTerm[term]=true;
24214 		}
24215 
24216 	nbroken=0;
24217 
24218 	int cnt=0;
24219 	for(int i=0; i<r.nTerms; i++)
24220 		if(occupiedTerm[i])
24221 			cnt++;
24222 
24223 	if(cnt>maxOccupiedTerms)
24224 		nbroken++;
24225 
24226 	if(nbroken>0){
24227 		if(conflictsString!=nullptr){
24228 			QString s=tr("Time constraint %1 broken - this should not happen, as this kind of constraint should "
24229 			 "have only 100.0% weight. Please report error!").arg(this->getDescription(r));
24230 
24231 			dl.append(s);
24232 			cl.append(weightPercentage/100.0);
24233 
24234 			*conflictsString+= s+"\n";
24235 		}
24236 	}
24237 
24238 	if(weightPercentage==100.0)
24239 		assert(nbroken==0);
24240 	return nbroken * weightPercentage / 100.0;
24241 }
24242 
removeUseless(Rules & r)24243 void ConstraintActivitiesOccupyMaxTerms::removeUseless(Rules& r)
24244 {
24245 	QList<int> newActs;
24246 
24247 	for(int aid : qAsConst(activitiesIds)){
24248 		Activity* act=r.activitiesPointerHash.value(aid, nullptr);
24249 		if(act!=nullptr)
24250 			newActs.append(aid);
24251 	}
24252 
24253 	activitiesIds=newActs;
24254 }
24255 
isRelatedToActivity(Rules & r,Activity * a)24256 bool ConstraintActivitiesOccupyMaxTerms::isRelatedToActivity(Rules& r, Activity* a)
24257 {
24258 	Q_UNUSED(r);
24259 
24260 	return this->activitiesIds.contains(a->id);
24261 }
24262 
isRelatedToTeacher(Teacher * t)24263 bool ConstraintActivitiesOccupyMaxTerms::isRelatedToTeacher(Teacher* t)
24264 {
24265 	Q_UNUSED(t);
24266 
24267 	return false;
24268 }
24269 
isRelatedToSubject(Subject * s)24270 bool ConstraintActivitiesOccupyMaxTerms::isRelatedToSubject(Subject* s)
24271 {
24272 	Q_UNUSED(s);
24273 
24274 	return false;
24275 }
24276 
isRelatedToActivityTag(ActivityTag * s)24277 bool ConstraintActivitiesOccupyMaxTerms::isRelatedToActivityTag(ActivityTag* s)
24278 {
24279 	Q_UNUSED(s);
24280 
24281 	return false;
24282 }
24283 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)24284 bool ConstraintActivitiesOccupyMaxTerms::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
24285 {
24286 	Q_UNUSED(r);
24287 	Q_UNUSED(s);
24288 
24289 	return false;
24290 }
24291 
hasWrongDayOrHour(Rules & r)24292 bool ConstraintActivitiesOccupyMaxTerms::hasWrongDayOrHour(Rules& r)
24293 {
24294 	Q_UNUSED(r);
24295 
24296 	return false;
24297 }
24298 
canRepairWrongDayOrHour(Rules & r)24299 bool ConstraintActivitiesOccupyMaxTerms::canRepairWrongDayOrHour(Rules& r)
24300 {
24301 	assert(hasWrongDayOrHour(r));
24302 
24303 	return true;
24304 }
24305 
repairWrongDayOrHour(Rules & r)24306 bool ConstraintActivitiesOccupyMaxTerms::repairWrongDayOrHour(Rules& r)
24307 {
24308 	assert(hasWrongDayOrHour(r));
24309 
24310 	return true;
24311 }
24312 
24313 ///////////////////////////////////////////////////////////////////////////////////////////
24314 ///////////////////////////////////////////////////////////////////////////////////////////
24315 
ConstraintStudentsSetMaxDaysPerWeek()24316 ConstraintStudentsSetMaxDaysPerWeek::ConstraintStudentsSetMaxDaysPerWeek()
24317 	: TimeConstraint()
24318 {
24319 	this->type=CONSTRAINT_STUDENTS_SET_MAX_DAYS_PER_WEEK;
24320 }
24321 
ConstraintStudentsSetMaxDaysPerWeek(double wp,int maxnd,const QString & sn)24322 ConstraintStudentsSetMaxDaysPerWeek::ConstraintStudentsSetMaxDaysPerWeek(double wp, int maxnd, const QString& sn)
24323 	 : TimeConstraint(wp)
24324 {
24325 	this->students = sn;
24326 	this->maxDaysPerWeek=maxnd;
24327 	this->type=CONSTRAINT_STUDENTS_SET_MAX_DAYS_PER_WEEK;
24328 }
24329 
computeInternalStructure(QWidget * parent,Rules & r)24330 bool ConstraintStudentsSetMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
24331 {
24332 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
24333 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
24334 
24335 	if(ss==nullptr){
24336 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
24337 		 tr("Constraint students set max days per week is wrong because it refers to inexistent students set."
24338 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
24339 
24340 		return false;
24341 	}
24342 
24343 	assert(ss!=nullptr);
24344 
24345 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
24346 	/*this->iSubgroupsList.clear();
24347 	if(ss->type==STUDENTS_SUBGROUP){
24348 		int tmp;
24349 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
24350 		assert(tmp>=0);
24351 		assert(tmp<r.nInternalSubgroups);
24352 		if(!this->iSubgroupsList.contains(tmp))
24353 			this->iSubgroupsList.append(tmp);
24354 	}
24355 	else if(ss->type==STUDENTS_GROUP){
24356 		StudentsGroup* stg=(StudentsGroup*)ss;
24357 		for(int i=0; i<stg->subgroupsList.size(); i++){
24358 			StudentsSubgroup* sts=stg->subgroupsList[i];
24359 			int tmp;
24360 			tmp=sts->indexInInternalSubgroupsList;
24361 			assert(tmp>=0);
24362 			assert(tmp<r.nInternalSubgroups);
24363 			if(!this->iSubgroupsList.contains(tmp))
24364 				this->iSubgroupsList.append(tmp);
24365 		}
24366 	}
24367 	else if(ss->type==STUDENTS_YEAR){
24368 		StudentsYear* sty=(StudentsYear*)ss;
24369 		for(int i=0; i<sty->groupsList.size(); i++){
24370 			StudentsGroup* stg=sty->groupsList[i];
24371 			for(int j=0; j<stg->subgroupsList.size(); j++){
24372 				StudentsSubgroup* sts=stg->subgroupsList[j];
24373 				int tmp;
24374 				tmp=sts->indexInInternalSubgroupsList;
24375 				assert(tmp>=0);
24376 				assert(tmp<r.nInternalSubgroups);
24377 				if(!this->iSubgroupsList.contains(tmp))
24378 					this->iSubgroupsList.append(tmp);
24379 			}
24380 		}
24381 	}
24382 	else
24383 		assert(0);*/
24384 
24385 	return true;
24386 }
24387 
hasInactiveActivities(Rules & r)24388 bool ConstraintStudentsSetMaxDaysPerWeek::hasInactiveActivities(Rules& r)
24389 {
24390 	Q_UNUSED(r);
24391 	return false;
24392 }
24393 
getXmlDescription(Rules & r)24394 QString ConstraintStudentsSetMaxDaysPerWeek::getXmlDescription(Rules& r)
24395 {
24396 	Q_UNUSED(r);
24397 
24398 	QString s="<ConstraintStudentsSetMaxDaysPerWeek>\n";
24399 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
24400 	s+="	<Students>"+protect(this->students)+"</Students>\n";
24401 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
24402 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
24403 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
24404 	s+="</ConstraintStudentsSetMaxDaysPerWeek>\n";
24405 	return s;
24406 }
24407 
getDescription(Rules & r)24408 QString ConstraintStudentsSetMaxDaysPerWeek::getDescription(Rules& r)
24409 {
24410 	Q_UNUSED(r);
24411 
24412 	QString begin=QString("");
24413 	if(!active)
24414 		begin="X - ";
24415 
24416 	QString end=QString("");
24417 	if(!comments.isEmpty())
24418 		end=", "+tr("C: %1", "Comments").arg(comments);
24419 
24420 	QString s=tr("Students set max days per week");s+=", ";
24421 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
24422 	s+=tr("St:%1", "Abbreviation for students (sets)").arg(this->students);s+=", ";
24423 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
24424 
24425 	return begin+s+end;
24426 }
24427 
getDetailedDescription(Rules & r)24428 QString ConstraintStudentsSetMaxDaysPerWeek::getDetailedDescription(Rules& r)
24429 {
24430 	Q_UNUSED(r);
24431 
24432 	QString s=tr("Time constraint");s+="\n";
24433 	s+=tr("A students set must respect the maximum number of days per week");s+="\n";
24434 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
24435 	s+=tr("Students set=%1").arg(this->students);s+="\n";
24436 
24437 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
24438 
24439 	if(!active){
24440 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
24441 		s+="\n";
24442 	}
24443 	if(!comments.isEmpty()){
24444 		s+=tr("Comments=%1").arg(comments);
24445 		s+="\n";
24446 	}
24447 
24448 	return s;
24449 }
24450 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)24451 double ConstraintStudentsSetMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
24452 {
24453 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
24454 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
24455 		c.teachersMatrixReady=true;
24456 		c.subgroupsMatrixReady=true;
24457 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
24458 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
24459 
24460 		c.changedForMatrixCalculation=false;
24461 	}
24462 
24463 	int nbroken;
24464 
24465 	nbroken=0;
24466 
24467 	Matrix1D<bool> ocDay;
24468 	ocDay.resize(r.nDaysPerWeek);
24469 	for(int sbg : qAsConst(this->iSubgroupsList)){
24470 		for(int d=0; d<r.nDaysPerWeek; d++){
24471 			ocDay[d]=false;
24472 			for(int h=0; h<r.nHoursPerDay; h++){
24473 				if(subgroupsMatrix[sbg][d][h]>0){
24474 					ocDay[d]=true;
24475 				}
24476 			}
24477 		}
24478 		int nOcDays=0;
24479 		for(int d=0; d<r.nDaysPerWeek; d++)
24480 			if(ocDay[d])
24481 				nOcDays++;
24482 		if(nOcDays > this->maxDaysPerWeek){
24483 			nbroken+=nOcDays-this->maxDaysPerWeek;
24484 
24485 			if((nOcDays-this->maxDaysPerWeek)>0){
24486 				QString s= tr("Time constraint students set max days per week broken for subgroup: %1, allowed %2 days, required %3 days.")
24487 				 .arg(r.internalSubgroupsList[sbg]->name)
24488 				 .arg(this->maxDaysPerWeek)
24489 				 .arg(nOcDays);
24490 				s+=" ";
24491 				s += tr("This increases the conflicts total by %1")
24492 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
24493 
24494 				dl.append(s);
24495 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
24496 
24497 				*conflictsString += s+"\n";
24498 			}
24499 		}
24500 	}
24501 
24502 	if(weightPercentage==100)
24503 		assert(nbroken==0);
24504 	return weightPercentage/100 * nbroken;
24505 }
24506 
isRelatedToActivity(Rules & r,Activity * a)24507 bool ConstraintStudentsSetMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
24508 {
24509 	Q_UNUSED(r);
24510 	Q_UNUSED(a);
24511 
24512 	return false;
24513 }
24514 
isRelatedToTeacher(Teacher * t)24515 bool ConstraintStudentsSetMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
24516 {
24517 	Q_UNUSED(t);
24518 	return false;
24519 }
24520 
isRelatedToSubject(Subject * s)24521 bool ConstraintStudentsSetMaxDaysPerWeek::isRelatedToSubject(Subject* s)
24522 {
24523 	Q_UNUSED(s);
24524 
24525 	return false;
24526 }
24527 
isRelatedToActivityTag(ActivityTag * s)24528 bool ConstraintStudentsSetMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
24529 {
24530 	Q_UNUSED(s);
24531 
24532 	return false;
24533 }
24534 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)24535 bool ConstraintStudentsSetMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
24536 {
24537 	return r.setsShareStudents(this->students, s->name);
24538 }
24539 
hasWrongDayOrHour(Rules & r)24540 bool ConstraintStudentsSetMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
24541 {
24542 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
24543 		return true;
24544 
24545 	return false;
24546 }
24547 
canRepairWrongDayOrHour(Rules & r)24548 bool ConstraintStudentsSetMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
24549 {
24550 	assert(hasWrongDayOrHour(r));
24551 
24552 	return true;
24553 }
24554 
repairWrongDayOrHour(Rules & r)24555 bool ConstraintStudentsSetMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
24556 {
24557 	assert(hasWrongDayOrHour(r));
24558 
24559 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
24560 		this->maxDaysPerWeek=r.nDaysPerWeek;
24561 
24562 	return true;
24563 }
24564 
24565 ///////////////////////////////////////////////////////////////////////////////////////////
24566 ///////////////////////////////////////////////////////////////////////////////////////////
24567 
ConstraintStudentsMaxDaysPerWeek()24568 ConstraintStudentsMaxDaysPerWeek::ConstraintStudentsMaxDaysPerWeek()
24569 	: TimeConstraint()
24570 {
24571 	this->type=CONSTRAINT_STUDENTS_MAX_DAYS_PER_WEEK;
24572 }
24573 
ConstraintStudentsMaxDaysPerWeek(double wp,int maxnd)24574 ConstraintStudentsMaxDaysPerWeek::ConstraintStudentsMaxDaysPerWeek(double wp, int maxnd)
24575 	 : TimeConstraint(wp)
24576 {
24577 	this->maxDaysPerWeek=maxnd;
24578 	this->type=CONSTRAINT_STUDENTS_MAX_DAYS_PER_WEEK;
24579 }
24580 
computeInternalStructure(QWidget * parent,Rules & r)24581 bool ConstraintStudentsMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
24582 {
24583 	Q_UNUSED(parent);
24584 	Q_UNUSED(r);
24585 
24586 	return true;
24587 }
24588 
hasInactiveActivities(Rules & r)24589 bool ConstraintStudentsMaxDaysPerWeek::hasInactiveActivities(Rules& r)
24590 {
24591 	Q_UNUSED(r);
24592 	return false;
24593 }
24594 
getXmlDescription(Rules & r)24595 QString ConstraintStudentsMaxDaysPerWeek::getXmlDescription(Rules& r)
24596 {
24597 	Q_UNUSED(r);
24598 
24599 	QString s="<ConstraintStudentsMaxDaysPerWeek>\n";
24600 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
24601 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
24602 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
24603 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
24604 	s+="</ConstraintStudentsMaxDaysPerWeek>\n";
24605 	return s;
24606 }
24607 
getDescription(Rules & r)24608 QString ConstraintStudentsMaxDaysPerWeek::getDescription(Rules& r)
24609 {
24610 	Q_UNUSED(r);
24611 
24612 	QString begin=QString("");
24613 	if(!active)
24614 		begin="X - ";
24615 
24616 	QString end=QString("");
24617 	if(!comments.isEmpty())
24618 		end=", "+tr("C: %1", "Comments").arg(comments);
24619 
24620 	QString s=tr("Students max days per week");s+=", ";
24621 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
24622 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
24623 
24624 	return begin+s+end;
24625 }
24626 
getDetailedDescription(Rules & r)24627 QString ConstraintStudentsMaxDaysPerWeek::getDetailedDescription(Rules& r)
24628 {
24629 	Q_UNUSED(r);
24630 
24631 	QString s=tr("Time constraint");s+="\n";
24632 	s+=tr("All students must respect the maximum number of days per week");s+="\n";
24633 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
24634 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
24635 
24636 	if(!active){
24637 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
24638 		s+="\n";
24639 	}
24640 	if(!comments.isEmpty()){
24641 		s+=tr("Comments=%1").arg(comments);
24642 		s+="\n";
24643 	}
24644 
24645 	return s;
24646 }
24647 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)24648 double ConstraintStudentsMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
24649 {
24650 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
24651 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
24652 		c.teachersMatrixReady=true;
24653 		c.subgroupsMatrixReady=true;
24654 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
24655 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
24656 
24657 		c.changedForMatrixCalculation=false;
24658 	}
24659 
24660 	int nbroken;
24661 
24662 	nbroken=0;
24663 
24664 	Matrix1D<bool> ocDay;
24665 	ocDay.resize(r.nDaysPerWeek);
24666 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
24667 		for(int d=0; d<r.nDaysPerWeek; d++){
24668 			ocDay[d]=false;
24669 			for(int h=0; h<r.nHoursPerDay; h++){
24670 				if(subgroupsMatrix[sbg][d][h]>0){
24671 					ocDay[d]=true;
24672 				}
24673 			}
24674 		}
24675 		int nOcDays=0;
24676 		for(int d=0; d<r.nDaysPerWeek; d++)
24677 			if(ocDay[d])
24678 				nOcDays++;
24679 		if(nOcDays > this->maxDaysPerWeek){
24680 			nbroken+=nOcDays-this->maxDaysPerWeek;
24681 
24682 			if((nOcDays-this->maxDaysPerWeek)>0){
24683 				QString s= tr("Time constraint students max days per week broken for subgroup: %1, allowed %2 days, required %3 days.")
24684 				 .arg(r.internalSubgroupsList[sbg]->name)
24685 				 .arg(this->maxDaysPerWeek)
24686 				 .arg(nOcDays);
24687 				s+=" ";
24688 				s += tr("This increases the conflicts total by %1")
24689 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
24690 
24691 				dl.append(s);
24692 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
24693 
24694 				*conflictsString += s+"\n";
24695 			}
24696 		}
24697 	}
24698 
24699 	if(weightPercentage==100)
24700 		assert(nbroken==0);
24701 	return weightPercentage/100 * nbroken;
24702 }
24703 
isRelatedToActivity(Rules & r,Activity * a)24704 bool ConstraintStudentsMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
24705 {
24706 	Q_UNUSED(r);
24707 	Q_UNUSED(a);
24708 
24709 	return false;
24710 }
24711 
isRelatedToTeacher(Teacher * t)24712 bool ConstraintStudentsMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
24713 {
24714 	Q_UNUSED(t);
24715 	return false;
24716 }
24717 
isRelatedToSubject(Subject * s)24718 bool ConstraintStudentsMaxDaysPerWeek::isRelatedToSubject(Subject* s)
24719 {
24720 	Q_UNUSED(s);
24721 
24722 	return false;
24723 }
24724 
isRelatedToActivityTag(ActivityTag * s)24725 bool ConstraintStudentsMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
24726 {
24727 	Q_UNUSED(s);
24728 
24729 	return false;
24730 }
24731 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)24732 bool ConstraintStudentsMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
24733 {
24734 	Q_UNUSED(r);
24735 	Q_UNUSED(s);
24736 	return true;
24737 }
24738 
hasWrongDayOrHour(Rules & r)24739 bool ConstraintStudentsMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
24740 {
24741 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
24742 		return true;
24743 
24744 	return false;
24745 }
24746 
canRepairWrongDayOrHour(Rules & r)24747 bool ConstraintStudentsMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
24748 {
24749 	assert(hasWrongDayOrHour(r));
24750 
24751 	return true;
24752 }
24753 
repairWrongDayOrHour(Rules & r)24754 bool ConstraintStudentsMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
24755 {
24756 	assert(hasWrongDayOrHour(r));
24757 
24758 	if(this->maxDaysPerWeek>r.nDaysPerWeek)
24759 		this->maxDaysPerWeek=r.nDaysPerWeek;
24760 
24761 	return true;
24762 }
24763 
24764 ///////////////////////////////////////////////////////////////////////////////////////////
24765 ///////////////////////////////////////////////////////////////////////////////////////////
24766 
ConstraintTeacherMaxSpanPerDay()24767 ConstraintTeacherMaxSpanPerDay::ConstraintTeacherMaxSpanPerDay()
24768 	: TimeConstraint()
24769 {
24770 	this->type=CONSTRAINT_TEACHER_MAX_SPAN_PER_DAY;
24771 	this->maxSpanPerDay = -1;
24772 	allowOneDayExceptionPlusOne=false;
24773 }
24774 
ConstraintTeacherMaxSpanPerDay(double wp,int maxspan,bool except,const QString & teacher)24775 ConstraintTeacherMaxSpanPerDay::ConstraintTeacherMaxSpanPerDay(double wp, int maxspan, bool except, const QString& teacher)
24776  : TimeConstraint(wp)
24777  {
24778 	assert(maxspan>0);
24779 	this->maxSpanPerDay=maxspan;
24780 	this->teacherName=teacher;
24781 
24782 	allowOneDayExceptionPlusOne=except;
24783 
24784 	this->type=CONSTRAINT_TEACHER_MAX_SPAN_PER_DAY;
24785 }
24786 
computeInternalStructure(QWidget * parent,Rules & r)24787 bool ConstraintTeacherMaxSpanPerDay::computeInternalStructure(QWidget* parent, Rules& r)
24788 {
24789 	Q_UNUSED(parent);
24790 
24791 	//this->teacher_ID=r.searchTeacher(this->teacherName);
24792 	teacher_ID=r.teachersHash.value(teacherName, -1);
24793 	assert(this->teacher_ID>=0);
24794 	return true;
24795 }
24796 
hasInactiveActivities(Rules & r)24797 bool ConstraintTeacherMaxSpanPerDay::hasInactiveActivities(Rules& r)
24798 {
24799 	Q_UNUSED(r);
24800 	return false;
24801 }
24802 
getXmlDescription(Rules & r)24803 QString ConstraintTeacherMaxSpanPerDay::getXmlDescription(Rules& r)
24804 {
24805 	Q_UNUSED(r);
24806 
24807 	QString s="<ConstraintTeacherMaxSpanPerDay>\n";
24808 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
24809 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
24810 	s+="	<Max_Span>"+CustomFETString::number(this->maxSpanPerDay)+"</Max_Span>\n";
24811 	s+="	<Allow_One_Day_Exception_of_Plus_One>"+trueFalse(allowOneDayExceptionPlusOne)+"</Allow_One_Day_Exception_of_Plus_One>\n";
24812 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
24813 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
24814 	s+="</ConstraintTeacherMaxSpanPerDay>\n";
24815 	return s;
24816 }
24817 
getDescription(Rules & r)24818 QString ConstraintTeacherMaxSpanPerDay::getDescription(Rules& r)
24819 {
24820 	Q_UNUSED(r);
24821 
24822 	QString begin=QString("");
24823 	if(!active)
24824 		begin="X - ";
24825 
24826 	QString end=QString("");
24827 	if(!comments.isEmpty())
24828 		end=", "+tr("C: %1", "Comments").arg(comments);
24829 
24830 	QString s;
24831 	s+=tr("Teacher max span per day");s+=", ";
24832 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
24833 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
24834 	s+=tr("MS:%1", "Maximum span (in hours, per day)").arg(this->maxSpanPerDay);s+=", ";
24835 	s+=tr("ODE:%1", "One day exception (in which the teacher can have span+1)").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));
24836 
24837 	return begin+s+end;
24838 }
24839 
getDetailedDescription(Rules & r)24840 QString ConstraintTeacherMaxSpanPerDay::getDetailedDescription(Rules& r)
24841 {
24842 	Q_UNUSED(r);
24843 
24844 	QString s=tr("Time constraint");s+="\n";
24845 	s+=tr("A teacher must respect the maximum number of span (in hours) per day");s+="\n";
24846 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
24847 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
24848 	s+=tr("Maximum span per day=%1").arg(this->maxSpanPerDay);s+="\n";
24849 	s+=tr("Allow one day exception of plus one=%1").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));s+="\n";
24850 
24851 	if(!active){
24852 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
24853 		s+="\n";
24854 	}
24855 	if(!comments.isEmpty()){
24856 		s+=tr("Comments=%1").arg(comments);
24857 		s+="\n";
24858 	}
24859 
24860 	return s;
24861 }
24862 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)24863 double ConstraintTeacherMaxSpanPerDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
24864 {
24865 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
24866 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
24867 		c.teachersMatrixReady=true;
24868 		c.subgroupsMatrixReady=true;
24869 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
24870 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
24871 
24872 		c.changedForMatrixCalculation=false;
24873 	}
24874 
24875 	Q_UNUSED(cl);
24876 	Q_UNUSED(dl);
24877 	Q_UNUSED(conflictsString);
24878 
24879 	assert(this->weightPercentage==100.0);
24880 
24881 	int nbroken=0;
24882 
24883 	bool except;
24884 	if(allowOneDayExceptionPlusOne)
24885 		except=true;
24886 	else
24887 		except=false;
24888 
24889 	for(int d=0; d<r.nDaysPerWeek; d++){
24890 		int begin=-1;
24891 		int end=-1;
24892 		for(int h=0; h<r.nHoursPerDay; h++)
24893 			if(teachersMatrix[this->teacher_ID][d][h]>0){
24894 				begin=h;
24895 				break;
24896 			}
24897 		for(int h=r.nHoursPerDay-1; h>=0; h--)
24898 			if(teachersMatrix[this->teacher_ID][d][h]>0){
24899 				end=h;
24900 				break;
24901 			}
24902 		if(end>=0 && begin>=0 && end>=begin){
24903 			int span=end-begin+1;
24904 			if(span>this->maxSpanPerDay){
24905 				if(except && span==maxSpanPerDay+1)
24906 					except=false;
24907 				else
24908 					nbroken++;
24909 			}
24910 		}
24911 	}
24912 
24913 	assert(nbroken==0);
24914 
24915 	return nbroken;
24916 }
24917 
isRelatedToActivity(Rules & r,Activity * a)24918 bool ConstraintTeacherMaxSpanPerDay::isRelatedToActivity(Rules& r, Activity* a)
24919 {
24920 	Q_UNUSED(r);
24921 	Q_UNUSED(a);
24922 
24923 	return false;
24924 }
24925 
isRelatedToTeacher(Teacher * t)24926 bool ConstraintTeacherMaxSpanPerDay::isRelatedToTeacher(Teacher* t)
24927 {
24928 	if(this->teacherName==t->name)
24929 		return true;
24930 	return false;
24931 }
24932 
isRelatedToSubject(Subject * s)24933 bool ConstraintTeacherMaxSpanPerDay::isRelatedToSubject(Subject* s)
24934 {
24935 	Q_UNUSED(s);
24936 
24937 	return false;
24938 }
24939 
isRelatedToActivityTag(ActivityTag * s)24940 bool ConstraintTeacherMaxSpanPerDay::isRelatedToActivityTag(ActivityTag* s)
24941 {
24942 	Q_UNUSED(s);
24943 
24944 	return false;
24945 }
24946 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)24947 bool ConstraintTeacherMaxSpanPerDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
24948 {
24949 	Q_UNUSED(r);
24950 	Q_UNUSED(s);
24951 
24952 	return false;
24953 }
24954 
hasWrongDayOrHour(Rules & r)24955 bool ConstraintTeacherMaxSpanPerDay::hasWrongDayOrHour(Rules& r)
24956 {
24957 	if(maxSpanPerDay>r.nHoursPerDay)
24958 		return true;
24959 
24960 	return false;
24961 }
24962 
canRepairWrongDayOrHour(Rules & r)24963 bool ConstraintTeacherMaxSpanPerDay::canRepairWrongDayOrHour(Rules& r)
24964 {
24965 	assert(hasWrongDayOrHour(r));
24966 
24967 	return true;
24968 }
24969 
repairWrongDayOrHour(Rules & r)24970 bool ConstraintTeacherMaxSpanPerDay::repairWrongDayOrHour(Rules& r)
24971 {
24972 	assert(hasWrongDayOrHour(r));
24973 
24974 	if(maxSpanPerDay>r.nHoursPerDay)
24975 		maxSpanPerDay=r.nHoursPerDay;
24976 
24977 	return true;
24978 }
24979 
24980 ///////////////////////////////////////////////////////////////////////////////////////////
24981 ///////////////////////////////////////////////////////////////////////////////////////////
24982 
ConstraintTeachersMaxSpanPerDay()24983 ConstraintTeachersMaxSpanPerDay::ConstraintTeachersMaxSpanPerDay()
24984 	: TimeConstraint()
24985 {
24986 	this->type=CONSTRAINT_TEACHERS_MAX_SPAN_PER_DAY;
24987 	this->maxSpanPerDay = -1;
24988 	allowOneDayExceptionPlusOne=false;
24989 }
24990 
ConstraintTeachersMaxSpanPerDay(double wp,int maxspan,bool except)24991 ConstraintTeachersMaxSpanPerDay::ConstraintTeachersMaxSpanPerDay(double wp, int maxspan, bool except)
24992  : TimeConstraint(wp)
24993  {
24994 	assert(maxspan>0);
24995 	this->maxSpanPerDay=maxspan;
24996 
24997 	allowOneDayExceptionPlusOne=except;
24998 
24999 	this->type=CONSTRAINT_TEACHERS_MAX_SPAN_PER_DAY;
25000 }
25001 
computeInternalStructure(QWidget * parent,Rules & r)25002 bool ConstraintTeachersMaxSpanPerDay::computeInternalStructure(QWidget* parent, Rules& r)
25003 {
25004 	Q_UNUSED(parent);
25005 	Q_UNUSED(r);
25006 
25007 	return true;
25008 }
25009 
hasInactiveActivities(Rules & r)25010 bool ConstraintTeachersMaxSpanPerDay::hasInactiveActivities(Rules& r)
25011 {
25012 	Q_UNUSED(r);
25013 	return false;
25014 }
25015 
getXmlDescription(Rules & r)25016 QString ConstraintTeachersMaxSpanPerDay::getXmlDescription(Rules& r)
25017 {
25018 	Q_UNUSED(r);
25019 
25020 	QString s="<ConstraintTeachersMaxSpanPerDay>\n";
25021 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
25022 	s+="	<Max_Span>"+CustomFETString::number(this->maxSpanPerDay)+"</Max_Span>\n";
25023 	s+="	<Allow_One_Day_Exception_of_Plus_One>"+trueFalse(allowOneDayExceptionPlusOne)+"</Allow_One_Day_Exception_of_Plus_One>\n";
25024 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
25025 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
25026 	s+="</ConstraintTeachersMaxSpanPerDay>\n";
25027 	return s;
25028 }
25029 
getDescription(Rules & r)25030 QString ConstraintTeachersMaxSpanPerDay::getDescription(Rules& r)
25031 {
25032 	Q_UNUSED(r);
25033 
25034 	QString begin=QString("");
25035 	if(!active)
25036 		begin="X - ";
25037 
25038 	QString end=QString("");
25039 	if(!comments.isEmpty())
25040 		end=", "+tr("C: %1", "Comments").arg(comments);
25041 
25042 	QString s;
25043 	s+=tr("Teachers max span per day");s+=", ";
25044 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
25045 	s+=tr("MS:%1", "Maximum span (in hours, per day)").arg(this->maxSpanPerDay);s+=", ";
25046 	s+=tr("ODE:%1", "One day exception (in which the teachers can have span+1)").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));
25047 
25048 	return begin+s+end;
25049 }
25050 
getDetailedDescription(Rules & r)25051 QString ConstraintTeachersMaxSpanPerDay::getDetailedDescription(Rules& r)
25052 {
25053 	Q_UNUSED(r);
25054 
25055 	QString s=tr("Time constraint");s+="\n";
25056 	s+=tr("All teachers must respect the maximum number of span (in hours) per day");s+="\n";
25057 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
25058 	s+=tr("Maximum span per day=%1").arg(this->maxSpanPerDay);s+="\n";
25059 	s+=tr("Allow one day exception of plus one=%1").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));s+="\n";
25060 
25061 	if(!active){
25062 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
25063 		s+="\n";
25064 	}
25065 	if(!comments.isEmpty()){
25066 		s+=tr("Comments=%1").arg(comments);
25067 		s+="\n";
25068 	}
25069 
25070 	return s;
25071 }
25072 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)25073 double ConstraintTeachersMaxSpanPerDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
25074 {
25075 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
25076 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
25077 		c.teachersMatrixReady=true;
25078 		c.subgroupsMatrixReady=true;
25079 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
25080 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
25081 
25082 		c.changedForMatrixCalculation=false;
25083 	}
25084 
25085 	Q_UNUSED(cl);
25086 	Q_UNUSED(dl);
25087 	Q_UNUSED(conflictsString);
25088 
25089 	assert(this->weightPercentage==100.0);
25090 
25091 	int nbroken=0;
25092 
25093 	for(int tch=0; tch<r.nInternalTeachers; tch++){
25094 		bool except;
25095 		if(allowOneDayExceptionPlusOne)
25096 			except=true;
25097 		else
25098 			except=false;
25099 
25100 		for(int d=0; d<r.nDaysPerWeek; d++){
25101 			int begin=-1;
25102 			int end=-1;
25103 			for(int h=0; h<r.nHoursPerDay; h++)
25104 				if(teachersMatrix[tch][d][h]>0){
25105 					begin=h;
25106 					break;
25107 				}
25108 			for(int h=r.nHoursPerDay-1; h>=0; h--)
25109 				if(teachersMatrix[tch][d][h]>0){
25110 					end=h;
25111 					break;
25112 				}
25113 			if(end>=0 && begin>=0 && end>=begin){
25114 				int span=end-begin+1;
25115 				if(span>this->maxSpanPerDay){
25116 					if(except && span==maxSpanPerDay+1)
25117 						except=false;
25118 					else
25119 						nbroken++;
25120 				}
25121 			}
25122 		}
25123 	}
25124 
25125 	assert(nbroken==0);
25126 
25127 	return nbroken;
25128 }
25129 
isRelatedToActivity(Rules & r,Activity * a)25130 bool ConstraintTeachersMaxSpanPerDay::isRelatedToActivity(Rules& r, Activity* a)
25131 {
25132 	Q_UNUSED(r);
25133 	Q_UNUSED(a);
25134 
25135 	return false;
25136 }
25137 
isRelatedToTeacher(Teacher * t)25138 bool ConstraintTeachersMaxSpanPerDay::isRelatedToTeacher(Teacher* t)
25139 {
25140 	Q_UNUSED(t);
25141 
25142 	return true;
25143 }
25144 
isRelatedToSubject(Subject * s)25145 bool ConstraintTeachersMaxSpanPerDay::isRelatedToSubject(Subject* s)
25146 {
25147 	Q_UNUSED(s);
25148 
25149 	return false;
25150 }
25151 
isRelatedToActivityTag(ActivityTag * s)25152 bool ConstraintTeachersMaxSpanPerDay::isRelatedToActivityTag(ActivityTag* s)
25153 {
25154 	Q_UNUSED(s);
25155 
25156 	return false;
25157 }
25158 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)25159 bool ConstraintTeachersMaxSpanPerDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
25160 {
25161 	Q_UNUSED(r);
25162 	Q_UNUSED(s);
25163 
25164 	return false;
25165 }
25166 
hasWrongDayOrHour(Rules & r)25167 bool ConstraintTeachersMaxSpanPerDay::hasWrongDayOrHour(Rules& r)
25168 {
25169 	if(maxSpanPerDay>r.nHoursPerDay)
25170 		return true;
25171 
25172 	return false;
25173 }
25174 
canRepairWrongDayOrHour(Rules & r)25175 bool ConstraintTeachersMaxSpanPerDay::canRepairWrongDayOrHour(Rules& r)
25176 {
25177 	assert(hasWrongDayOrHour(r));
25178 
25179 	return true;
25180 }
25181 
repairWrongDayOrHour(Rules & r)25182 bool ConstraintTeachersMaxSpanPerDay::repairWrongDayOrHour(Rules& r)
25183 {
25184 	assert(hasWrongDayOrHour(r));
25185 
25186 	if(maxSpanPerDay>r.nHoursPerDay)
25187 		maxSpanPerDay=r.nHoursPerDay;
25188 
25189 	return true;
25190 }
25191 
25192 ////////////////////////////////////////////////////////////////////////////////////////////
25193 ////////////////////////////////////////////////////////////////////////////////////////////
25194 
ConstraintStudentsSetMaxSpanPerDay()25195 ConstraintStudentsSetMaxSpanPerDay::ConstraintStudentsSetMaxSpanPerDay()
25196 	: TimeConstraint()
25197 {
25198 	this->type = CONSTRAINT_STUDENTS_SET_MAX_SPAN_PER_DAY;
25199 	this->maxSpanPerDay = -1;
25200 }
25201 
ConstraintStudentsSetMaxSpanPerDay(double wp,int maxspan,const QString & sn)25202 ConstraintStudentsSetMaxSpanPerDay::ConstraintStudentsSetMaxSpanPerDay(double wp, int maxspan, const QString& sn)
25203 	: TimeConstraint(wp)
25204 {
25205 	this->maxSpanPerDay = maxspan;
25206 	this->students = sn;
25207 	this->type = CONSTRAINT_STUDENTS_SET_MAX_SPAN_PER_DAY;
25208 }
25209 
hasInactiveActivities(Rules & r)25210 bool ConstraintStudentsSetMaxSpanPerDay::hasInactiveActivities(Rules& r)
25211 {
25212 	Q_UNUSED(r);
25213 	return false;
25214 }
25215 
getXmlDescription(Rules & r)25216 QString ConstraintStudentsSetMaxSpanPerDay::getXmlDescription(Rules& r)
25217 {
25218 	Q_UNUSED(r);
25219 
25220 	QString s="<ConstraintStudentsSetMaxSpanPerDay>\n";
25221 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
25222 	s+="	<Max_Span>"+CustomFETString::number(this->maxSpanPerDay)+"</Max_Span>\n";
25223 	s+="	<Students>"+protect(this->students)+"</Students>\n";
25224 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
25225 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
25226 	s+="</ConstraintStudentsSetMaxSpanPerDay>\n";
25227 	return s;
25228 }
25229 
getDescription(Rules & r)25230 QString ConstraintStudentsSetMaxSpanPerDay::getDescription(Rules& r)
25231 {
25232 	Q_UNUSED(r);
25233 
25234 	QString begin=QString("");
25235 	if(!active)
25236 		begin="X - ";
25237 
25238 	QString end=QString("");
25239 	if(!comments.isEmpty())
25240 		end=", "+tr("C: %1", "Comments").arg(comments);
25241 
25242 	QString s;
25243 	s+=tr("Students set max span per day");s+=", ";
25244 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
25245 	s+=tr("St:%1", "Students (set)").arg(this->students); s+=", ";
25246 	s+=tr("MS:%1", "Max span (in hours, per day)").arg(this->maxSpanPerDay);
25247 
25248 	return begin+s+end;
25249 }
25250 
getDetailedDescription(Rules & r)25251 QString ConstraintStudentsSetMaxSpanPerDay::getDetailedDescription(Rules& r)
25252 {
25253 	Q_UNUSED(r);
25254 
25255 	QString s=tr("Time constraint");s+="\n";
25256 	s+=tr("A students set must respect the maximum number of span (in hours) per day");s+="\n";
25257 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
25258 	s+=tr("Students set=%1").arg(this->students);s+="\n";
25259 	s+=tr("Maximum span per day=%1").arg(this->maxSpanPerDay);s+="\n";
25260 
25261 	if(!active){
25262 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
25263 		s+="\n";
25264 	}
25265 	if(!comments.isEmpty()){
25266 		s+=tr("Comments=%1").arg(comments);
25267 		s+="\n";
25268 	}
25269 
25270 	return s;
25271 }
25272 
computeInternalStructure(QWidget * parent,Rules & r)25273 bool ConstraintStudentsSetMaxSpanPerDay::computeInternalStructure(QWidget* parent, Rules& r)
25274 {
25275 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
25276 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
25277 
25278 	if(ss==nullptr){
25279 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
25280 		 tr("Constraint students set max span per day is wrong because it refers to inexistent students set."
25281 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
25282 
25283 		return false;
25284 	}
25285 
25286 	assert(ss!=nullptr);
25287 
25288 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
25289 	/*this->iSubgroupsList.clear();
25290 	if(ss->type==STUDENTS_SUBGROUP){
25291 		int tmp;
25292 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
25293 		assert(tmp>=0);
25294 		assert(tmp<r.nInternalSubgroups);
25295 		if(!this->iSubgroupsList.contains(tmp))
25296 			this->iSubgroupsList.append(tmp);
25297 	}
25298 	else if(ss->type==STUDENTS_GROUP){
25299 		StudentsGroup* stg=(StudentsGroup*)ss;
25300 		for(int i=0; i<stg->subgroupsList.size(); i++){
25301 			StudentsSubgroup* sts=stg->subgroupsList[i];
25302 			int tmp;
25303 			tmp=sts->indexInInternalSubgroupsList;
25304 			assert(tmp>=0);
25305 			assert(tmp<r.nInternalSubgroups);
25306 			if(!this->iSubgroupsList.contains(tmp))
25307 				this->iSubgroupsList.append(tmp);
25308 		}
25309 	}
25310 	else if(ss->type==STUDENTS_YEAR){
25311 		StudentsYear* sty=(StudentsYear*)ss;
25312 		for(int i=0; i<sty->groupsList.size(); i++){
25313 			StudentsGroup* stg=sty->groupsList[i];
25314 			for(int j=0; j<stg->subgroupsList.size(); j++){
25315 				StudentsSubgroup* sts=stg->subgroupsList[j];
25316 				int tmp;
25317 				tmp=sts->indexInInternalSubgroupsList;
25318 				assert(tmp>=0);
25319 				assert(tmp<r.nInternalSubgroups);
25320 				if(!this->iSubgroupsList.contains(tmp))
25321 					this->iSubgroupsList.append(tmp);
25322 			}
25323 		}
25324 	}
25325 	else
25326 		assert(0);*/
25327 
25328 	return true;
25329 }
25330 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)25331 double ConstraintStudentsSetMaxSpanPerDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
25332 {
25333 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
25334 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
25335 		c.teachersMatrixReady=true;
25336 		c.subgroupsMatrixReady=true;
25337 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
25338 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
25339 
25340 		c.changedForMatrixCalculation=false;
25341 	}
25342 
25343 	Q_UNUSED(cl);
25344 	Q_UNUSED(dl);
25345 	Q_UNUSED(conflictsString);
25346 
25347 	assert(this->weightPercentage==100.0);
25348 
25349 	int nbroken=0;
25350 
25351 	for(int sbg : qAsConst(this->iSubgroupsList)){
25352 		for(int d=0; d<r.nDaysPerWeek; d++){
25353 			int begin=-1;
25354 			int end=-1;
25355 			for(int h=0; h<r.nHoursPerDay; h++)
25356 				if(subgroupsMatrix[sbg][d][h]>0){
25357 					begin=h;
25358 					break;
25359 				}
25360 			for(int h=r.nHoursPerDay-1; h>=0; h--)
25361 				if(subgroupsMatrix[sbg][d][h]>0){
25362 					end=h;
25363 					break;
25364 				}
25365 			if(end>=0 && begin>=0 && end>=begin){
25366 				int span=end-begin+1;
25367 				if(span>this->maxSpanPerDay)
25368 					nbroken++;
25369 			}
25370 		}
25371 	}
25372 
25373 	assert(nbroken==0);
25374 
25375 	return nbroken;
25376 }
25377 
isRelatedToActivity(Rules & r,Activity * a)25378 bool ConstraintStudentsSetMaxSpanPerDay::isRelatedToActivity(Rules& r, Activity* a)
25379 {
25380 	Q_UNUSED(r);
25381 	Q_UNUSED(a);
25382 
25383 	return false;
25384 }
25385 
isRelatedToTeacher(Teacher * t)25386 bool ConstraintStudentsSetMaxSpanPerDay::isRelatedToTeacher(Teacher* t)
25387 {
25388 	Q_UNUSED(t);
25389 
25390 	return false;
25391 }
25392 
isRelatedToSubject(Subject * s)25393 bool ConstraintStudentsSetMaxSpanPerDay::isRelatedToSubject(Subject* s)
25394 {
25395 	Q_UNUSED(s);
25396 
25397 	return false;
25398 }
25399 
isRelatedToActivityTag(ActivityTag * s)25400 bool ConstraintStudentsSetMaxSpanPerDay::isRelatedToActivityTag(ActivityTag* s)
25401 {
25402 	Q_UNUSED(s);
25403 
25404 	return false;
25405 }
25406 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)25407 bool ConstraintStudentsSetMaxSpanPerDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
25408 {
25409 	return r.setsShareStudents(this->students, s->name);
25410 }
25411 
hasWrongDayOrHour(Rules & r)25412 bool ConstraintStudentsSetMaxSpanPerDay::hasWrongDayOrHour(Rules& r)
25413 {
25414 	if(maxSpanPerDay>r.nHoursPerDay)
25415 		return true;
25416 
25417 	return false;
25418 }
25419 
canRepairWrongDayOrHour(Rules & r)25420 bool ConstraintStudentsSetMaxSpanPerDay::canRepairWrongDayOrHour(Rules& r)
25421 {
25422 	assert(hasWrongDayOrHour(r));
25423 
25424 	return true;
25425 }
25426 
repairWrongDayOrHour(Rules & r)25427 bool ConstraintStudentsSetMaxSpanPerDay::repairWrongDayOrHour(Rules& r)
25428 {
25429 	assert(hasWrongDayOrHour(r));
25430 
25431 	if(maxSpanPerDay>r.nHoursPerDay)
25432 		maxSpanPerDay=r.nHoursPerDay;
25433 
25434 	return true;
25435 }
25436 
25437 ////////////////////////////////////////////////////////////////////////////////////////////
25438 ////////////////////////////////////////////////////////////////////////////////////////////
25439 
ConstraintStudentsMaxSpanPerDay()25440 ConstraintStudentsMaxSpanPerDay::ConstraintStudentsMaxSpanPerDay()
25441 	: TimeConstraint()
25442 {
25443 	this->type = CONSTRAINT_STUDENTS_MAX_SPAN_PER_DAY;
25444 	this->maxSpanPerDay = -1;
25445 }
25446 
ConstraintStudentsMaxSpanPerDay(double wp,int maxspan)25447 ConstraintStudentsMaxSpanPerDay::ConstraintStudentsMaxSpanPerDay(double wp, int maxspan)
25448 	: TimeConstraint(wp)
25449 {
25450 	this->maxSpanPerDay = maxspan;
25451 	this->type = CONSTRAINT_STUDENTS_MAX_SPAN_PER_DAY;
25452 }
25453 
hasInactiveActivities(Rules & r)25454 bool ConstraintStudentsMaxSpanPerDay::hasInactiveActivities(Rules& r)
25455 {
25456 	Q_UNUSED(r);
25457 	return false;
25458 }
25459 
getXmlDescription(Rules & r)25460 QString ConstraintStudentsMaxSpanPerDay::getXmlDescription(Rules& r)
25461 {
25462 	Q_UNUSED(r);
25463 
25464 	QString s="<ConstraintStudentsMaxSpanPerDay>\n";
25465 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
25466 	s+="	<Max_Span>"+CustomFETString::number(this->maxSpanPerDay)+"</Max_Span>\n";
25467 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
25468 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
25469 	s+="</ConstraintStudentsMaxSpanPerDay>\n";
25470 	return s;
25471 }
25472 
getDescription(Rules & r)25473 QString ConstraintStudentsMaxSpanPerDay::getDescription(Rules& r)
25474 {
25475 	Q_UNUSED(r);
25476 
25477 	QString begin=QString("");
25478 	if(!active)
25479 		begin="X - ";
25480 
25481 	QString end=QString("");
25482 	if(!comments.isEmpty())
25483 		end=", "+tr("C: %1", "Comments").arg(comments);
25484 
25485 	QString s;
25486 	s+=tr("Students max span per day");s+=", ";
25487 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
25488 	s+=tr("MS:%1", "Max span (in hours, per day)").arg(this->maxSpanPerDay);
25489 
25490 	return begin+s+end;
25491 }
25492 
getDetailedDescription(Rules & r)25493 QString ConstraintStudentsMaxSpanPerDay::getDetailedDescription(Rules& r)
25494 {
25495 	Q_UNUSED(r);
25496 
25497 	QString s=tr("Time constraint");s+="\n";
25498 	s+=tr("All students must respect the maximum number of span (in hours) per day");s+="\n";
25499 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
25500 	s+=tr("Maximum span per day=%1").arg(this->maxSpanPerDay);s+="\n";
25501 
25502 	if(!active){
25503 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
25504 		s+="\n";
25505 	}
25506 	if(!comments.isEmpty()){
25507 		s+=tr("Comments=%1").arg(comments);
25508 		s+="\n";
25509 	}
25510 
25511 	return s;
25512 }
25513 
computeInternalStructure(QWidget * parent,Rules & r)25514 bool ConstraintStudentsMaxSpanPerDay::computeInternalStructure(QWidget* parent, Rules& r)
25515 {
25516 	Q_UNUSED(parent);
25517 	Q_UNUSED(r);
25518 
25519 	return true;
25520 }
25521 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)25522 double ConstraintStudentsMaxSpanPerDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
25523 {
25524 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
25525 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
25526 		c.teachersMatrixReady=true;
25527 		c.subgroupsMatrixReady=true;
25528 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
25529 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
25530 
25531 		c.changedForMatrixCalculation=false;
25532 	}
25533 
25534 	Q_UNUSED(cl);
25535 	Q_UNUSED(dl);
25536 	Q_UNUSED(conflictsString);
25537 
25538 	assert(this->weightPercentage==100.0);
25539 
25540 	int nbroken=0;
25541 
25542 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
25543 		for(int d=0; d<r.nDaysPerWeek; d++){
25544 			int begin=-1;
25545 			int end=-1;
25546 			for(int h=0; h<r.nHoursPerDay; h++)
25547 				if(subgroupsMatrix[sbg][d][h]>0){
25548 					begin=h;
25549 					break;
25550 				}
25551 			for(int h=r.nHoursPerDay-1; h>=0; h--)
25552 				if(subgroupsMatrix[sbg][d][h]>0){
25553 					end=h;
25554 					break;
25555 				}
25556 			if(end>=0 && begin>=0 && end>=begin){
25557 				int span=end-begin+1;
25558 				if(span>this->maxSpanPerDay)
25559 					nbroken++;
25560 			}
25561 		}
25562 	}
25563 
25564 	assert(nbroken==0);
25565 
25566 	return nbroken;
25567 }
25568 
isRelatedToActivity(Rules & r,Activity * a)25569 bool ConstraintStudentsMaxSpanPerDay::isRelatedToActivity(Rules& r, Activity* a)
25570 {
25571 	Q_UNUSED(r);
25572 	Q_UNUSED(a);
25573 
25574 	return false;
25575 }
25576 
isRelatedToTeacher(Teacher * t)25577 bool ConstraintStudentsMaxSpanPerDay::isRelatedToTeacher(Teacher* t)
25578 {
25579 	Q_UNUSED(t);
25580 
25581 	return false;
25582 }
25583 
isRelatedToSubject(Subject * s)25584 bool ConstraintStudentsMaxSpanPerDay::isRelatedToSubject(Subject* s)
25585 {
25586 	Q_UNUSED(s);
25587 
25588 	return false;
25589 }
25590 
isRelatedToActivityTag(ActivityTag * s)25591 bool ConstraintStudentsMaxSpanPerDay::isRelatedToActivityTag(ActivityTag* s)
25592 {
25593 	Q_UNUSED(s);
25594 
25595 	return false;
25596 }
25597 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)25598 bool ConstraintStudentsMaxSpanPerDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
25599 {
25600 	Q_UNUSED(r);
25601 	Q_UNUSED(s);
25602 
25603 	return true;
25604 }
25605 
hasWrongDayOrHour(Rules & r)25606 bool ConstraintStudentsMaxSpanPerDay::hasWrongDayOrHour(Rules& r)
25607 {
25608 	if(maxSpanPerDay>r.nHoursPerDay)
25609 		return true;
25610 
25611 	return false;
25612 }
25613 
canRepairWrongDayOrHour(Rules & r)25614 bool ConstraintStudentsMaxSpanPerDay::canRepairWrongDayOrHour(Rules& r)
25615 {
25616 	assert(hasWrongDayOrHour(r));
25617 
25618 	return true;
25619 }
25620 
repairWrongDayOrHour(Rules & r)25621 bool ConstraintStudentsMaxSpanPerDay::repairWrongDayOrHour(Rules& r)
25622 {
25623 	assert(hasWrongDayOrHour(r));
25624 
25625 	if(maxSpanPerDay>r.nHoursPerDay)
25626 		maxSpanPerDay=r.nHoursPerDay;
25627 
25628 	return true;
25629 }
25630 
25631 ///////////////////////////////////////////////////////////////////////////////////////////
25632 ///////////////////////////////////////////////////////////////////////////////////////////
25633 
ConstraintTeacherMinRestingHours()25634 ConstraintTeacherMinRestingHours::ConstraintTeacherMinRestingHours()
25635 	: TimeConstraint()
25636 {
25637 	this->type=CONSTRAINT_TEACHER_MIN_RESTING_HOURS;
25638 	this->minRestingHours=-1;
25639 	this->circular=true;
25640 }
25641 
ConstraintTeacherMinRestingHours(double wp,int minrestinghours,bool circ,const QString & teacher)25642 ConstraintTeacherMinRestingHours::ConstraintTeacherMinRestingHours(double wp, int minrestinghours, bool circ, const QString& teacher)
25643  : TimeConstraint(wp)
25644  {
25645 	assert(minrestinghours>0);
25646 	this->minRestingHours=minrestinghours;
25647 	this->circular=circ;
25648 	this->teacherName=teacher;
25649 
25650 	this->type=CONSTRAINT_TEACHER_MIN_RESTING_HOURS;
25651 }
25652 
computeInternalStructure(QWidget * parent,Rules & r)25653 bool ConstraintTeacherMinRestingHours::computeInternalStructure(QWidget* parent, Rules& r)
25654 {
25655 	Q_UNUSED(parent);
25656 
25657 	//this->teacher_ID=r.searchTeacher(this->teacherName);
25658 	teacher_ID=r.teachersHash.value(teacherName, -1);
25659 	assert(this->teacher_ID>=0);
25660 	return true;
25661 }
25662 
hasInactiveActivities(Rules & r)25663 bool ConstraintTeacherMinRestingHours::hasInactiveActivities(Rules& r)
25664 {
25665 	Q_UNUSED(r);
25666 	return false;
25667 }
25668 
getXmlDescription(Rules & r)25669 QString ConstraintTeacherMinRestingHours::getXmlDescription(Rules& r)
25670 {
25671 	Q_UNUSED(r);
25672 
25673 	QString s="<ConstraintTeacherMinRestingHours>\n";
25674 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
25675 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
25676 	s+="	<Minimum_Resting_Hours>"+CustomFETString::number(this->minRestingHours)+"</Minimum_Resting_Hours>\n";
25677 	s+="	<Circular>"+trueFalse(circular)+"</Circular>\n";
25678 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
25679 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
25680 	s+="</ConstraintTeacherMinRestingHours>\n";
25681 	return s;
25682 }
25683 
getDescription(Rules & r)25684 QString ConstraintTeacherMinRestingHours::getDescription(Rules& r)
25685 {
25686 	Q_UNUSED(r);
25687 
25688 	QString begin=QString("");
25689 	if(!active)
25690 		begin="X - ";
25691 
25692 	QString end=QString("");
25693 	if(!comments.isEmpty())
25694 		end=", "+tr("C: %1", "Comments").arg(comments);
25695 
25696 	QString s;
25697 	s+=tr("Teacher min resting hours");s+=", ";
25698 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
25699 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
25700 	s+=tr("mRH:%1", "Minimum resting hours").arg(this->minRestingHours);s+=", ";
25701 	s+=tr("C:%1", "Circular").arg(yesNoTranslated(this->circular));
25702 
25703 	return begin+s+end;
25704 }
25705 
getDetailedDescription(Rules & r)25706 QString ConstraintTeacherMinRestingHours::getDetailedDescription(Rules& r)
25707 {
25708 	Q_UNUSED(r);
25709 
25710 	QString s=tr("Time constraint");s+="\n";
25711 	s+=tr("A teacher must respect the minimum resting hours (between days)");s+="\n";
25712 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
25713 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
25714 	s+=tr("Minimum resting hours=%1").arg(this->minRestingHours);s+="\n";
25715 	s+=tr("Circular=%1").arg(yesNoTranslated(circular));s+="\n";
25716 
25717 	if(!active){
25718 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
25719 		s+="\n";
25720 	}
25721 	if(!comments.isEmpty()){
25722 		s+=tr("Comments=%1").arg(comments);
25723 		s+="\n";
25724 	}
25725 
25726 	return s;
25727 }
25728 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)25729 double ConstraintTeacherMinRestingHours::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
25730 {
25731 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
25732 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
25733 		c.teachersMatrixReady=true;
25734 		c.subgroupsMatrixReady=true;
25735 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
25736 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
25737 
25738 		c.changedForMatrixCalculation=false;
25739 	}
25740 
25741 	Q_UNUSED(cl);
25742 	Q_UNUSED(dl);
25743 	Q_UNUSED(conflictsString);
25744 
25745 	assert(this->weightPercentage==100.0);
25746 
25747 	int nbroken=0;
25748 
25749 	for(int d=0; d<=r.nDaysPerWeek-2+(circular?1:0); d++){
25750 		int cnt=0;
25751 		for(int h=r.nHoursPerDay-1; h>=0; h--){
25752 			if(teachersMatrix[this->teacher_ID][d][h]>0)
25753 				break;
25754 			else
25755 				cnt++;
25756 		}
25757 		for(int h=0; h<r.nHoursPerDay; h++){
25758 			if(teachersMatrix[this->teacher_ID][(d+1<=r.nDaysPerWeek-1?d+1:0)][h]>0)
25759 				break;
25760 			else
25761 				cnt++;
25762 		}
25763 		if(cnt < this->minRestingHours)
25764 			nbroken++;
25765 	}
25766 
25767 	assert(nbroken==0);
25768 
25769 	return nbroken;
25770 }
25771 
isRelatedToActivity(Rules & r,Activity * a)25772 bool ConstraintTeacherMinRestingHours::isRelatedToActivity(Rules& r, Activity* a)
25773 {
25774 	Q_UNUSED(r);
25775 	Q_UNUSED(a);
25776 
25777 	return false;
25778 }
25779 
isRelatedToTeacher(Teacher * t)25780 bool ConstraintTeacherMinRestingHours::isRelatedToTeacher(Teacher* t)
25781 {
25782 	if(this->teacherName==t->name)
25783 		return true;
25784 	return false;
25785 }
25786 
isRelatedToSubject(Subject * s)25787 bool ConstraintTeacherMinRestingHours::isRelatedToSubject(Subject* s)
25788 {
25789 	Q_UNUSED(s);
25790 
25791 	return false;
25792 }
25793 
isRelatedToActivityTag(ActivityTag * s)25794 bool ConstraintTeacherMinRestingHours::isRelatedToActivityTag(ActivityTag* s)
25795 {
25796 	Q_UNUSED(s);
25797 
25798 	return false;
25799 }
25800 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)25801 bool ConstraintTeacherMinRestingHours::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
25802 {
25803 	Q_UNUSED(r);
25804 	Q_UNUSED(s);
25805 
25806 	return false;
25807 }
25808 
hasWrongDayOrHour(Rules & r)25809 bool ConstraintTeacherMinRestingHours::hasWrongDayOrHour(Rules& r)
25810 {
25811 	if(minRestingHours>r.nHoursPerDay)
25812 		return true;
25813 
25814 	return false;
25815 }
25816 
canRepairWrongDayOrHour(Rules & r)25817 bool ConstraintTeacherMinRestingHours::canRepairWrongDayOrHour(Rules& r)
25818 {
25819 	assert(hasWrongDayOrHour(r));
25820 
25821 	return true;
25822 }
25823 
repairWrongDayOrHour(Rules & r)25824 bool ConstraintTeacherMinRestingHours::repairWrongDayOrHour(Rules& r)
25825 {
25826 	assert(hasWrongDayOrHour(r));
25827 
25828 	if(minRestingHours>r.nHoursPerDay)
25829 		minRestingHours=r.nHoursPerDay;
25830 
25831 	return true;
25832 }
25833 
25834 ///////////////////////////////////////////////////////////////////////////////////////////
25835 ///////////////////////////////////////////////////////////////////////////////////////////
25836 
ConstraintTeachersMinRestingHours()25837 ConstraintTeachersMinRestingHours::ConstraintTeachersMinRestingHours()
25838 	: TimeConstraint()
25839 {
25840 	this->type=CONSTRAINT_TEACHERS_MIN_RESTING_HOURS;
25841 	this->minRestingHours=-1;
25842 	this->circular=true;
25843 }
25844 
ConstraintTeachersMinRestingHours(double wp,int minrestinghours,bool circ)25845 ConstraintTeachersMinRestingHours::ConstraintTeachersMinRestingHours(double wp, int minrestinghours, bool circ)
25846  : TimeConstraint(wp)
25847  {
25848 	assert(minrestinghours>0);
25849 	this->minRestingHours=minrestinghours;
25850 	this->circular=circ;
25851 
25852 	this->type=CONSTRAINT_TEACHERS_MIN_RESTING_HOURS;
25853 }
25854 
computeInternalStructure(QWidget * parent,Rules & r)25855 bool ConstraintTeachersMinRestingHours::computeInternalStructure(QWidget* parent, Rules& r)
25856 {
25857 	Q_UNUSED(parent);
25858 	Q_UNUSED(r);
25859 
25860 	return true;
25861 }
25862 
hasInactiveActivities(Rules & r)25863 bool ConstraintTeachersMinRestingHours::hasInactiveActivities(Rules& r)
25864 {
25865 	Q_UNUSED(r);
25866 	return false;
25867 }
25868 
getXmlDescription(Rules & r)25869 QString ConstraintTeachersMinRestingHours::getXmlDescription(Rules& r)
25870 {
25871 	Q_UNUSED(r);
25872 
25873 	QString s="<ConstraintTeachersMinRestingHours>\n";
25874 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
25875 	s+="	<Minimum_Resting_Hours>"+CustomFETString::number(this->minRestingHours)+"</Minimum_Resting_Hours>\n";
25876 	s+="	<Circular>"+trueFalse(circular)+"</Circular>\n";
25877 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
25878 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
25879 	s+="</ConstraintTeachersMinRestingHours>\n";
25880 	return s;
25881 }
25882 
getDescription(Rules & r)25883 QString ConstraintTeachersMinRestingHours::getDescription(Rules& r)
25884 {
25885 	Q_UNUSED(r);
25886 
25887 	QString begin=QString("");
25888 	if(!active)
25889 		begin="X - ";
25890 
25891 	QString end=QString("");
25892 	if(!comments.isEmpty())
25893 		end=", "+tr("C: %1", "Comments").arg(comments);
25894 
25895 	QString s;
25896 	s+=tr("Teachers min resting hours");s+=", ";
25897 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
25898 	s+=tr("mRH:%1", "Minimum resting hours").arg(this->minRestingHours);s+=", ";
25899 	s+=tr("C:%1", "Circular").arg(yesNoTranslated(this->circular));
25900 
25901 	return begin+s+end;
25902 }
25903 
getDetailedDescription(Rules & r)25904 QString ConstraintTeachersMinRestingHours::getDetailedDescription(Rules& r)
25905 {
25906 	Q_UNUSED(r);
25907 
25908 	QString s=tr("Time constraint");s+="\n";
25909 	s+=tr("All teachers must respect the minimum resting hours (between days)");s+="\n";
25910 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
25911 	s+=tr("Minimum resting hours=%1").arg(this->minRestingHours);s+="\n";
25912 	s+=tr("Circular=%1").arg(yesNoTranslated(circular));s+="\n";
25913 
25914 	if(!active){
25915 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
25916 		s+="\n";
25917 	}
25918 	if(!comments.isEmpty()){
25919 		s+=tr("Comments=%1").arg(comments);
25920 		s+="\n";
25921 	}
25922 
25923 	return s;
25924 }
25925 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)25926 double ConstraintTeachersMinRestingHours::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
25927 {
25928 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
25929 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
25930 		c.teachersMatrixReady=true;
25931 		c.subgroupsMatrixReady=true;
25932 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
25933 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
25934 
25935 		c.changedForMatrixCalculation=false;
25936 	}
25937 
25938 	Q_UNUSED(cl);
25939 	Q_UNUSED(dl);
25940 	Q_UNUSED(conflictsString);
25941 
25942 	assert(this->weightPercentage==100.0);
25943 
25944 	int nbroken=0;
25945 
25946 	for(int tch=0; tch<r.nInternalTeachers; tch++){
25947 		for(int d=0; d<=r.nDaysPerWeek-2+(circular?1:0); d++){
25948 			int cnt=0;
25949 			for(int h=r.nHoursPerDay-1; h>=0; h--){
25950 				if(teachersMatrix[tch][d][h]>0)
25951 					break;
25952 				else
25953 					cnt++;
25954 			}
25955 			for(int h=0; h<r.nHoursPerDay; h++){
25956 				if(teachersMatrix[tch][(d+1<=r.nDaysPerWeek-1?d+1:0)][h]>0)
25957 					break;
25958 				else
25959 					cnt++;
25960 			}
25961 			if(cnt < this->minRestingHours)
25962 				nbroken++;
25963 		}
25964 	}
25965 
25966 	assert(nbroken==0);
25967 
25968 	return nbroken;
25969 }
25970 
isRelatedToActivity(Rules & r,Activity * a)25971 bool ConstraintTeachersMinRestingHours::isRelatedToActivity(Rules& r, Activity* a)
25972 {
25973 	Q_UNUSED(r);
25974 	Q_UNUSED(a);
25975 
25976 	return false;
25977 }
25978 
isRelatedToTeacher(Teacher * t)25979 bool ConstraintTeachersMinRestingHours::isRelatedToTeacher(Teacher* t)
25980 {
25981 	Q_UNUSED(t);
25982 
25983 	return true;
25984 }
25985 
isRelatedToSubject(Subject * s)25986 bool ConstraintTeachersMinRestingHours::isRelatedToSubject(Subject* s)
25987 {
25988 	Q_UNUSED(s);
25989 
25990 	return false;
25991 }
25992 
isRelatedToActivityTag(ActivityTag * s)25993 bool ConstraintTeachersMinRestingHours::isRelatedToActivityTag(ActivityTag* s)
25994 {
25995 	Q_UNUSED(s);
25996 
25997 	return false;
25998 }
25999 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)26000 bool ConstraintTeachersMinRestingHours::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
26001 {
26002 	Q_UNUSED(r);
26003 	Q_UNUSED(s);
26004 
26005 	return false;
26006 }
26007 
hasWrongDayOrHour(Rules & r)26008 bool ConstraintTeachersMinRestingHours::hasWrongDayOrHour(Rules& r)
26009 {
26010 	if(minRestingHours>r.nHoursPerDay)
26011 		return true;
26012 
26013 	return false;
26014 }
26015 
canRepairWrongDayOrHour(Rules & r)26016 bool ConstraintTeachersMinRestingHours::canRepairWrongDayOrHour(Rules& r)
26017 {
26018 	assert(hasWrongDayOrHour(r));
26019 
26020 	return true;
26021 }
26022 
repairWrongDayOrHour(Rules & r)26023 bool ConstraintTeachersMinRestingHours::repairWrongDayOrHour(Rules& r)
26024 {
26025 	assert(hasWrongDayOrHour(r));
26026 
26027 	if(minRestingHours>r.nHoursPerDay)
26028 		minRestingHours=r.nHoursPerDay;
26029 
26030 	return true;
26031 }
26032 
26033 ////////////////////////////////////////////////////////////////////////////////////////////
26034 ////////////////////////////////////////////////////////////////////////////////////////////
26035 
ConstraintStudentsSetMinRestingHours()26036 ConstraintStudentsSetMinRestingHours::ConstraintStudentsSetMinRestingHours()
26037 	: TimeConstraint()
26038 {
26039 	this->type = CONSTRAINT_STUDENTS_SET_MIN_RESTING_HOURS;
26040 	this->minRestingHours = -1;
26041 	this->circular=true;
26042 }
26043 
ConstraintStudentsSetMinRestingHours(double wp,int minrestinghours,bool circ,const QString & sn)26044 ConstraintStudentsSetMinRestingHours::ConstraintStudentsSetMinRestingHours(double wp, int minrestinghours, bool circ, const QString& sn)
26045 	: TimeConstraint(wp)
26046 {
26047 	this->minRestingHours = minrestinghours;
26048 	this->circular=circ;
26049 	this->students = sn;
26050 	this->type = CONSTRAINT_STUDENTS_SET_MIN_RESTING_HOURS;
26051 }
26052 
hasInactiveActivities(Rules & r)26053 bool ConstraintStudentsSetMinRestingHours::hasInactiveActivities(Rules& r)
26054 {
26055 	Q_UNUSED(r);
26056 	return false;
26057 }
26058 
getXmlDescription(Rules & r)26059 QString ConstraintStudentsSetMinRestingHours::getXmlDescription(Rules& r)
26060 {
26061 	Q_UNUSED(r);
26062 
26063 	QString s="<ConstraintStudentsSetMinRestingHours>\n";
26064 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
26065 	s+="	<Minimum_Resting_Hours>"+CustomFETString::number(this->minRestingHours)+"</Minimum_Resting_Hours>\n";
26066 	s+="	<Students>"+protect(this->students)+"</Students>\n";
26067 	s+="	<Circular>"+trueFalse(circular)+"</Circular>\n";
26068 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
26069 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
26070 	s+="</ConstraintStudentsSetMinRestingHours>\n";
26071 	return s;
26072 }
26073 
getDescription(Rules & r)26074 QString ConstraintStudentsSetMinRestingHours::getDescription(Rules& r)
26075 {
26076 	Q_UNUSED(r);
26077 
26078 	QString begin=QString("");
26079 	if(!active)
26080 		begin="X - ";
26081 
26082 	QString end=QString("");
26083 	if(!comments.isEmpty())
26084 		end=", "+tr("C: %1", "Comments").arg(comments);
26085 
26086 	QString s;
26087 	s+=tr("Students set min resting hours");s+=", ";
26088 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
26089 	s+=tr("St:%1", "Students (set)").arg(this->students); s+=", ";
26090 	s+=tr("mRH:%1", "Minimum resting hours").arg(this->minRestingHours);s+=", ";
26091 	s+=tr("C:%1", "Circular").arg(yesNoTranslated(this->circular));
26092 
26093 	return begin+s+end;
26094 }
26095 
getDetailedDescription(Rules & r)26096 QString ConstraintStudentsSetMinRestingHours::getDetailedDescription(Rules& r)
26097 {
26098 	Q_UNUSED(r);
26099 
26100 	QString s=tr("Time constraint");s+="\n";
26101 	s+=tr("A students set must respect the minimum resting hours (between days)");s+="\n";
26102 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
26103 	s+=tr("Students set=%1").arg(this->students);s+="\n";
26104 	s+=tr("Minimum resting hours=%1").arg(this->minRestingHours);s+="\n";
26105 	s+=tr("Circular=%1").arg(yesNoTranslated(circular));s+="\n";
26106 
26107 	if(!active){
26108 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
26109 		s+="\n";
26110 	}
26111 	if(!comments.isEmpty()){
26112 		s+=tr("Comments=%1").arg(comments);
26113 		s+="\n";
26114 	}
26115 
26116 	return s;
26117 }
26118 
computeInternalStructure(QWidget * parent,Rules & r)26119 bool ConstraintStudentsSetMinRestingHours::computeInternalStructure(QWidget* parent, Rules& r)
26120 {
26121 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
26122 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
26123 
26124 	if(ss==nullptr){
26125 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
26126 		 tr("Constraint students set min resting hours is wrong because it refers to inexistent students set."
26127 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
26128 
26129 		return false;
26130 	}
26131 
26132 	assert(ss!=nullptr);
26133 
26134 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
26135 	/*this->iSubgroupsList.clear();
26136 	if(ss->type==STUDENTS_SUBGROUP){
26137 		int tmp;
26138 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
26139 		assert(tmp>=0);
26140 		assert(tmp<r.nInternalSubgroups);
26141 		if(!this->iSubgroupsList.contains(tmp))
26142 			this->iSubgroupsList.append(tmp);
26143 	}
26144 	else if(ss->type==STUDENTS_GROUP){
26145 		StudentsGroup* stg=(StudentsGroup*)ss;
26146 		for(int i=0; i<stg->subgroupsList.size(); i++){
26147 			StudentsSubgroup* sts=stg->subgroupsList[i];
26148 			int tmp;
26149 			tmp=sts->indexInInternalSubgroupsList;
26150 			assert(tmp>=0);
26151 			assert(tmp<r.nInternalSubgroups);
26152 			if(!this->iSubgroupsList.contains(tmp))
26153 				this->iSubgroupsList.append(tmp);
26154 		}
26155 	}
26156 	else if(ss->type==STUDENTS_YEAR){
26157 		StudentsYear* sty=(StudentsYear*)ss;
26158 		for(int i=0; i<sty->groupsList.size(); i++){
26159 			StudentsGroup* stg=sty->groupsList[i];
26160 			for(int j=0; j<stg->subgroupsList.size(); j++){
26161 				StudentsSubgroup* sts=stg->subgroupsList[j];
26162 				int tmp;
26163 				tmp=sts->indexInInternalSubgroupsList;
26164 				assert(tmp>=0);
26165 				assert(tmp<r.nInternalSubgroups);
26166 				if(!this->iSubgroupsList.contains(tmp))
26167 					this->iSubgroupsList.append(tmp);
26168 			}
26169 		}
26170 	}
26171 	else
26172 		assert(0);*/
26173 
26174 	return true;
26175 }
26176 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)26177 double ConstraintStudentsSetMinRestingHours::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
26178 {
26179 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
26180 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
26181 		c.teachersMatrixReady=true;
26182 		c.subgroupsMatrixReady=true;
26183 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
26184 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
26185 
26186 		c.changedForMatrixCalculation=false;
26187 	}
26188 
26189 	Q_UNUSED(cl);
26190 	Q_UNUSED(dl);
26191 	Q_UNUSED(conflictsString);
26192 
26193 	assert(this->weightPercentage==100.0);
26194 
26195 	int nbroken=0;
26196 
26197 	for(int sbg : qAsConst(this->iSubgroupsList)){
26198 		for(int d=0; d<=r.nDaysPerWeek-2+(circular?1:0); d++){
26199 			int cnt=0;
26200 			for(int h=r.nHoursPerDay-1; h>=0; h--){
26201 				if(subgroupsMatrix[sbg][d][h]>0)
26202 					break;
26203 				else
26204 					cnt++;
26205 			}
26206 			for(int h=0; h<r.nHoursPerDay; h++){
26207 				if(subgroupsMatrix[sbg][(d+1<=r.nDaysPerWeek-1?d+1:0)][h]>0)
26208 					break;
26209 				else
26210 					cnt++;
26211 			}
26212 			if(cnt < this->minRestingHours)
26213 				nbroken++;
26214 		}
26215 	}
26216 
26217 	assert(nbroken==0);
26218 
26219 	return nbroken;
26220 }
26221 
isRelatedToActivity(Rules & r,Activity * a)26222 bool ConstraintStudentsSetMinRestingHours::isRelatedToActivity(Rules& r, Activity* a)
26223 {
26224 	Q_UNUSED(r);
26225 	Q_UNUSED(a);
26226 
26227 	return false;
26228 }
26229 
isRelatedToTeacher(Teacher * t)26230 bool ConstraintStudentsSetMinRestingHours::isRelatedToTeacher(Teacher* t)
26231 {
26232 	Q_UNUSED(t);
26233 
26234 	return false;
26235 }
26236 
isRelatedToSubject(Subject * s)26237 bool ConstraintStudentsSetMinRestingHours::isRelatedToSubject(Subject* s)
26238 {
26239 	Q_UNUSED(s);
26240 
26241 	return false;
26242 }
26243 
isRelatedToActivityTag(ActivityTag * s)26244 bool ConstraintStudentsSetMinRestingHours::isRelatedToActivityTag(ActivityTag* s)
26245 {
26246 	Q_UNUSED(s);
26247 
26248 	return false;
26249 }
26250 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)26251 bool ConstraintStudentsSetMinRestingHours::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
26252 {
26253 	return r.setsShareStudents(this->students, s->name);
26254 }
26255 
hasWrongDayOrHour(Rules & r)26256 bool ConstraintStudentsSetMinRestingHours::hasWrongDayOrHour(Rules& r)
26257 {
26258 	if(minRestingHours>r.nHoursPerDay)
26259 		return true;
26260 
26261 	return false;
26262 }
26263 
canRepairWrongDayOrHour(Rules & r)26264 bool ConstraintStudentsSetMinRestingHours::canRepairWrongDayOrHour(Rules& r)
26265 {
26266 	assert(hasWrongDayOrHour(r));
26267 
26268 	return true;
26269 }
26270 
repairWrongDayOrHour(Rules & r)26271 bool ConstraintStudentsSetMinRestingHours::repairWrongDayOrHour(Rules& r)
26272 {
26273 	assert(hasWrongDayOrHour(r));
26274 
26275 	if(minRestingHours>r.nHoursPerDay)
26276 		minRestingHours=r.nHoursPerDay;
26277 
26278 	return true;
26279 }
26280 
26281 ////////////////////////////////////////////////////////////////////////////////////////////
26282 ////////////////////////////////////////////////////////////////////////////////////////////
26283 
ConstraintStudentsMinRestingHours()26284 ConstraintStudentsMinRestingHours::ConstraintStudentsMinRestingHours()
26285 	: TimeConstraint()
26286 {
26287 	this->type = CONSTRAINT_STUDENTS_MIN_RESTING_HOURS;
26288 	this->minRestingHours = -1;
26289 	this->circular=true;
26290 }
26291 
ConstraintStudentsMinRestingHours(double wp,int minrestinghours,bool circ)26292 ConstraintStudentsMinRestingHours::ConstraintStudentsMinRestingHours(double wp, int minrestinghours, bool circ)
26293 	: TimeConstraint(wp)
26294 {
26295 	this->minRestingHours = minrestinghours;
26296 	this->circular=circ;
26297 	this->type = CONSTRAINT_STUDENTS_MIN_RESTING_HOURS;
26298 }
26299 
hasInactiveActivities(Rules & r)26300 bool ConstraintStudentsMinRestingHours::hasInactiveActivities(Rules& r)
26301 {
26302 	Q_UNUSED(r);
26303 	return false;
26304 }
26305 
getXmlDescription(Rules & r)26306 QString ConstraintStudentsMinRestingHours::getXmlDescription(Rules& r)
26307 {
26308 	Q_UNUSED(r);
26309 
26310 	QString s="<ConstraintStudentsMinRestingHours>\n";
26311 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
26312 	s+="	<Minimum_Resting_Hours>"+CustomFETString::number(this->minRestingHours)+"</Minimum_Resting_Hours>\n";
26313 	s+="	<Circular>"+trueFalse(circular)+"</Circular>\n";
26314 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
26315 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
26316 	s+="</ConstraintStudentsMinRestingHours>\n";
26317 	return s;
26318 }
26319 
getDescription(Rules & r)26320 QString ConstraintStudentsMinRestingHours::getDescription(Rules& r)
26321 {
26322 	Q_UNUSED(r);
26323 
26324 	QString begin=QString("");
26325 	if(!active)
26326 		begin="X - ";
26327 
26328 	QString end=QString("");
26329 	if(!comments.isEmpty())
26330 		end=", "+tr("C: %1", "Comments").arg(comments);
26331 
26332 	QString s;
26333 	s+=tr("Students min resting hours");s+=", ";
26334 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
26335 	s+=tr("mRH:%1", "Minimum resting hours").arg(this->minRestingHours);s+=", ";
26336 	s+=tr("C:%1", "Circular").arg(yesNoTranslated(this->circular));
26337 
26338 	return begin+s+end;
26339 }
26340 
getDetailedDescription(Rules & r)26341 QString ConstraintStudentsMinRestingHours::getDetailedDescription(Rules& r)
26342 {
26343 	Q_UNUSED(r);
26344 
26345 	QString s=tr("Time constraint");s+="\n";
26346 	s+=tr("All students must respect the minimum resting hours (between days)");s+="\n";
26347 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
26348 	s+=tr("Minimum resting hours=%1").arg(this->minRestingHours);s+="\n";
26349 	s+=tr("Circular=%1").arg(yesNoTranslated(circular));s+="\n";
26350 
26351 	if(!active){
26352 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
26353 		s+="\n";
26354 	}
26355 	if(!comments.isEmpty()){
26356 		s+=tr("Comments=%1").arg(comments);
26357 		s+="\n";
26358 	}
26359 
26360 	return s;
26361 }
26362 
computeInternalStructure(QWidget * parent,Rules & r)26363 bool ConstraintStudentsMinRestingHours::computeInternalStructure(QWidget* parent, Rules& r)
26364 {
26365 	Q_UNUSED(parent);
26366 	Q_UNUSED(r);
26367 
26368 	return true;
26369 }
26370 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)26371 double ConstraintStudentsMinRestingHours::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
26372 {
26373 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
26374 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
26375 		c.teachersMatrixReady=true;
26376 		c.subgroupsMatrixReady=true;
26377 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
26378 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
26379 
26380 		c.changedForMatrixCalculation=false;
26381 	}
26382 
26383 	Q_UNUSED(cl);
26384 	Q_UNUSED(dl);
26385 	Q_UNUSED(conflictsString);
26386 
26387 	assert(this->weightPercentage==100.0);
26388 
26389 	int nbroken=0;
26390 
26391 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
26392 		for(int d=0; d<=r.nDaysPerWeek-2+(circular?1:0); d++){
26393 			int cnt=0;
26394 			for(int h=r.nHoursPerDay-1; h>=0; h--){
26395 				if(subgroupsMatrix[sbg][d][h]>0)
26396 					break;
26397 				else
26398 					cnt++;
26399 			}
26400 			for(int h=0; h<r.nHoursPerDay; h++){
26401 				if(subgroupsMatrix[sbg][(d+1<=r.nDaysPerWeek-1?d+1:0)][h]>0)
26402 					break;
26403 				else
26404 					cnt++;
26405 			}
26406 			if(cnt < this->minRestingHours)
26407 				nbroken++;
26408 		}
26409 	}
26410 
26411 	assert(nbroken==0);
26412 
26413 	return nbroken;
26414 }
26415 
isRelatedToActivity(Rules & r,Activity * a)26416 bool ConstraintStudentsMinRestingHours::isRelatedToActivity(Rules& r, Activity* a)
26417 {
26418 	Q_UNUSED(r);
26419 	Q_UNUSED(a);
26420 
26421 	return false;
26422 }
26423 
isRelatedToTeacher(Teacher * t)26424 bool ConstraintStudentsMinRestingHours::isRelatedToTeacher(Teacher* t)
26425 {
26426 	Q_UNUSED(t);
26427 
26428 	return false;
26429 }
26430 
isRelatedToSubject(Subject * s)26431 bool ConstraintStudentsMinRestingHours::isRelatedToSubject(Subject* s)
26432 {
26433 	Q_UNUSED(s);
26434 
26435 	return false;
26436 }
26437 
isRelatedToActivityTag(ActivityTag * s)26438 bool ConstraintStudentsMinRestingHours::isRelatedToActivityTag(ActivityTag* s)
26439 {
26440 	Q_UNUSED(s);
26441 
26442 	return false;
26443 }
26444 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)26445 bool ConstraintStudentsMinRestingHours::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
26446 {
26447 	Q_UNUSED(r);
26448 	Q_UNUSED(s);
26449 
26450 	return true;
26451 }
26452 
hasWrongDayOrHour(Rules & r)26453 bool ConstraintStudentsMinRestingHours::hasWrongDayOrHour(Rules& r)
26454 {
26455 	if(minRestingHours>r.nHoursPerDay)
26456 		return true;
26457 
26458 	return false;
26459 }
26460 
canRepairWrongDayOrHour(Rules & r)26461 bool ConstraintStudentsMinRestingHours::canRepairWrongDayOrHour(Rules& r)
26462 {
26463 	assert(hasWrongDayOrHour(r));
26464 
26465 	return true;
26466 }
26467 
repairWrongDayOrHour(Rules & r)26468 bool ConstraintStudentsMinRestingHours::repairWrongDayOrHour(Rules& r)
26469 {
26470 	assert(hasWrongDayOrHour(r));
26471 
26472 	if(minRestingHours>r.nHoursPerDay)
26473 		minRestingHours=r.nHoursPerDay;
26474 
26475 	return true;
26476 }
26477 
26478 ////////////////////////////////////////////////////////////////////////////////////////////
26479 ////////////////////////////////////////////////////////////////////////////////////////////
26480 
ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags()26481 ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags()
26482 	: TimeConstraint()
26483 {
26484 	this->type = CONSTRAINT_STUDENTS_SET_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS;
26485 
26486 	this->minGaps = 0;
26487 	this->firstActivityTag=QString("");
26488 	this->secondActivityTag=QString("");
26489 	this->students=QString("");
26490 }
26491 
ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags(double wp,const QString & _students,int _minGaps,const QString & _firstActivityTag,const QString & _secondActivityTag)26492 ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags(double wp, const QString& _students, int _minGaps, const QString& _firstActivityTag, const QString& _secondActivityTag)
26493 	: TimeConstraint(wp)
26494 {
26495 	this->type = CONSTRAINT_STUDENTS_SET_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS;
26496 
26497 	this->minGaps = _minGaps;
26498 	this->firstActivityTag=_firstActivityTag;
26499 	this->secondActivityTag=_secondActivityTag;
26500 	this->students=_students;
26501 }
26502 
hasInactiveActivities(Rules & r)26503 bool ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::hasInactiveActivities(Rules& r)
26504 {
26505 	Q_UNUSED(r);
26506 	return false;
26507 }
26508 
getXmlDescription(Rules & r)26509 QString ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::getXmlDescription(Rules& r)
26510 {
26511 	Q_UNUSED(r);
26512 
26513 	QString s="<ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags>\n";
26514 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
26515 	s+="	<Students>"+protect(this->students)+"</Students>\n";
26516 	s+="	<First_Activity_Tag>"+protect(this->firstActivityTag)+"</First_Activity_Tag>\n";
26517 	s+="	<Second_Activity_Tag>"+protect(this->secondActivityTag)+"</Second_Activity_Tag>\n";
26518 	s+="	<MinGaps>"+CustomFETString::number(this->minGaps)+"</MinGaps>\n";
26519 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
26520 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
26521 	s+="</ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags>\n";
26522 	return s;
26523 }
26524 
getDescription(Rules & r)26525 QString ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::getDescription(Rules& r)
26526 {
26527 	Q_UNUSED(r);
26528 
26529 	QString begin=QString("");
26530 	if(!active)
26531 		begin="X - ";
26532 
26533 	QString end=QString("");
26534 	if(!comments.isEmpty())
26535 		end=", "+tr("C: %1", "Comments").arg(comments);
26536 
26537 	QString s;
26538 
26539 	s+=tr("Students set min gaps between ordered pair of activity tags");s+=", ";
26540 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
26541 	s+=tr("St:%1", "Students (set)").arg(this->students);s+=", ";
26542 	s+=tr("FAT:%1", "First activity tag").arg(this->firstActivityTag);s+=", ";
26543 	s+=tr("SAT:%1", "Second activity tag").arg(this->secondActivityTag);s+=", ";
26544 	s+=tr("mG:%1", "Min gaps").arg(this->minGaps);
26545 
26546 	return begin+s+end;
26547 }
26548 
getDetailedDescription(Rules & r)26549 QString ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::getDetailedDescription(Rules& r)
26550 {
26551 	Q_UNUSED(r);
26552 
26553 	QString s=tr("Time constraint");s+="\n";
26554 	s+=tr("A students set must respect the minimum gaps between an ordered pair of activity tags");s+="\n";
26555 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
26556 	s+=tr("Students set=%1").arg(this->students);s+="\n";
26557 	s+=tr("First activity tag=%1").arg(this->firstActivityTag);s+="\n";
26558 	s+=tr("Second activity tag=%1").arg(this->secondActivityTag);s+="\n";
26559 	s+=tr("Minimum gaps=%1").arg(this->minGaps);s+="\n";
26560 
26561 	if(!active){
26562 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
26563 		s+="\n";
26564 	}
26565 	if(!comments.isEmpty()){
26566 		s+=tr("Comments=%1").arg(comments);
26567 		s+="\n";
26568 	}
26569 
26570 	return s;
26571 }
26572 
computeInternalStructure(QWidget * parent,Rules & r)26573 bool ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::computeInternalStructure(QWidget* parent, Rules& r)
26574 {
26575 	_firstActivityTagIndex=r.activityTagsHash.value(firstActivityTag, -1);
26576 	assert(this->_firstActivityTagIndex>=0);
26577 
26578 	_secondActivityTagIndex=r.activityTagsHash.value(secondActivityTag, -1);
26579 	assert(this->_secondActivityTagIndex>=0);
26580 
26581 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
26582 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
26583 
26584 	if(ss==nullptr){
26585 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
26586 		 tr("Constraint students set min gaps between ordered pair of activity tags is wrong because it refers to inexistent students set."
26587 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
26588 
26589 		return false;
26590 	}
26591 
26592 	assert(ss!=nullptr);
26593 
26594 	QList<int> iSubgroupsList;
26595 	populateInternalSubgroupsList(r, ss, iSubgroupsList);
26596 	/*iSubgroupsList.clear();
26597 	if(ss->type==STUDENTS_SUBGROUP){
26598 		int tmp;
26599 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
26600 		assert(tmp>=0);
26601 		assert(tmp<r.nInternalSubgroups);
26602 		if(!iSubgroupsList.contains(tmp))
26603 			iSubgroupsList.append(tmp);
26604 	}
26605 	else if(ss->type==STUDENTS_GROUP){
26606 		StudentsGroup* stg=(StudentsGroup*)ss;
26607 		for(int i=0; i<stg->subgroupsList.size(); i++){
26608 			StudentsSubgroup* sts=stg->subgroupsList[i];
26609 			int tmp;
26610 			tmp=sts->indexInInternalSubgroupsList;
26611 			assert(tmp>=0);
26612 			assert(tmp<r.nInternalSubgroups);
26613 			if(!iSubgroupsList.contains(tmp))
26614 				iSubgroupsList.append(tmp);
26615 		}
26616 	}
26617 	else if(ss->type==STUDENTS_YEAR){
26618 		StudentsYear* sty=(StudentsYear*)ss;
26619 		for(int i=0; i<sty->groupsList.size(); i++){
26620 			StudentsGroup* stg=sty->groupsList[i];
26621 			for(int j=0; j<stg->subgroupsList.size(); j++){
26622 				StudentsSubgroup* sts=stg->subgroupsList[j];
26623 				int tmp;
26624 				tmp=sts->indexInInternalSubgroupsList;
26625 				assert(tmp>=0);
26626 				assert(tmp<r.nInternalSubgroups);
26627 				if(!iSubgroupsList.contains(tmp))
26628 					iSubgroupsList.append(tmp);
26629 			}
26630 		}
26631 	}
26632 	else
26633 		assert(0);*/
26634 
26635 	/////////////
26636 	this->canonicalSubgroupsList.clear();
26637 	for(int i : qAsConst(iSubgroupsList)){
26638 		bool foundF=false; //found first
26639 		bool foundS=false; //found second
26640 
26641 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
26642 		for(int actIndex : qAsConst(sbg->activitiesForSubgroup)){
26643 			if(!foundF)
26644 				if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->_firstActivityTagIndex))
26645 					foundF=true;
26646 			if(!foundS)
26647 				if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->_secondActivityTagIndex))
26648 					foundS=true;
26649 
26650 			if(foundF && foundS)
26651 				break;
26652 		}
26653 
26654 		if(foundF && foundS)
26655 			this->canonicalSubgroupsList.append(i);
26656 	}
26657 
26658 	return true;
26659 }
26660 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)26661 double ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
26662 {
26663 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
26664 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
26665 		c.teachersMatrixReady=true;
26666 		c.subgroupsMatrixReady=true;
26667 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
26668 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
26669 
26670 		c.changedForMatrixCalculation=false;
26671 	}
26672 
26673 	int nbroken=0;
26674 
26675 	Matrix2D<int> crtSubgroupTimetableActivityTag;
26676 	crtSubgroupTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
26677 
26678 	for(int i : qAsConst(this->canonicalSubgroupsList)){
26679 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
26680 
26681 		for(int d=0; d<r.nDaysPerWeek; d++)
26682 			for(int h=0; h<r.nHoursPerDay; h++)
26683 				crtSubgroupTimetableActivityTag[d][h]=-1;
26684 
26685 		for(int ai : qAsConst(sbg->activitiesForSubgroup)) if(c.times[ai]!=UNALLOCATED_TIME){
26686 			int d=c.times[ai]%r.nDaysPerWeek;
26687 			int h=c.times[ai]/r.nDaysPerWeek;
26688 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
26689 				assert(h+dur<r.nHoursPerDay);
26690 				assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
26691 
26692 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->_firstActivityTagIndex)){
26693 					assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
26694 					crtSubgroupTimetableActivityTag[d][h+dur]=this->_firstActivityTagIndex;
26695 				}
26696 				else if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->_secondActivityTagIndex)){
26697 					assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
26698 					crtSubgroupTimetableActivityTag[d][h+dur]=this->_secondActivityTagIndex;
26699 				}
26700 			}
26701 		}
26702 
26703 		for(int d=0; d<r.nDaysPerWeek; d++){
26704 			int cnt=-1;
26705 			for(int h=0; h<r.nHoursPerDay; h++){
26706 				if(crtSubgroupTimetableActivityTag[d][h]==_firstActivityTagIndex){
26707 					cnt=0;
26708 				}
26709 				else if(crtSubgroupTimetableActivityTag[d][h]==-1){
26710 					if(cnt>=0)
26711 						cnt++;
26712 				}
26713 				else if(crtSubgroupTimetableActivityTag[d][h]==_secondActivityTagIndex){
26714 					if(cnt>=0 && cnt<minGaps){
26715 						nbroken++;
26716 
26717 						if(conflictsString!=nullptr){
26718 							QString s=tr("Time constraint students set min %1 gaps between ordered pair of activity tags broken for subgroup: %2,"
26719 							 " day: %3, real gaps=%4, conflicts increase=%5")
26720 							 .arg(minGaps)
26721 							 .arg(sbg->name)
26722 							 .arg(r.daysOfTheWeek[d])
26723 							 .arg(CustomFETString::number(cnt))
26724 							 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
26725 
26726 							dl.append(s);
26727 							cl.append(1*weightPercentage/100);
26728 
26729 							*conflictsString+= s+"\n";
26730 						}
26731 					}
26732 
26733 					cnt=-1;
26734 				}
26735 				else{
26736 					assert(0);
26737 				}
26738 			}
26739 		}
26740 	}
26741 
26742 	if(weightPercentage==100)
26743 		assert(nbroken==0);
26744 
26745 	return nbroken * weightPercentage / 100.0;
26746 }
26747 
isRelatedToActivity(Rules & r,Activity * a)26748 bool ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::isRelatedToActivity(Rules& r, Activity* a)
26749 {
26750 	Q_UNUSED(r);
26751 	Q_UNUSED(a);
26752 
26753 	return false;
26754 }
26755 
isRelatedToTeacher(Teacher * t)26756 bool ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::isRelatedToTeacher(Teacher* t)
26757 {
26758 	Q_UNUSED(t);
26759 
26760 	return false;
26761 }
26762 
isRelatedToSubject(Subject * s)26763 bool ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::isRelatedToSubject(Subject* s)
26764 {
26765 	Q_UNUSED(s);
26766 
26767 	return false;
26768 }
26769 
isRelatedToActivityTag(ActivityTag * s)26770 bool ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::isRelatedToActivityTag(ActivityTag* s)
26771 {
26772 	if(s->name==this->firstActivityTag || s->name==this->secondActivityTag)
26773 		return true;
26774 
26775 	return false;
26776 }
26777 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)26778 bool ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
26779 {
26780 	return r.setsShareStudents(this->students, s->name);
26781 }
26782 
hasWrongDayOrHour(Rules & r)26783 bool ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::hasWrongDayOrHour(Rules& r)
26784 {
26785 	if(minGaps>r.nHoursPerDay)
26786 		return true;
26787 
26788 	return false;
26789 }
26790 
canRepairWrongDayOrHour(Rules & r)26791 bool ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::canRepairWrongDayOrHour(Rules& r)
26792 {
26793 	assert(hasWrongDayOrHour(r));
26794 
26795 	return true;
26796 }
26797 
repairWrongDayOrHour(Rules & r)26798 bool ConstraintStudentsSetMinGapsBetweenOrderedPairOfActivityTags::repairWrongDayOrHour(Rules& r)
26799 {
26800 	assert(hasWrongDayOrHour(r));
26801 
26802 	if(minGaps>r.nHoursPerDay)
26803 		minGaps=r.nHoursPerDay;
26804 
26805 	return true;
26806 }
26807 
26808 ////////////////////////////////////////////////////////////////////////////////////////////
26809 ////////////////////////////////////////////////////////////////////////////////////////////
26810 
ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags()26811 ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags()
26812 	: TimeConstraint()
26813 {
26814 	this->type = CONSTRAINT_STUDENTS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS;
26815 
26816 	this->minGaps = 0;
26817 	this->firstActivityTag=QString("");
26818 	this->secondActivityTag=QString("");
26819 }
26820 
ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags(double wp,int _minGaps,const QString & _firstActivityTag,const QString & _secondActivityTag)26821 ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags(double wp, int _minGaps, const QString& _firstActivityTag, const QString& _secondActivityTag)
26822 	: TimeConstraint(wp)
26823 {
26824 	this->type = CONSTRAINT_STUDENTS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS;
26825 
26826 	this->minGaps = _minGaps;
26827 	this->firstActivityTag=_firstActivityTag;
26828 	this->secondActivityTag=_secondActivityTag;
26829 }
26830 
hasInactiveActivities(Rules & r)26831 bool ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::hasInactiveActivities(Rules& r)
26832 {
26833 	Q_UNUSED(r);
26834 	return false;
26835 }
26836 
getXmlDescription(Rules & r)26837 QString ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::getXmlDescription(Rules& r)
26838 {
26839 	Q_UNUSED(r);
26840 
26841 	QString s="<ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags>\n";
26842 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
26843 	s+="	<First_Activity_Tag>"+protect(this->firstActivityTag)+"</First_Activity_Tag>\n";
26844 	s+="	<Second_Activity_Tag>"+protect(this->secondActivityTag)+"</Second_Activity_Tag>\n";
26845 	s+="	<MinGaps>"+CustomFETString::number(this->minGaps)+"</MinGaps>\n";
26846 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
26847 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
26848 	s+="</ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags>\n";
26849 	return s;
26850 }
26851 
getDescription(Rules & r)26852 QString ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::getDescription(Rules& r)
26853 {
26854 	Q_UNUSED(r);
26855 
26856 	QString begin=QString("");
26857 	if(!active)
26858 		begin="X - ";
26859 
26860 	QString end=QString("");
26861 	if(!comments.isEmpty())
26862 		end=", "+tr("C: %1", "Comments").arg(comments);
26863 
26864 	QString s;
26865 
26866 	s+=tr("Students min gaps between ordered pair of activity tags");s+=", ";
26867 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
26868 	s+=tr("FAT:%1", "First activity tag").arg(this->firstActivityTag);s+=", ";
26869 	s+=tr("SAT:%1", "Second activity tag").arg(this->secondActivityTag);s+=", ";
26870 	s+=tr("mG:%1", "Min gaps").arg(this->minGaps);
26871 
26872 	return begin+s+end;
26873 }
26874 
getDetailedDescription(Rules & r)26875 QString ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::getDetailedDescription(Rules& r)
26876 {
26877 	Q_UNUSED(r);
26878 
26879 	QString s=tr("Time constraint");s+="\n";
26880 	s+=tr("All students must respect the minimum gaps between an ordered pair of activity tags");s+="\n";
26881 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
26882 	s+=tr("First activity tag=%1").arg(this->firstActivityTag);s+="\n";
26883 	s+=tr("Second activity tag=%1").arg(this->secondActivityTag);s+="\n";
26884 	s+=tr("Minimum gaps=%1").arg(this->minGaps);s+="\n";
26885 
26886 	if(!active){
26887 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
26888 		s+="\n";
26889 	}
26890 	if(!comments.isEmpty()){
26891 		s+=tr("Comments=%1").arg(comments);
26892 		s+="\n";
26893 	}
26894 
26895 	return s;
26896 }
26897 
computeInternalStructure(QWidget * parent,Rules & r)26898 bool ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::computeInternalStructure(QWidget* parent, Rules& r)
26899 {
26900 	Q_UNUSED(parent);
26901 
26902 	_firstActivityTagIndex=r.activityTagsHash.value(firstActivityTag, -1);
26903 	assert(this->_firstActivityTagIndex>=0);
26904 
26905 	_secondActivityTagIndex=r.activityTagsHash.value(secondActivityTag, -1);
26906 	assert(this->_secondActivityTagIndex>=0);
26907 
26908 	this->canonicalSubgroupsList.clear();
26909 	for(int i=0; i<r.nInternalSubgroups; i++){
26910 		bool foundF=false; //found first
26911 		bool foundS=false; //found second
26912 
26913 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
26914 		for(int actIndex : qAsConst(sbg->activitiesForSubgroup)){
26915 			if(!foundF)
26916 				if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->_firstActivityTagIndex))
26917 					foundF=true;
26918 			if(!foundS)
26919 				if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->_secondActivityTagIndex))
26920 					foundS=true;
26921 
26922 			if(foundF && foundS)
26923 				break;
26924 		}
26925 
26926 		if(foundF && foundS)
26927 			this->canonicalSubgroupsList.append(i);
26928 	}
26929 
26930 	return true;
26931 }
26932 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)26933 double ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
26934 {
26935 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
26936 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
26937 		c.teachersMatrixReady=true;
26938 		c.subgroupsMatrixReady=true;
26939 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
26940 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
26941 
26942 		c.changedForMatrixCalculation=false;
26943 	}
26944 
26945 	int nbroken=0;
26946 
26947 	Matrix2D<int> crtSubgroupTimetableActivityTag;
26948 	crtSubgroupTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
26949 
26950 	for(int i : qAsConst(this->canonicalSubgroupsList)){
26951 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
26952 
26953 		for(int d=0; d<r.nDaysPerWeek; d++)
26954 			for(int h=0; h<r.nHoursPerDay; h++)
26955 				crtSubgroupTimetableActivityTag[d][h]=-1;
26956 
26957 		for(int ai : qAsConst(sbg->activitiesForSubgroup)) if(c.times[ai]!=UNALLOCATED_TIME){
26958 			int d=c.times[ai]%r.nDaysPerWeek;
26959 			int h=c.times[ai]/r.nDaysPerWeek;
26960 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
26961 				assert(h+dur<r.nHoursPerDay);
26962 				assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
26963 
26964 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->_firstActivityTagIndex)){
26965 					assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
26966 					crtSubgroupTimetableActivityTag[d][h+dur]=this->_firstActivityTagIndex;
26967 				}
26968 				else if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->_secondActivityTagIndex)){
26969 					assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
26970 					crtSubgroupTimetableActivityTag[d][h+dur]=this->_secondActivityTagIndex;
26971 				}
26972 			}
26973 		}
26974 
26975 		for(int d=0; d<r.nDaysPerWeek; d++){
26976 			int cnt=-1;
26977 			for(int h=0; h<r.nHoursPerDay; h++){
26978 				if(crtSubgroupTimetableActivityTag[d][h]==_firstActivityTagIndex){
26979 					cnt=0;
26980 				}
26981 				else if(crtSubgroupTimetableActivityTag[d][h]==-1){
26982 					if(cnt>=0)
26983 						cnt++;
26984 				}
26985 				else if(crtSubgroupTimetableActivityTag[d][h]==_secondActivityTagIndex){
26986 					if(cnt>=0 && cnt<minGaps){
26987 						nbroken++;
26988 
26989 						if(conflictsString!=nullptr){
26990 							QString s=tr("Time constraint students min %1 gaps between ordered pair of activity tags broken for subgroup: %2,"
26991 							 " day: %3, real gaps=%4, conflicts increase=%5")
26992 							 .arg(minGaps)
26993 							 .arg(sbg->name)
26994 							 .arg(r.daysOfTheWeek[d])
26995 							 .arg(CustomFETString::number(cnt))
26996 							 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
26997 
26998 							dl.append(s);
26999 							cl.append(1*weightPercentage/100);
27000 
27001 							*conflictsString+= s+"\n";
27002 						}
27003 					}
27004 
27005 					cnt=-1;
27006 				}
27007 				else{
27008 					assert(0);
27009 				}
27010 			}
27011 		}
27012 	}
27013 
27014 	if(weightPercentage==100)
27015 		assert(nbroken==0);
27016 
27017 	return nbroken * weightPercentage / 100.0;
27018 }
27019 
isRelatedToActivity(Rules & r,Activity * a)27020 bool ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::isRelatedToActivity(Rules& r, Activity* a)
27021 {
27022 	Q_UNUSED(r);
27023 	Q_UNUSED(a);
27024 
27025 	return false;
27026 }
27027 
isRelatedToTeacher(Teacher * t)27028 bool ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::isRelatedToTeacher(Teacher* t)
27029 {
27030 	Q_UNUSED(t);
27031 
27032 	return false;
27033 }
27034 
isRelatedToSubject(Subject * s)27035 bool ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::isRelatedToSubject(Subject* s)
27036 {
27037 	Q_UNUSED(s);
27038 
27039 	return false;
27040 }
27041 
isRelatedToActivityTag(ActivityTag * s)27042 bool ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::isRelatedToActivityTag(ActivityTag* s)
27043 {
27044 	if(s->name==this->firstActivityTag || s->name==this->secondActivityTag)
27045 		return true;
27046 
27047 	return false;
27048 }
27049 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)27050 bool ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
27051 {
27052 	Q_UNUSED(r);
27053 	Q_UNUSED(s);
27054 
27055 	return true;
27056 }
27057 
hasWrongDayOrHour(Rules & r)27058 bool ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::hasWrongDayOrHour(Rules& r)
27059 {
27060 	if(minGaps>r.nHoursPerDay)
27061 		return true;
27062 
27063 	return false;
27064 }
27065 
canRepairWrongDayOrHour(Rules & r)27066 bool ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::canRepairWrongDayOrHour(Rules& r)
27067 {
27068 	assert(hasWrongDayOrHour(r));
27069 
27070 	return true;
27071 }
27072 
repairWrongDayOrHour(Rules & r)27073 bool ConstraintStudentsMinGapsBetweenOrderedPairOfActivityTags::repairWrongDayOrHour(Rules& r)
27074 {
27075 	assert(hasWrongDayOrHour(r));
27076 
27077 	if(minGaps>r.nHoursPerDay)
27078 		minGaps=r.nHoursPerDay;
27079 
27080 	return true;
27081 }
27082 
27083 ////////////////////////////////////////////////////////////////////////////////////////////
27084 ////////////////////////////////////////////////////////////////////////////////////////////
27085 
ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags()27086 ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags()
27087 	: TimeConstraint()
27088 {
27089 	this->type = CONSTRAINT_TEACHER_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS;
27090 
27091 	this->minGaps = 0;
27092 	this->firstActivityTag=QString("");
27093 	this->secondActivityTag=QString("");
27094 	this->teacher=QString("");
27095 }
27096 
ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags(double wp,const QString & _teacher,int _minGaps,const QString & _firstActivityTag,const QString & _secondActivityTag)27097 ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags(double wp, const QString& _teacher, int _minGaps, const QString& _firstActivityTag, const QString& _secondActivityTag)
27098 	: TimeConstraint(wp)
27099 {
27100 	this->type = CONSTRAINT_TEACHER_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS;
27101 
27102 	this->minGaps = _minGaps;
27103 	this->firstActivityTag=_firstActivityTag;
27104 	this->secondActivityTag=_secondActivityTag;
27105 	this->teacher=_teacher;
27106 }
27107 
hasInactiveActivities(Rules & r)27108 bool ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::hasInactiveActivities(Rules& r)
27109 {
27110 	Q_UNUSED(r);
27111 	return false;
27112 }
27113 
getXmlDescription(Rules & r)27114 QString ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::getXmlDescription(Rules& r)
27115 {
27116 	Q_UNUSED(r);
27117 
27118 	QString s="<ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags>\n";
27119 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
27120 	s+="	<Teacher>"+protect(this->teacher)+"</Teacher>\n";
27121 	s+="	<First_Activity_Tag>"+protect(this->firstActivityTag)+"</First_Activity_Tag>\n";
27122 	s+="	<Second_Activity_Tag>"+protect(this->secondActivityTag)+"</Second_Activity_Tag>\n";
27123 	s+="	<MinGaps>"+CustomFETString::number(this->minGaps)+"</MinGaps>\n";
27124 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
27125 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
27126 	s+="</ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags>\n";
27127 	return s;
27128 }
27129 
getDescription(Rules & r)27130 QString ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::getDescription(Rules& r)
27131 {
27132 	Q_UNUSED(r);
27133 
27134 	QString begin=QString("");
27135 	if(!active)
27136 		begin="X - ";
27137 
27138 	QString end=QString("");
27139 	if(!comments.isEmpty())
27140 		end=", "+tr("C: %1", "Comments").arg(comments);
27141 
27142 	QString s;
27143 
27144 	s+=tr("Teacher min gaps between ordered pair of activity tags");s+=", ";
27145 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
27146 	s+=tr("T:%1", "Teacher").arg(this->teacher);s+=", ";
27147 	s+=tr("FAT:%1", "First activity tag").arg(this->firstActivityTag);s+=", ";
27148 	s+=tr("SAT:%1", "Second activity tag").arg(this->secondActivityTag);s+=", ";
27149 	s+=tr("mG:%1", "Min gaps").arg(this->minGaps);
27150 
27151 	return begin+s+end;
27152 }
27153 
getDetailedDescription(Rules & r)27154 QString ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::getDetailedDescription(Rules& r)
27155 {
27156 	Q_UNUSED(r);
27157 
27158 	QString s=tr("Time constraint");s+="\n";
27159 	s+=tr("A teacher must respect the minimum gaps between an ordered pair of activity tags");s+="\n";
27160 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
27161 	s+=tr("Teacher=%1").arg(this->teacher);s+="\n";
27162 	s+=tr("First activity tag=%1").arg(this->firstActivityTag);s+="\n";
27163 	s+=tr("Second activity tag=%1").arg(this->secondActivityTag);s+="\n";
27164 	s+=tr("Minimum gaps=%1").arg(this->minGaps);s+="\n";
27165 
27166 	if(!active){
27167 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
27168 		s+="\n";
27169 	}
27170 	if(!comments.isEmpty()){
27171 		s+=tr("Comments=%1").arg(comments);
27172 		s+="\n";
27173 	}
27174 
27175 	return s;
27176 }
27177 
computeInternalStructure(QWidget * parent,Rules & r)27178 bool ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::computeInternalStructure(QWidget* parent, Rules& r)
27179 {
27180 	_firstActivityTagIndex=r.activityTagsHash.value(firstActivityTag, -1);
27181 	assert(this->_firstActivityTagIndex>=0);
27182 
27183 	_secondActivityTagIndex=r.activityTagsHash.value(secondActivityTag, -1);
27184 	assert(this->_secondActivityTagIndex>=0);
27185 
27186 	int teacherIndex=r.teachersHash.value(teacher, -1);
27187 
27188 	if(teacherIndex<0){
27189 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
27190 		 tr("Constraint teacher min gaps between ordered pair of activity tags is wrong because it refers to inexistent teacher."
27191 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
27192 
27193 		return false;
27194 	}
27195 
27196 	/////////////
27197 	this->canonicalTeachersList.clear();
27198 
27199 	bool foundF=false; //found first
27200 	bool foundS=false; //found second
27201 
27202 	Teacher* tch=r.internalTeachersList[teacherIndex];
27203 
27204 	for(int actIndex : qAsConst(tch->activitiesForTeacher)){
27205 		if(!foundF)
27206 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->_firstActivityTagIndex))
27207 				foundF=true;
27208 		if(!foundS)
27209 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->_secondActivityTagIndex))
27210 				foundS=true;
27211 
27212 		if(foundF && foundS)
27213 			break;
27214 	}
27215 
27216 	if(foundF && foundS)
27217 		this->canonicalTeachersList.append(teacherIndex);
27218 
27219 	return true;
27220 }
27221 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)27222 double ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
27223 {
27224 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
27225 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
27226 		c.teachersMatrixReady=true;
27227 		c.subgroupsMatrixReady=true;
27228 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
27229 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
27230 
27231 		c.changedForMatrixCalculation=false;
27232 	}
27233 
27234 	int nbroken=0;
27235 
27236 	Matrix2D<int> crtTeacherTimetableActivityTag;
27237 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
27238 
27239 	for(int i : qAsConst(this->canonicalTeachersList)){
27240 		Teacher* tch=r.internalTeachersList[i];
27241 
27242 		for(int d=0; d<r.nDaysPerWeek; d++)
27243 			for(int h=0; h<r.nHoursPerDay; h++)
27244 				crtTeacherTimetableActivityTag[d][h]=-1;
27245 
27246 		for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
27247 			int d=c.times[ai]%r.nDaysPerWeek;
27248 			int h=c.times[ai]/r.nDaysPerWeek;
27249 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
27250 				assert(h+dur<r.nHoursPerDay);
27251 				assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
27252 
27253 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->_firstActivityTagIndex)){
27254 					assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
27255 					crtTeacherTimetableActivityTag[d][h+dur]=this->_firstActivityTagIndex;
27256 				}
27257 				else if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->_secondActivityTagIndex)){
27258 					assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
27259 					crtTeacherTimetableActivityTag[d][h+dur]=this->_secondActivityTagIndex;
27260 				}
27261 			}
27262 		}
27263 
27264 		for(int d=0; d<r.nDaysPerWeek; d++){
27265 			int cnt=-1;
27266 			for(int h=0; h<r.nHoursPerDay; h++){
27267 				if(crtTeacherTimetableActivityTag[d][h]==_firstActivityTagIndex){
27268 					cnt=0;
27269 				}
27270 				else if(crtTeacherTimetableActivityTag[d][h]==-1){
27271 					if(cnt>=0)
27272 						cnt++;
27273 				}
27274 				else if(crtTeacherTimetableActivityTag[d][h]==_secondActivityTagIndex){
27275 					if(cnt>=0 && cnt<minGaps){
27276 						nbroken++;
27277 
27278 						if(conflictsString!=nullptr){
27279 							QString s=tr("Time constraint teacher min %1 gaps between ordered pair of activity tags broken for teacher: %2,"
27280 							 " day: %3, real gaps=%4, conflicts increase=%5")
27281 							 .arg(minGaps)
27282 							 .arg(tch->name)
27283 							 .arg(r.daysOfTheWeek[d])
27284 							 .arg(CustomFETString::number(cnt))
27285 							 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
27286 
27287 							dl.append(s);
27288 							cl.append(1*weightPercentage/100);
27289 
27290 							*conflictsString+= s+"\n";
27291 						}
27292 					}
27293 
27294 					cnt=-1;
27295 				}
27296 				else{
27297 					assert(0);
27298 				}
27299 			}
27300 		}
27301 	}
27302 
27303 	if(weightPercentage==100)
27304 		assert(nbroken==0);
27305 
27306 	return nbroken * weightPercentage / 100.0;
27307 }
27308 
isRelatedToActivity(Rules & r,Activity * a)27309 bool ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::isRelatedToActivity(Rules& r, Activity* a)
27310 {
27311 	Q_UNUSED(r);
27312 	Q_UNUSED(a);
27313 
27314 	return false;
27315 }
27316 
isRelatedToTeacher(Teacher * t)27317 bool ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::isRelatedToTeacher(Teacher* t)
27318 {
27319 	if(t->name==this->teacher)
27320 		return true;
27321 
27322 	return false;
27323 }
27324 
isRelatedToSubject(Subject * s)27325 bool ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::isRelatedToSubject(Subject* s)
27326 {
27327 	Q_UNUSED(s);
27328 
27329 	return false;
27330 }
27331 
isRelatedToActivityTag(ActivityTag * s)27332 bool ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::isRelatedToActivityTag(ActivityTag* s)
27333 {
27334 	if(s->name==this->firstActivityTag || s->name==this->secondActivityTag)
27335 		return true;
27336 
27337 	return false;
27338 }
27339 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)27340 bool ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
27341 {
27342 	Q_UNUSED(r);
27343 	Q_UNUSED(s);
27344 
27345 	return false;
27346 }
27347 
hasWrongDayOrHour(Rules & r)27348 bool ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::hasWrongDayOrHour(Rules& r)
27349 {
27350 	if(minGaps>r.nHoursPerDay)
27351 		return true;
27352 
27353 	return false;
27354 }
27355 
canRepairWrongDayOrHour(Rules & r)27356 bool ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::canRepairWrongDayOrHour(Rules& r)
27357 {
27358 	assert(hasWrongDayOrHour(r));
27359 
27360 	return true;
27361 }
27362 
repairWrongDayOrHour(Rules & r)27363 bool ConstraintTeacherMinGapsBetweenOrderedPairOfActivityTags::repairWrongDayOrHour(Rules& r)
27364 {
27365 	assert(hasWrongDayOrHour(r));
27366 
27367 	if(minGaps>r.nHoursPerDay)
27368 		minGaps=r.nHoursPerDay;
27369 
27370 	return true;
27371 }
27372 
27373 ////////////////////////////////////////////////////////////////////////////////////////////
27374 ////////////////////////////////////////////////////////////////////////////////////////////
27375 
ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags()27376 ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags()
27377 	: TimeConstraint()
27378 {
27379 	this->type = CONSTRAINT_TEACHERS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS;
27380 
27381 	this->minGaps = 0;
27382 	this->firstActivityTag=QString("");
27383 	this->secondActivityTag=QString("");
27384 }
27385 
ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags(double wp,int _minGaps,const QString & _firstActivityTag,const QString & _secondActivityTag)27386 ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags(double wp, int _minGaps, const QString& _firstActivityTag, const QString& _secondActivityTag)
27387 	: TimeConstraint(wp)
27388 {
27389 	this->type = CONSTRAINT_TEACHERS_MIN_GAPS_BETWEEN_ORDERED_PAIR_OF_ACTIVITY_TAGS;
27390 
27391 	this->minGaps = _minGaps;
27392 	this->firstActivityTag=_firstActivityTag;
27393 	this->secondActivityTag=_secondActivityTag;
27394 }
27395 
hasInactiveActivities(Rules & r)27396 bool ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::hasInactiveActivities(Rules& r)
27397 {
27398 	Q_UNUSED(r);
27399 	return false;
27400 }
27401 
getXmlDescription(Rules & r)27402 QString ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::getXmlDescription(Rules& r)
27403 {
27404 	Q_UNUSED(r);
27405 
27406 	QString s="<ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags>\n";
27407 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
27408 	s+="	<First_Activity_Tag>"+protect(this->firstActivityTag)+"</First_Activity_Tag>\n";
27409 	s+="	<Second_Activity_Tag>"+protect(this->secondActivityTag)+"</Second_Activity_Tag>\n";
27410 	s+="	<MinGaps>"+CustomFETString::number(this->minGaps)+"</MinGaps>\n";
27411 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
27412 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
27413 	s+="</ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags>\n";
27414 	return s;
27415 }
27416 
getDescription(Rules & r)27417 QString ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::getDescription(Rules& r)
27418 {
27419 	Q_UNUSED(r);
27420 
27421 	QString begin=QString("");
27422 	if(!active)
27423 		begin="X - ";
27424 
27425 	QString end=QString("");
27426 	if(!comments.isEmpty())
27427 		end=", "+tr("C: %1", "Comments").arg(comments);
27428 
27429 	QString s;
27430 
27431 	s+=tr("Teachers min gaps between ordered pair of activity tags");s+=", ";
27432 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
27433 	s+=tr("FAT:%1", "First activity tag").arg(this->firstActivityTag);s+=", ";
27434 	s+=tr("SAT:%1", "Second activity tag").arg(this->secondActivityTag);s+=", ";
27435 	s+=tr("mG:%1", "Min gaps").arg(this->minGaps);
27436 
27437 	return begin+s+end;
27438 }
27439 
getDetailedDescription(Rules & r)27440 QString ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::getDetailedDescription(Rules& r)
27441 {
27442 	Q_UNUSED(r);
27443 
27444 	QString s=tr("Time constraint");s+="\n";
27445 	s+=tr("All teachers must respect the minimum gaps between an ordered pair of activity tags");s+="\n";
27446 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
27447 	s+=tr("First activity tag=%1").arg(this->firstActivityTag);s+="\n";
27448 	s+=tr("Second activity tag=%1").arg(this->secondActivityTag);s+="\n";
27449 	s+=tr("Minimum gaps=%1").arg(this->minGaps);s+="\n";
27450 
27451 	if(!active){
27452 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
27453 		s+="\n";
27454 	}
27455 	if(!comments.isEmpty()){
27456 		s+=tr("Comments=%1").arg(comments);
27457 		s+="\n";
27458 	}
27459 
27460 	return s;
27461 }
27462 
computeInternalStructure(QWidget * parent,Rules & r)27463 bool ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::computeInternalStructure(QWidget* parent, Rules& r)
27464 {
27465 	Q_UNUSED(parent);
27466 
27467 	_firstActivityTagIndex=r.activityTagsHash.value(firstActivityTag, -1);
27468 	assert(this->_firstActivityTagIndex>=0);
27469 
27470 	_secondActivityTagIndex=r.activityTagsHash.value(secondActivityTag, -1);
27471 	assert(this->_secondActivityTagIndex>=0);
27472 
27473 	/////////////
27474 	this->canonicalTeachersList.clear();
27475 
27476 	for(int teacherIndex=0; teacherIndex<r.nInternalTeachers; teacherIndex++){
27477 		bool foundF=false; //found first
27478 		bool foundS=false; //found second
27479 
27480 		Teacher* tch=r.internalTeachersList[teacherIndex];
27481 
27482 		for(int actIndex : qAsConst(tch->activitiesForTeacher)){
27483 			if(!foundF)
27484 				if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->_firstActivityTagIndex))
27485 					foundF=true;
27486 			if(!foundS)
27487 				if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->_secondActivityTagIndex))
27488 					foundS=true;
27489 
27490 			if(foundF && foundS)
27491 				break;
27492 		}
27493 		if(foundF && foundS)
27494 			this->canonicalTeachersList.append(teacherIndex);
27495 	}
27496 
27497 	return true;
27498 }
27499 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)27500 double ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
27501 {
27502 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
27503 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
27504 		c.teachersMatrixReady=true;
27505 		c.subgroupsMatrixReady=true;
27506 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
27507 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
27508 
27509 		c.changedForMatrixCalculation=false;
27510 	}
27511 
27512 	int nbroken=0;
27513 
27514 	Matrix2D<int> crtTeacherTimetableActivityTag;
27515 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
27516 
27517 	for(int i : qAsConst(this->canonicalTeachersList)){
27518 		Teacher* tch=r.internalTeachersList[i];
27519 
27520 		for(int d=0; d<r.nDaysPerWeek; d++)
27521 			for(int h=0; h<r.nHoursPerDay; h++)
27522 				crtTeacherTimetableActivityTag[d][h]=-1;
27523 
27524 		for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
27525 			int d=c.times[ai]%r.nDaysPerWeek;
27526 			int h=c.times[ai]/r.nDaysPerWeek;
27527 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
27528 				assert(h+dur<r.nHoursPerDay);
27529 				assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
27530 
27531 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->_firstActivityTagIndex)){
27532 					assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
27533 					crtTeacherTimetableActivityTag[d][h+dur]=this->_firstActivityTagIndex;
27534 				}
27535 				else if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->_secondActivityTagIndex)){
27536 					assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
27537 					crtTeacherTimetableActivityTag[d][h+dur]=this->_secondActivityTagIndex;
27538 				}
27539 			}
27540 		}
27541 
27542 		for(int d=0; d<r.nDaysPerWeek; d++){
27543 			int cnt=-1;
27544 			for(int h=0; h<r.nHoursPerDay; h++){
27545 				if(crtTeacherTimetableActivityTag[d][h]==_firstActivityTagIndex){
27546 					cnt=0;
27547 				}
27548 				else if(crtTeacherTimetableActivityTag[d][h]==-1){
27549 					if(cnt>=0)
27550 						cnt++;
27551 				}
27552 				else if(crtTeacherTimetableActivityTag[d][h]==_secondActivityTagIndex){
27553 					if(cnt>=0 && cnt<minGaps){
27554 						nbroken++;
27555 
27556 						if(conflictsString!=nullptr){
27557 							QString s=tr("Time constraint teachers min %1 gaps between ordered pair of activity tags broken for teacher: %2,"
27558 							 " day: %3, real gaps=%4, conflicts increase=%5")
27559 							 .arg(minGaps)
27560 							 .arg(tch->name)
27561 							 .arg(r.daysOfTheWeek[d])
27562 							 .arg(CustomFETString::number(cnt))
27563 							 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
27564 
27565 							dl.append(s);
27566 							cl.append(1*weightPercentage/100);
27567 
27568 							*conflictsString+= s+"\n";
27569 						}
27570 					}
27571 
27572 					cnt=-1;
27573 				}
27574 				else{
27575 					assert(0);
27576 				}
27577 			}
27578 		}
27579 	}
27580 
27581 	if(weightPercentage==100)
27582 		assert(nbroken==0);
27583 
27584 	return nbroken * weightPercentage / 100.0;
27585 }
27586 
isRelatedToActivity(Rules & r,Activity * a)27587 bool ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::isRelatedToActivity(Rules& r, Activity* a)
27588 {
27589 	Q_UNUSED(r);
27590 	Q_UNUSED(a);
27591 
27592 	return false;
27593 }
27594 
isRelatedToTeacher(Teacher * t)27595 bool ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::isRelatedToTeacher(Teacher* t)
27596 {
27597 	Q_UNUSED(t);
27598 
27599 	return true;
27600 }
27601 
isRelatedToSubject(Subject * s)27602 bool ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::isRelatedToSubject(Subject* s)
27603 {
27604 	Q_UNUSED(s);
27605 
27606 	return false;
27607 }
27608 
isRelatedToActivityTag(ActivityTag * s)27609 bool ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::isRelatedToActivityTag(ActivityTag* s)
27610 {
27611 	if(s->name==this->firstActivityTag || s->name==this->secondActivityTag)
27612 		return true;
27613 
27614 	return false;
27615 }
27616 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)27617 bool ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
27618 {
27619 	Q_UNUSED(r);
27620 	Q_UNUSED(s);
27621 
27622 	return false;
27623 }
27624 
hasWrongDayOrHour(Rules & r)27625 bool ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::hasWrongDayOrHour(Rules& r)
27626 {
27627 	if(minGaps>r.nHoursPerDay)
27628 		return true;
27629 
27630 	return false;
27631 }
27632 
canRepairWrongDayOrHour(Rules & r)27633 bool ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::canRepairWrongDayOrHour(Rules& r)
27634 {
27635 	assert(hasWrongDayOrHour(r));
27636 
27637 	return true;
27638 }
27639 
repairWrongDayOrHour(Rules & r)27640 bool ConstraintTeachersMinGapsBetweenOrderedPairOfActivityTags::repairWrongDayOrHour(Rules& r)
27641 {
27642 	assert(hasWrongDayOrHour(r));
27643 
27644 	if(minGaps>r.nHoursPerDay)
27645 		minGaps=r.nHoursPerDay;
27646 
27647 	return true;
27648 }
27649 
27650 ///////////////////////////////////////////////////////////////////////////////////////////
27651 ///////////////////////////////////////////////////////////////////////////////////////////
27652 
27653 //For mornings-afternoons
27654 
27655 ///////////////////////////////////////////////////////////////////////////////////////////
27656 ///////////////////////////////////////////////////////////////////////////////////////////
27657 
ConstraintTeachersMaxHoursDailyRealDays()27658 ConstraintTeachersMaxHoursDailyRealDays::ConstraintTeachersMaxHoursDailyRealDays()
27659 	: TimeConstraint()
27660 {
27661 	this->type=CONSTRAINT_TEACHERS_MAX_HOURS_DAILY_REAL_DAYS;
27662 }
27663 
ConstraintTeachersMaxHoursDailyRealDays(double wp,int maxhours)27664 ConstraintTeachersMaxHoursDailyRealDays::ConstraintTeachersMaxHoursDailyRealDays(double wp, int maxhours)
27665  : TimeConstraint(wp)
27666  {
27667 	assert(maxhours>0);
27668 	this->maxHoursDaily=maxhours;
27669 
27670 	this->type=CONSTRAINT_TEACHERS_MAX_HOURS_DAILY_REAL_DAYS;
27671 }
27672 
computeInternalStructure(QWidget * parent,Rules & r)27673 bool ConstraintTeachersMaxHoursDailyRealDays::computeInternalStructure(QWidget* parent, Rules& r)
27674 {
27675 	Q_UNUSED(parent);
27676 	Q_UNUSED(r);
27677 
27678 	return true;
27679 }
27680 
hasInactiveActivities(Rules & r)27681 bool ConstraintTeachersMaxHoursDailyRealDays::hasInactiveActivities(Rules& r)
27682 {
27683 	Q_UNUSED(r);
27684 	return false;
27685 }
27686 
getXmlDescription(Rules & r)27687 QString ConstraintTeachersMaxHoursDailyRealDays::getXmlDescription(Rules& r){
27688 	Q_UNUSED(r);
27689 
27690 	QString s="<ConstraintTeachersMaxHoursDailyRealDays>\n";
27691 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
27692 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
27693 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
27694 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
27695 	s+="</ConstraintTeachersMaxHoursDailyRealDays>\n";
27696 	return s;
27697 }
27698 
getDescription(Rules & r)27699 QString ConstraintTeachersMaxHoursDailyRealDays::getDescription(Rules& r){
27700 	Q_UNUSED(r);
27701 
27702 	QString begin=QString("");
27703 	if(!active)
27704 		begin="X - ";
27705 
27706 	QString end=QString("");
27707 	if(!comments.isEmpty())
27708 		end=", "+tr("C: %1", "Comments").arg(comments);
27709 
27710 	QString s;
27711 	s+=tr("Teachers max hours daily per real day"), s+=", ";
27712 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
27713 	s+=tr("MH:%1", "Maximum hours (daily)").arg(this->maxHoursDaily);
27714 
27715 	return begin+s+end;
27716 }
27717 
getDetailedDescription(Rules & r)27718 QString ConstraintTeachersMaxHoursDailyRealDays::getDetailedDescription(Rules& r){
27719 	Q_UNUSED(r);
27720 
27721 	QString s=tr("Time constraint");s+="\n";
27722 	s+=tr("All teachers must respect the maximum number of hours daily per real day");s+="\n";
27723 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
27724 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
27725 
27726 	if(!active){
27727 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
27728 		s+="\n";
27729 	}
27730 	if(!comments.isEmpty()){
27731 		s+=tr("Comments=%1").arg(comments);
27732 		s+="\n";
27733 	}
27734 
27735 	return s;
27736 }
27737 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)27738 double ConstraintTeachersMaxHoursDailyRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
27739 {
27740 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
27741 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
27742 		c.teachersMatrixReady=true;
27743 		c.subgroupsMatrixReady=true;
27744 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
27745 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
27746 
27747 		c.changedForMatrixCalculation=false;
27748 	}
27749 
27750 	int nbroken;
27751 
27752 	//without logging
27753 	if(conflictsString==nullptr){
27754 		nbroken=0;
27755 		for(int i=0; i<r.nInternalTeachers; i++){
27756 			for(int d=0; d<r.nDaysPerWeek/2; d++){
27757 				int n_hours_daily=0;
27758 				for(int h=0; h<r.nHoursPerDay; h++)
27759 					if(teachersMatrix[i][2*d][h]>0)
27760 						n_hours_daily++;
27761 				for(int h=0; h<r.nHoursPerDay; h++)
27762 					if(teachersMatrix[i][2*d+1][h]>0)
27763 						n_hours_daily++;
27764 
27765 				if(n_hours_daily>this->maxHoursDaily)
27766 					nbroken++;
27767 			}
27768 		}
27769 	}
27770 	//with logging
27771 	else{
27772 		nbroken=0;
27773 		for(int i=0; i<r.nInternalTeachers; i++){
27774 			for(int d=0; d<r.nDaysPerWeek/2; d++){
27775 				int n_hours_daily=0;
27776 				for(int h=0; h<r.nHoursPerDay; h++)
27777 					if(teachersMatrix[i][2*d][h]>0)
27778 						n_hours_daily++;
27779 				for(int h=0; h<r.nHoursPerDay; h++)
27780 					if(teachersMatrix[i][2*d+1][h]>0)
27781 						n_hours_daily++;
27782 
27783 				if(n_hours_daily>this->maxHoursDaily){
27784 					nbroken++;
27785 
27786 					if(conflictsString!=nullptr){
27787 						QString s=(tr(
27788 						 "Time constraint teachers max %1 hours daily per real day broken for teacher %2, on real day %3, length=%4.")
27789 						 .arg(CustomFETString::number(this->maxHoursDaily))
27790 						 .arg(r.internalTeachersList[i]->name)
27791 						 .arg(d/*r.daysOfTheWeek[d]*/)
27792 						 .arg(n_hours_daily)
27793 						 )
27794 						 +
27795 						 " "
27796 						 +
27797 						 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
27798 
27799 						dl.append(s);
27800 						cl.append(weightPercentage/100);
27801 
27802 						*conflictsString+= s+"\n";
27803 					}
27804 				}
27805 			}
27806 		}
27807 	}
27808 
27809 	if(weightPercentage==100)
27810 		assert(nbroken==0);
27811 	return weightPercentage/100 * nbroken;
27812 }
27813 
isRelatedToActivity(Rules & r,Activity * a)27814 bool ConstraintTeachersMaxHoursDailyRealDays::isRelatedToActivity(Rules& r, Activity* a)
27815 {
27816 	Q_UNUSED(r);
27817 	Q_UNUSED(a);
27818 
27819 	return false;
27820 }
27821 
isRelatedToTeacher(Teacher * t)27822 bool ConstraintTeachersMaxHoursDailyRealDays::isRelatedToTeacher(Teacher* t)
27823 {
27824 	Q_UNUSED(t);
27825 
27826 	return true;
27827 }
27828 
isRelatedToSubject(Subject * s)27829 bool ConstraintTeachersMaxHoursDailyRealDays::isRelatedToSubject(Subject* s)
27830 {
27831 	Q_UNUSED(s);
27832 
27833 	return false;
27834 }
27835 
isRelatedToActivityTag(ActivityTag * s)27836 bool ConstraintTeachersMaxHoursDailyRealDays::isRelatedToActivityTag(ActivityTag* s)
27837 {
27838 	Q_UNUSED(s);
27839 
27840 	return false;
27841 }
27842 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)27843 bool ConstraintTeachersMaxHoursDailyRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
27844 {
27845 	Q_UNUSED(r);
27846 	Q_UNUSED(s);
27847 
27848 	return false;
27849 }
27850 
hasWrongDayOrHour(Rules & r)27851 bool ConstraintTeachersMaxHoursDailyRealDays::hasWrongDayOrHour(Rules& r)
27852 {
27853 	if(maxHoursDaily>2*r.nHoursPerDay)
27854 		return true;
27855 
27856 	return false;
27857 }
27858 
canRepairWrongDayOrHour(Rules & r)27859 bool ConstraintTeachersMaxHoursDailyRealDays::canRepairWrongDayOrHour(Rules& r)
27860 {
27861 	assert(hasWrongDayOrHour(r));
27862 
27863 	return true;
27864 }
27865 
repairWrongDayOrHour(Rules & r)27866 bool ConstraintTeachersMaxHoursDailyRealDays::repairWrongDayOrHour(Rules& r)
27867 {
27868 	assert(hasWrongDayOrHour(r));
27869 
27870 	if(maxHoursDaily>2*r.nHoursPerDay)
27871 		maxHoursDaily=2*r.nHoursPerDay;
27872 
27873 	return true;
27874 }
27875 
27876 ///////////////////////////////////////////////////////////////////////////////////////////
27877 ///////////////////////////////////////////////////////////////////////////////////////////
27878 
ConstraintTeacherMaxHoursDailyRealDays()27879 ConstraintTeacherMaxHoursDailyRealDays::ConstraintTeacherMaxHoursDailyRealDays()
27880 	: TimeConstraint()
27881 {
27882 	this->type=CONSTRAINT_TEACHER_MAX_HOURS_DAILY_REAL_DAYS;
27883 }
27884 
ConstraintTeacherMaxHoursDailyRealDays(double wp,int maxhours,const QString & teacher)27885 ConstraintTeacherMaxHoursDailyRealDays::ConstraintTeacherMaxHoursDailyRealDays(double wp, int maxhours, const QString& teacher)
27886  : TimeConstraint(wp)
27887  {
27888 	assert(maxhours>0);
27889 	this->maxHoursDaily=maxhours;
27890 	this->teacherName=teacher;
27891 
27892 	this->type=CONSTRAINT_TEACHER_MAX_HOURS_DAILY_REAL_DAYS;
27893 }
27894 
computeInternalStructure(QWidget * parent,Rules & r)27895 bool ConstraintTeacherMaxHoursDailyRealDays::computeInternalStructure(QWidget* parent, Rules& r)
27896 {
27897 	Q_UNUSED(parent);
27898 
27899 	//this->teacher_ID=r.searchTeacher(this->teacherName);
27900 	teacher_ID=r.teachersHash.value(teacherName, -1);
27901 	assert(this->teacher_ID>=0);
27902 	return true;
27903 }
27904 
hasInactiveActivities(Rules & r)27905 bool ConstraintTeacherMaxHoursDailyRealDays::hasInactiveActivities(Rules& r)
27906 {
27907 	Q_UNUSED(r);
27908 	return false;
27909 }
27910 
getXmlDescription(Rules & r)27911 QString ConstraintTeacherMaxHoursDailyRealDays::getXmlDescription(Rules& r){
27912 	Q_UNUSED(r);
27913 
27914 	QString s="<ConstraintTeacherMaxHoursDailyRealDays>\n";
27915 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
27916 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
27917 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
27918 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
27919 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
27920 	s+="</ConstraintTeacherMaxHoursDailyRealDays>\n";
27921 	return s;
27922 }
27923 
getDescription(Rules & r)27924 QString ConstraintTeacherMaxHoursDailyRealDays::getDescription(Rules& r){
27925 	Q_UNUSED(r);
27926 
27927 	QString begin=QString("");
27928 	if(!active)
27929 		begin="X - ";
27930 
27931 	QString end=QString("");
27932 	if(!comments.isEmpty())
27933 		end=", "+tr("C: %1", "Comments").arg(comments);
27934 
27935 	QString s;
27936 	s+=tr("Teacher max hours daily per real day");s+=", ";
27937 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
27938 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
27939 	s+=tr("MH:%1", "Maximum hours (daily)").arg(this->maxHoursDaily);
27940 
27941 	return begin+s+end;
27942 }
27943 
getDetailedDescription(Rules & r)27944 QString ConstraintTeacherMaxHoursDailyRealDays::getDetailedDescription(Rules& r){
27945 	Q_UNUSED(r);
27946 
27947 	QString s=tr("Time constraint");s+="\n";
27948 	s+=tr("A teacher must respect the maximum number of hours daily per real day");s+="\n";
27949 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
27950 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
27951 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
27952 
27953 	if(!active){
27954 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
27955 		s+="\n";
27956 	}
27957 	if(!comments.isEmpty()){
27958 		s+=tr("Comments=%1").arg(comments);
27959 		s+="\n";
27960 	}
27961 
27962 	return s;
27963 }
27964 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)27965 double ConstraintTeacherMaxHoursDailyRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
27966 {
27967 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
27968 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
27969 		c.teachersMatrixReady=true;
27970 		c.subgroupsMatrixReady=true;
27971 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
27972 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
27973 
27974 		c.changedForMatrixCalculation=false;
27975 	}
27976 
27977 	int nbroken;
27978 
27979 	//without logging
27980 	if(conflictsString==nullptr){
27981 		nbroken=0;
27982 		int i=this->teacher_ID;
27983 		for(int d=0; d<r.nDaysPerWeek/2; d++){
27984 			int n_hours_daily=0;
27985 			for(int h=0; h<r.nHoursPerDay; h++)
27986 				if(teachersMatrix[i][2*d][h]>0)
27987 					n_hours_daily++;
27988 			for(int h=0; h<r.nHoursPerDay; h++)
27989 				if(teachersMatrix[i][2*d+1][h]>0)
27990 					n_hours_daily++;
27991 
27992 			if(n_hours_daily>this->maxHoursDaily){
27993 				nbroken++;
27994 			}
27995 		}
27996 	}
27997 	//with logging
27998 	else{
27999 		nbroken=0;
28000 		int i=this->teacher_ID;
28001 		for(int d=0; d<r.nDaysPerWeek/2; d++){
28002 			int n_hours_daily=0;
28003 			for(int h=0; h<r.nHoursPerDay; h++)
28004 				if(teachersMatrix[i][2*d][h]>0)
28005 					n_hours_daily++;
28006 			for(int h=0; h<r.nHoursPerDay; h++)
28007 				if(teachersMatrix[i][2*d+1][h]>0)
28008 					n_hours_daily++;
28009 
28010 			if(n_hours_daily>this->maxHoursDaily){
28011 				nbroken++;
28012 
28013 				if(conflictsString!=nullptr){
28014 					QString s=(tr(
28015 					 "Time constraint teacher max %1 hours daily per real day broken for teacher %2, on real day %3, length=%4.")
28016 					 .arg(CustomFETString::number(this->maxHoursDaily))
28017 					 .arg(r.internalTeachersList[i]->name)
28018 					 .arg(d/*r.daysOfTheWeek[d]*/)
28019 					 .arg(n_hours_daily)
28020 					 )
28021 					 +" "
28022 					 +
28023 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
28024 
28025 					dl.append(s);
28026 					cl.append(weightPercentage/100);
28027 
28028 					*conflictsString+= s+"\n";
28029 				}
28030 			}
28031 		}
28032 	}
28033 
28034 	if(weightPercentage==100)
28035 		assert(nbroken==0);
28036 	return weightPercentage/100 * nbroken;
28037 }
28038 
isRelatedToActivity(Rules & r,Activity * a)28039 bool ConstraintTeacherMaxHoursDailyRealDays::isRelatedToActivity(Rules& r, Activity* a)
28040 {
28041 	Q_UNUSED(r);
28042 	Q_UNUSED(a);
28043 
28044 	return false;
28045 }
28046 
isRelatedToTeacher(Teacher * t)28047 bool ConstraintTeacherMaxHoursDailyRealDays::isRelatedToTeacher(Teacher* t)
28048 {
28049 	if(this->teacherName==t->name)
28050 		return true;
28051 	return false;
28052 }
28053 
isRelatedToSubject(Subject * s)28054 bool ConstraintTeacherMaxHoursDailyRealDays::isRelatedToSubject(Subject* s)
28055 {
28056 	Q_UNUSED(s);
28057 
28058 	return false;
28059 }
28060 
isRelatedToActivityTag(ActivityTag * s)28061 bool ConstraintTeacherMaxHoursDailyRealDays::isRelatedToActivityTag(ActivityTag* s)
28062 {
28063 	Q_UNUSED(s);
28064 
28065 	return false;
28066 }
28067 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)28068 bool ConstraintTeacherMaxHoursDailyRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
28069 {
28070 	Q_UNUSED(r);
28071 	Q_UNUSED(s);
28072 
28073 	return false;
28074 }
28075 
hasWrongDayOrHour(Rules & r)28076 bool ConstraintTeacherMaxHoursDailyRealDays::hasWrongDayOrHour(Rules& r)
28077 {
28078 	if(maxHoursDaily>2*r.nHoursPerDay)
28079 		return true;
28080 
28081 	return false;
28082 }
28083 
canRepairWrongDayOrHour(Rules & r)28084 bool ConstraintTeacherMaxHoursDailyRealDays::canRepairWrongDayOrHour(Rules& r)
28085 {
28086 	assert(hasWrongDayOrHour(r));
28087 
28088 	return true;
28089 }
28090 
repairWrongDayOrHour(Rules & r)28091 bool ConstraintTeacherMaxHoursDailyRealDays::repairWrongDayOrHour(Rules& r)
28092 {
28093 	assert(hasWrongDayOrHour(r));
28094 
28095 	if(maxHoursDaily>2*r.nHoursPerDay)
28096 		maxHoursDaily=2*r.nHoursPerDay;
28097 
28098 	return true;
28099 }
28100 
28101 ///////////////////////////////////////////////////////////////////////////////////////////
28102 ///////////////////////////////////////////////////////////////////////////////////////////
28103 
ConstraintTeacherMaxRealDaysPerWeek()28104 ConstraintTeacherMaxRealDaysPerWeek::ConstraintTeacherMaxRealDaysPerWeek()
28105 	: TimeConstraint()
28106 {
28107 	this->type=CONSTRAINT_TEACHER_MAX_REAL_DAYS_PER_WEEK;
28108 }
28109 
ConstraintTeacherMaxRealDaysPerWeek(double wp,int maxnd,QString tn)28110 ConstraintTeacherMaxRealDaysPerWeek::ConstraintTeacherMaxRealDaysPerWeek(double wp, int maxnd, QString tn)
28111 	 : TimeConstraint(wp)
28112 {
28113 	this->teacherName = tn;
28114 	this->maxDaysPerWeek=maxnd;
28115 	this->type=CONSTRAINT_TEACHER_MAX_REAL_DAYS_PER_WEEK;
28116 }
28117 
computeInternalStructure(QWidget * parent,Rules & r)28118 bool ConstraintTeacherMaxRealDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
28119 {
28120 	Q_UNUSED(parent);
28121 
28122 	//this->teacher_ID=r.searchTeacher(this->teacherName);
28123 	teacher_ID=r.teachersHash.value(teacherName, -1);
28124 	assert(this->teacher_ID>=0);
28125 	return true;
28126 }
28127 
hasInactiveActivities(Rules & r)28128 bool ConstraintTeacherMaxRealDaysPerWeek::hasInactiveActivities(Rules& r)
28129 {
28130 	Q_UNUSED(r);
28131 	return false;
28132 }
28133 
getXmlDescription(Rules & r)28134 QString ConstraintTeacherMaxRealDaysPerWeek::getXmlDescription(Rules& r)
28135 {
28136 	Q_UNUSED(r);
28137 
28138 	QString s="<ConstraintTeacherMaxRealDaysPerWeek>\n";
28139 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
28140 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
28141 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
28142 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
28143 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
28144 	s+="</ConstraintTeacherMaxRealDaysPerWeek>\n";
28145 	return s;
28146 }
28147 
getDescription(Rules & r)28148 QString ConstraintTeacherMaxRealDaysPerWeek::getDescription(Rules& r){
28149 	Q_UNUSED(r);
28150 
28151 	QString begin=QString("");
28152 	if(!active)
28153 		begin="X - ";
28154 
28155 	QString end=QString("");
28156 	if(!comments.isEmpty())
28157 		end=", "+tr("C: %1", "Comments").arg(comments);
28158 
28159 	QString s=tr("Teacher max real days per week");s+=", ";
28160 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
28161 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
28162 	s+=tr("MD:%1", "Max days (per week)").arg(this->maxDaysPerWeek);
28163 
28164 	return begin+s+end;
28165 }
28166 
getDetailedDescription(Rules & r)28167 QString ConstraintTeacherMaxRealDaysPerWeek::getDetailedDescription(Rules& r){
28168 	Q_UNUSED(r);
28169 
28170 	QString s=tr("Time constraint");s+="\n";
28171 	s+=tr("A teacher must respect the maximum number of real days per week");s+="\n";
28172 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
28173 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
28174 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
28175 
28176 	if(!active){
28177 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
28178 		s+="\n";
28179 	}
28180 	if(!comments.isEmpty()){
28181 		s+=tr("Comments=%1").arg(comments);
28182 		s+="\n";
28183 	}
28184 
28185 	return s;
28186 }
28187 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)28188 double ConstraintTeacherMaxRealDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
28189 {
28190 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
28191 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
28192 		c.teachersMatrixReady=true;
28193 		c.subgroupsMatrixReady=true;
28194 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
28195 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
28196 
28197 		c.changedForMatrixCalculation=false;
28198 	}
28199 
28200 	int nbroken;
28201 
28202 	//without logging
28203 	/*
28204 	if(conflictsString==nullptr){
28205 		nbroken=0;
28206 		//count sort
28207 		int t=this->teacher_ID;
28208 		int nd[MAX_HOURS_PER_DAY + 1];
28209 		for(int h=0; h<=r.nHoursPerDay; h++)
28210 			nd[h]=0;
28211 		for(int d=0; d<r.nDaysPerWeek; d++){
28212 			int nh=0;
28213 			for(int h=0; h<r.nHoursPerDay; h++)
28214 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
28215 			nd[nh]++;
28216 		}
28217 		//return the minimum occupied days which do not respect this constraint
28218 		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
28219 		for(int k=0; k<=r.nHoursPerDay; k++){
28220 			if(nd[k]>0){
28221 				if(i>nd[k]){
28222 					i-=nd[k];
28223 					nbroken+=nd[k]*k;
28224 				}
28225 				else{
28226 					nbroken+=i*k;
28227 					break;
28228 				}
28229 			}
28230 		}
28231 	}
28232 	//with logging
28233 	else{*/
28234 		nbroken=0;
28235 		//count sort
28236 		int t=this->teacher_ID;
28237 		//int nd[2*MAX_HOURS_PER_DAY + 1];
28238 		int nOD=0; //n occupied days
28239 		//for(int h=0; h<=2*r.nHoursPerDay; h++)
28240 		//nd[h]=0;
28241 		for(int d=0; d<r.nDaysPerWeek/2; d++){
28242 			int nh=0;
28243 			for(int h=0; h<r.nHoursPerDay; h++)
28244 				nh += teachersMatrix[t][2*d][h]>=1 ? 1 : 0;
28245 			for(int h=0; h<r.nHoursPerDay; h++)
28246 				nh += teachersMatrix[t][2*d+1][h]>=1 ? 1 : 0;
28247 			if(nh>0)
28248 				nOD++;
28249 			//nd[nh]++;
28250 		}
28251 
28252 		//return the minimum occupied days which do not respect this constraint
28253 /*		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
28254 		for(int k=0; k<=2*r.nHoursPerDay; k++){
28255 			if(nd[k]>0){
28256 				if(i>nd[k]){
28257 					i-=nd[k];
28258 					nbroken+=nd[k]*k;
28259 				}
28260 				else{
28261 					nbroken+=i*k;
28262 					break;
28263 				}
28264 			}
28265 		}*/
28266 
28267 		if(nOD>this->maxDaysPerWeek)
28268 			nbroken=1;
28269 
28270 		if(nbroken>0 && conflictsString!=nullptr){
28271 			QString s= tr("Time constraint teacher max real days per week broken for teacher: %1.")
28272 			 .arg(r.internalTeachersList[t]->name);
28273 			s += tr("This increases the conflicts total by %1")
28274 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
28275 
28276 			dl.append(s);
28277 			cl.append(nbroken*weightPercentage/100);
28278 
28279 			*conflictsString += s+"\n";
28280 		}
28281 	//}
28282 
28283 	if(weightPercentage==100)
28284 		assert(nbroken==0);
28285 	return weightPercentage/100 * nbroken;
28286 }
28287 
isRelatedToActivity(Rules & r,Activity * a)28288 bool ConstraintTeacherMaxRealDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
28289 {
28290 	Q_UNUSED(r);
28291 	Q_UNUSED(a);
28292 
28293 	return false;
28294 }
28295 
isRelatedToTeacher(Teacher * t)28296 bool ConstraintTeacherMaxRealDaysPerWeek::isRelatedToTeacher(Teacher* t)
28297 {
28298 	if(this->teacherName==t->name)
28299 		return true;
28300 	return false;
28301 }
28302 
isRelatedToSubject(Subject * s)28303 bool ConstraintTeacherMaxRealDaysPerWeek::isRelatedToSubject(Subject* s)
28304 {
28305 	Q_UNUSED(s);
28306 
28307 	return false;
28308 }
28309 
isRelatedToActivityTag(ActivityTag * s)28310 bool ConstraintTeacherMaxRealDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
28311 {
28312 	Q_UNUSED(s);
28313 
28314 	return false;
28315 }
28316 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)28317 bool ConstraintTeacherMaxRealDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
28318 {
28319 	Q_UNUSED(r);
28320 	Q_UNUSED(s);
28321 
28322 	return false;
28323 }
28324 
hasWrongDayOrHour(Rules & r)28325 bool ConstraintTeacherMaxRealDaysPerWeek::hasWrongDayOrHour(Rules& r)
28326 {
28327 	if(maxDaysPerWeek>r.nDaysPerWeek/2)
28328 		return true;
28329 
28330 	return false;
28331 }
28332 
canRepairWrongDayOrHour(Rules & r)28333 bool ConstraintTeacherMaxRealDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
28334 {
28335 	assert(hasWrongDayOrHour(r));
28336 
28337 	return true;
28338 }
28339 
repairWrongDayOrHour(Rules & r)28340 bool ConstraintTeacherMaxRealDaysPerWeek::repairWrongDayOrHour(Rules& r)
28341 {
28342 	assert(hasWrongDayOrHour(r));
28343 
28344 	if(maxDaysPerWeek>r.nDaysPerWeek/2)
28345 		maxDaysPerWeek=r.nDaysPerWeek/2;
28346 
28347 	return true;
28348 }
28349 
28350 ///////////////////////////////////////////////////////////////////////////////////////////
28351 ///////////////////////////////////////////////////////////////////////////////////////////
28352 
ConstraintTeachersMaxRealDaysPerWeek()28353 ConstraintTeachersMaxRealDaysPerWeek::ConstraintTeachersMaxRealDaysPerWeek()
28354 	: TimeConstraint()
28355 {
28356 	this->type=CONSTRAINT_TEACHERS_MAX_REAL_DAYS_PER_WEEK;
28357 }
28358 
ConstraintTeachersMaxRealDaysPerWeek(double wp,int maxnd)28359 ConstraintTeachersMaxRealDaysPerWeek::ConstraintTeachersMaxRealDaysPerWeek(double wp, int maxnd)
28360 	 : TimeConstraint(wp)
28361 {
28362 	this->maxDaysPerWeek=maxnd;
28363 	this->type=CONSTRAINT_TEACHERS_MAX_REAL_DAYS_PER_WEEK;
28364 }
28365 
computeInternalStructure(QWidget * parent,Rules & r)28366 bool ConstraintTeachersMaxRealDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
28367 {
28368 	Q_UNUSED(parent);
28369 	Q_UNUSED(r);
28370 
28371 	return true;
28372 }
28373 
hasInactiveActivities(Rules & r)28374 bool ConstraintTeachersMaxRealDaysPerWeek::hasInactiveActivities(Rules& r)
28375 {
28376 	Q_UNUSED(r);
28377 	return false;
28378 }
28379 
getXmlDescription(Rules & r)28380 QString ConstraintTeachersMaxRealDaysPerWeek::getXmlDescription(Rules& r)
28381 {
28382 	Q_UNUSED(r);
28383 
28384 	QString s="<ConstraintTeachersMaxRealDaysPerWeek>\n";
28385 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
28386 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
28387 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
28388 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
28389 	s+="</ConstraintTeachersMaxRealDaysPerWeek>\n";
28390 	return s;
28391 }
28392 
getDescription(Rules & r)28393 QString ConstraintTeachersMaxRealDaysPerWeek::getDescription(Rules& r){
28394 	Q_UNUSED(r);
28395 
28396 	QString begin=QString("");
28397 	if(!active)
28398 		begin="X - ";
28399 
28400 	QString end=QString("");
28401 	if(!comments.isEmpty())
28402 		end=", "+tr("C: %1", "Comments").arg(comments);
28403 
28404 	QString s=tr("Teachers max real days per week");s+=", ";
28405 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
28406 	s+=tr("MD:%1", "Max days (per week)").arg(this->maxDaysPerWeek);
28407 
28408 	return begin+s+end;
28409 }
28410 
getDetailedDescription(Rules & r)28411 QString ConstraintTeachersMaxRealDaysPerWeek::getDetailedDescription(Rules& r){
28412 	Q_UNUSED(r);
28413 
28414 	QString s=tr("Time constraint");s+="\n";
28415 	s+=tr("All teachers must respect the maximum number of real days per week");s+="\n";
28416 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
28417 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
28418 
28419 	if(!active){
28420 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
28421 		s+="\n";
28422 	}
28423 	if(!comments.isEmpty()){
28424 		s+=tr("Comments=%1").arg(comments);
28425 		s+="\n";
28426 	}
28427 
28428 	return s;
28429 }
28430 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)28431 double ConstraintTeachersMaxRealDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
28432 {
28433 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
28434 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
28435 		c.teachersMatrixReady=true;
28436 		c.subgroupsMatrixReady=true;
28437 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
28438 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
28439 
28440 		c.changedForMatrixCalculation=false;
28441 	}
28442 
28443 	int nbroken;
28444 
28445 	//without logging
28446 	/*
28447 	if(conflictsString==nullptr){
28448 		nbroken=0;
28449 		//count sort
28450 
28451 		for(int t=0; t<r.nInternalTeachers; t++){
28452 			int nd[MAX_HOURS_PER_DAY + 1];
28453 			for(int h=0; h<=r.nHoursPerDay; h++)
28454 				nd[h]=0;
28455 			for(int d=0; d<r.nDaysPerWeek; d++){
28456 				int nh=0;
28457 				for(int h=0; h<r.nHoursPerDay; h++)
28458 					nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
28459 				nd[nh]++;
28460 			}
28461 			//return the minimum occupied days which do not respect this constraint
28462 			int i = r.nDaysPerWeek - this->maxDaysPerWeek;
28463 			for(int k=0; k<=r.nHoursPerDay; k++){
28464 				if(nd[k]>0){
28465 					if(i>nd[k]){
28466 						i-=nd[k];
28467 						nbroken+=nd[k]*k;
28468 					}
28469 					else{
28470 						nbroken+=i*k;
28471 						break;
28472 					}
28473 				}
28474 			}
28475 
28476 		}
28477 	}
28478 	//with logging
28479 	else{*/
28480 		nbroken=0;
28481 
28482 		for(int t=0; t<r.nInternalTeachers; t++){
28483 			int nbr=0;
28484 
28485 			//count sort
28486 			//int t=this->teacher_ID;
28487 			//int nd[2*MAX_HOURS_PER_DAY + 1];
28488 			//for(int h=0; h<=2*r.nHoursPerDay; h++)
28489 			//	nd[h]=0;
28490 			int nOD=0;
28491 			for(int d=0; d<r.nDaysPerWeek/2; d++){
28492 				int nh=0;
28493 				for(int h=0; h<r.nHoursPerDay; h++)
28494 					nh += teachersMatrix[t][2*d][h]>=1 ? 1 : 0;
28495 				for(int h=0; h<r.nHoursPerDay; h++)
28496 					nh += teachersMatrix[t][2*d+1][h]>=1 ? 1 : 0;
28497 				if(nh>0)
28498 					nOD++;
28499 				//nd[nh]++;
28500 			}
28501 			//return the minimum occupied days which do not respect this constraint
28502 /*			int i = r.nDaysPerWeek - this->maxDaysPerWeek;
28503 			for(int k=0; k<=2*r.nHoursPerDay; k++){
28504 				if(nd[k]>0){
28505 					if(i>nd[k]){
28506 						i-=nd[k];
28507 						nbroken+=nd[k]*k;
28508 						nbr+=nd[k]*k;
28509 					}
28510 					else{
28511 						nbroken+=i*k;
28512 						nbr+=i*k;
28513 						break;
28514 					}
28515 				}
28516 			}*/
28517 
28518 			if(nOD>this->maxDaysPerWeek)
28519 				 nbr=1;
28520 
28521 			if(nbr>0 && conflictsString!=nullptr){
28522 				QString s= tr("Time constraint teachers max real days per week broken for teacher: %1.")
28523 				.arg(r.internalTeachersList[t]->name);
28524 				s += tr("This increases the conflicts total by %1")
28525 				.arg(CustomFETString::numberPlusTwoDigitsPrecision(nbr*weightPercentage/100));
28526 
28527 				dl.append(s);
28528 				cl.append(nbr*weightPercentage/100);
28529 
28530 				*conflictsString += s+"\n";
28531 			}
28532 
28533 		}
28534 //	}
28535 
28536 	if(weightPercentage==100)
28537 		assert(nbroken==0);
28538 	return weightPercentage/100 * nbroken;
28539 }
28540 
isRelatedToActivity(Rules & r,Activity * a)28541 bool ConstraintTeachersMaxRealDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
28542 {
28543 	Q_UNUSED(r);
28544 	Q_UNUSED(a);
28545 
28546 	return false;
28547 }
28548 
isRelatedToTeacher(Teacher * t)28549 bool ConstraintTeachersMaxRealDaysPerWeek::isRelatedToTeacher(Teacher* t)
28550 {
28551 	Q_UNUSED(t);
28552 
28553 	return true;
28554 }
28555 
isRelatedToSubject(Subject * s)28556 bool ConstraintTeachersMaxRealDaysPerWeek::isRelatedToSubject(Subject* s)
28557 {
28558 	Q_UNUSED(s);
28559 
28560 	return false;
28561 }
28562 
isRelatedToActivityTag(ActivityTag * s)28563 bool ConstraintTeachersMaxRealDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
28564 {
28565 	Q_UNUSED(s);
28566 
28567 	return false;
28568 }
28569 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)28570 bool ConstraintTeachersMaxRealDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
28571 {
28572 	Q_UNUSED(r);
28573 	Q_UNUSED(s);
28574 
28575 	return false;
28576 }
28577 
hasWrongDayOrHour(Rules & r)28578 bool ConstraintTeachersMaxRealDaysPerWeek::hasWrongDayOrHour(Rules& r)
28579 {
28580 	if(maxDaysPerWeek>r.nDaysPerWeek/2)
28581 		return true;
28582 
28583 	return false;
28584 }
28585 
canRepairWrongDayOrHour(Rules & r)28586 bool ConstraintTeachersMaxRealDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
28587 {
28588 	assert(hasWrongDayOrHour(r));
28589 
28590 	return true;
28591 }
28592 
repairWrongDayOrHour(Rules & r)28593 bool ConstraintTeachersMaxRealDaysPerWeek::repairWrongDayOrHour(Rules& r)
28594 {
28595 	assert(hasWrongDayOrHour(r));
28596 
28597 	if(maxDaysPerWeek>r.nDaysPerWeek/2)
28598 		maxDaysPerWeek=r.nDaysPerWeek/2;
28599 
28600 	return true;
28601 }
28602 
28603 ////////////////////////////////////////////////////////////////////////////////////////////
28604 ////////////////////////////////////////////////////////////////////////////////////////////
28605 
ConstraintTeachersMaxGapsPerRealDay()28606 ConstraintTeachersMaxGapsPerRealDay::ConstraintTeachersMaxGapsPerRealDay()
28607 	: TimeConstraint()
28608 {
28609 	this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_REAL_DAY;
28610 	this->maxGaps=-1;
28611 	this->allowOneDayExceptionPlusOne=false;
28612 }
28613 
ConstraintTeachersMaxGapsPerRealDay(double wp,int mg,bool except)28614 ConstraintTeachersMaxGapsPerRealDay::ConstraintTeachersMaxGapsPerRealDay(double wp, int mg, bool except)
28615 	: TimeConstraint(wp)
28616 {
28617 	this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_REAL_DAY;
28618 	this->maxGaps=mg;
28619 	this->allowOneDayExceptionPlusOne=except;
28620 }
28621 
computeInternalStructure(QWidget * parent,Rules & r)28622 bool ConstraintTeachersMaxGapsPerRealDay::computeInternalStructure(QWidget* parent, Rules& r)
28623 {
28624 	Q_UNUSED(parent);
28625 	Q_UNUSED(r);
28626 
28627 	return true;
28628 }
28629 
hasInactiveActivities(Rules & r)28630 bool ConstraintTeachersMaxGapsPerRealDay::hasInactiveActivities(Rules& r)
28631 {
28632 	Q_UNUSED(r);
28633 	return false;
28634 }
28635 
getXmlDescription(Rules & r)28636 QString ConstraintTeachersMaxGapsPerRealDay::getXmlDescription(Rules& r){
28637 	Q_UNUSED(r);
28638 
28639 	QString s="<ConstraintTeachersMaxGapsPerRealDay>\n";
28640 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
28641 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
28642 	s+="	<Allow_One_Day_Exception_of_Plus_One>"+trueFalse(allowOneDayExceptionPlusOne)+"</Allow_One_Day_Exception_of_Plus_One>\n";
28643 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
28644 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
28645 	s+="</ConstraintTeachersMaxGapsPerRealDay>\n";
28646 	return s;
28647 }
28648 
getDescription(Rules & r)28649 QString ConstraintTeachersMaxGapsPerRealDay::getDescription(Rules& r){
28650 	Q_UNUSED(r);
28651 
28652 	QString begin=QString("");
28653 	if(!active)
28654 		begin="X - ";
28655 
28656 	QString end=QString("");
28657 	if(!comments.isEmpty())
28658 		end=", "+tr("C: %1", "Comments").arg(comments);
28659 
28660 	QString s;
28661 	s+="! ";
28662 	s+=tr("Teachers max gaps per real day");s+=", ";
28663 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
28664 	s+=tr("MG:%1", "Max gaps (per real day)").arg(this->maxGaps);s+=", ";
28665 	s+=tr("ODE:%1", "One day exception (in which the teacher can have gaps+1)").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));
28666 
28667 	return begin+s+end;
28668 }
28669 
getDetailedDescription(Rules & r)28670 QString ConstraintTeachersMaxGapsPerRealDay::getDetailedDescription(Rules& r){
28671 	Q_UNUSED(r);
28672 
28673 	QString s=tr("Time constraint");s+="\n";
28674 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
28675 	s+=tr("All teachers must respect the maximum gaps per real day");s+="\n";
28676 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
28677 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
28678 	s+=tr("Maximum gaps per real day=%1").arg(this->maxGaps); s+="\n";
28679 	s+=tr("Allow one day exception of plus one=%1").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));s+="\n";
28680 
28681 	if(!active){
28682 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
28683 		s+="\n";
28684 	}
28685 	if(!comments.isEmpty()){
28686 		s+=tr("Comments=%1").arg(comments);
28687 		s+="\n";
28688 	}
28689 
28690 	return s;
28691 }
28692 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)28693 double ConstraintTeachersMaxGapsPerRealDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
28694 {
28695 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
28696 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
28697 		c.teachersMatrixReady=true;
28698 		c.subgroupsMatrixReady=true;
28699 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
28700 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
28701 
28702 		c.changedForMatrixCalculation=false;
28703 	}
28704 
28705 	int tg;
28706 	int i, j, k;
28707 	int totalGaps;
28708 
28709 	int real_d, double_h;
28710 
28711 	totalGaps=0;
28712 	for(i=0; i<r.nInternalTeachers; i++){
28713 		int except;
28714 		if(allowOneDayExceptionPlusOne)
28715 			except=1;
28716 		else
28717 			except=0;
28718 
28719 		//for(j=0; j<r.nDaysPerWeek; j++){
28720 		for(real_d=0; real_d<r.nDaysPerWeek/2; real_d++){
28721 			tg=0;
28722 			for(double_h=0; double_h<2*r.nHoursPerDay; double_h++){
28723 				if(double_h<r.nHoursPerDay)
28724 					j=2*real_d;
28725 				else
28726 					j=2*real_d+1;
28727 				k=double_h%r.nHoursPerDay;
28728 				if(teachersMatrix[i][j][k]>0){
28729 					assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
28730 					break;
28731 				}
28732 			}
28733 
28734 			int cnt=0;
28735 			for(; double_h<2*r.nHoursPerDay; double_h++){
28736 				if(double_h<r.nHoursPerDay)
28737 					j=2*real_d;
28738 				else
28739 					j=2*real_d+1;
28740 				k=double_h%r.nHoursPerDay;
28741 				if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
28742 					if(teachersMatrix[i][j][k]>0){
28743 						tg+=cnt;
28744 						cnt=0;
28745 					}
28746 					else{
28747 						cnt++;
28748 					}
28749 				}
28750 			}
28751 			if(tg==this->maxGaps+1 && except>0)
28752 				except--;
28753 			else if(tg>this->maxGaps && except>0){
28754 				assert(tg>this->maxGaps+1);
28755 				except--;
28756 				totalGaps+=tg-maxGaps-1;
28757 				//assert(this->weightPercentage<100); partial solutions might break this rule
28758 				if(conflictsString!=nullptr){
28759 					QString s=tr("Time constraint teachers max gaps per real day broken for teacher: %1, real day number %2, conflicts factor increase=%3")
28760 						.arg(r.internalTeachersList[i]->name)
28761 						.arg(real_d)
28762 						.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps-1)*weightPercentage/100));
28763 
28764 					*conflictsString+= s+"\n";
28765 
28766 					dl.append(s);
28767 					cl.append((tg-maxGaps-1)*weightPercentage/100);
28768 				}
28769 			}
28770 			else if(tg>this->maxGaps){
28771 				assert(except==0);
28772 				totalGaps+=tg-maxGaps;
28773 				//assert(this->weightPercentage<100); partial solutions might break this rule
28774 				if(conflictsString!=nullptr){
28775 					QString s=tr("Time constraint teachers max gaps per real day broken for teacher: %1, real day number %2, conflicts factor increase=%3")
28776 						.arg(r.internalTeachersList[i]->name)
28777 						.arg(real_d)
28778 						.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps)*weightPercentage/100));
28779 
28780 					*conflictsString+= s+"\n";
28781 
28782 					dl.append(s);
28783 					cl.append((tg-maxGaps)*weightPercentage/100);
28784 				}
28785 			}
28786 		}
28787 	}
28788 
28789 	if(c.nPlacedActivities==r.nInternalActivities)
28790 		if(weightPercentage==100)
28791 			assert(totalGaps==0); //for partial solutions this rule might be broken
28792 	return weightPercentage/100 * totalGaps;
28793 }
28794 
isRelatedToActivity(Rules & r,Activity * a)28795 bool ConstraintTeachersMaxGapsPerRealDay::isRelatedToActivity(Rules& r, Activity* a)
28796 {
28797 	Q_UNUSED(r);
28798 	Q_UNUSED(a);
28799 
28800 	return false;
28801 }
28802 
isRelatedToTeacher(Teacher * t)28803 bool ConstraintTeachersMaxGapsPerRealDay::isRelatedToTeacher(Teacher* t)
28804 {
28805 	Q_UNUSED(t);
28806 
28807 	return true;
28808 }
28809 
isRelatedToSubject(Subject * s)28810 bool ConstraintTeachersMaxGapsPerRealDay::isRelatedToSubject(Subject* s)
28811 {
28812 	Q_UNUSED(s);
28813 
28814 	return false;
28815 }
28816 
isRelatedToActivityTag(ActivityTag * s)28817 bool ConstraintTeachersMaxGapsPerRealDay::isRelatedToActivityTag(ActivityTag* s)
28818 {
28819 	Q_UNUSED(s);
28820 
28821 	return false;
28822 }
28823 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)28824 bool ConstraintTeachersMaxGapsPerRealDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
28825 {
28826 	Q_UNUSED(r);
28827 	Q_UNUSED(s);
28828 
28829 	return false;
28830 }
28831 
hasWrongDayOrHour(Rules & r)28832 bool ConstraintTeachersMaxGapsPerRealDay::hasWrongDayOrHour(Rules& r)
28833 {
28834 	if(maxGaps>2*r.nHoursPerDay)
28835 		return true;
28836 
28837 	return false;
28838 }
28839 
canRepairWrongDayOrHour(Rules & r)28840 bool ConstraintTeachersMaxGapsPerRealDay::canRepairWrongDayOrHour(Rules& r)
28841 {
28842 	assert(hasWrongDayOrHour(r));
28843 
28844 	return true;
28845 }
28846 
repairWrongDayOrHour(Rules & r)28847 bool ConstraintTeachersMaxGapsPerRealDay::repairWrongDayOrHour(Rules& r)
28848 {
28849 	assert(hasWrongDayOrHour(r));
28850 
28851 	if(maxGaps>2*r.nHoursPerDay)
28852 		maxGaps=2*r.nHoursPerDay;
28853 
28854 	return true;
28855 }
28856 
28857 ////////////////////////////////////////////////////////////////////////////////////////////
28858 ////////////////////////////////////////////////////////////////////////////////////////////
28859 
ConstraintTeacherMaxGapsPerRealDay()28860 ConstraintTeacherMaxGapsPerRealDay::ConstraintTeacherMaxGapsPerRealDay()
28861 	: TimeConstraint()
28862 {
28863 	this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_REAL_DAY;
28864 	this->maxGaps = -1;
28865 	this->allowOneDayExceptionPlusOne=false;
28866 }
28867 
ConstraintTeacherMaxGapsPerRealDay(double wp,QString tn,int mg,bool except)28868 ConstraintTeacherMaxGapsPerRealDay::ConstraintTeacherMaxGapsPerRealDay(double wp, QString tn, int mg, bool except)
28869 	: TimeConstraint(wp)
28870 {
28871 	this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_REAL_DAY;
28872 	this->teacherName=tn;
28873 	this->maxGaps=mg;
28874 	this->allowOneDayExceptionPlusOne=except;
28875 }
28876 
computeInternalStructure(QWidget * parent,Rules & r)28877 bool ConstraintTeacherMaxGapsPerRealDay::computeInternalStructure(QWidget* parent, Rules& r)
28878 {
28879 	Q_UNUSED(parent);
28880 
28881 	//this->teacherIndex=r.searchTeacher(this->teacherName);
28882 	teacherIndex=r.teachersHash.value(teacherName, -1);
28883 	assert(this->teacherIndex>=0);
28884 	return true;
28885 }
28886 
hasInactiveActivities(Rules & r)28887 bool ConstraintTeacherMaxGapsPerRealDay::hasInactiveActivities(Rules& r)
28888 {
28889 	Q_UNUSED(r);
28890 	return false;
28891 }
28892 
getXmlDescription(Rules & r)28893 QString ConstraintTeacherMaxGapsPerRealDay::getXmlDescription(Rules& r){
28894 	Q_UNUSED(r);
28895 
28896 	QString s="<ConstraintTeacherMaxGapsPerRealDay>\n";
28897 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
28898 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
28899 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
28900 	s+="	<Allow_One_Day_Exception_of_Plus_One>"+trueFalse(allowOneDayExceptionPlusOne)+"</Allow_One_Day_Exception_of_Plus_One>\n";
28901 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
28902 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
28903 	s+="</ConstraintTeacherMaxGapsPerRealDay>\n";
28904 	return s;
28905 }
28906 
getDescription(Rules & r)28907 QString ConstraintTeacherMaxGapsPerRealDay::getDescription(Rules& r){
28908 	Q_UNUSED(r);
28909 
28910 	QString begin=QString("");
28911 	if(!active)
28912 		begin="X - ";
28913 
28914 	QString end=QString("");
28915 	if(!comments.isEmpty())
28916 		end=", "+tr("C: %1", "Comments").arg(comments);
28917 
28918 	QString s;
28919 	s+="! ";
28920 	s+=tr("Teacher max gaps per real day");s+=", ";
28921 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
28922 	s+=tr("T:%1", "Teacher").arg(this->teacherName); s+=", ";
28923 	s+=tr("MG:%1", "Max gaps (per real day)").arg(this->maxGaps);s+=", ";
28924 	s+=tr("ODE:%1", "One day exception (in which the teacher can have gaps+1)").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));
28925 
28926 	return begin+s+end;
28927 }
28928 
getDetailedDescription(Rules & r)28929 QString ConstraintTeacherMaxGapsPerRealDay::getDetailedDescription(Rules& r){
28930 	Q_UNUSED(r);
28931 
28932 	QString s=tr("Time constraint"); s+="\n";
28933 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
28934 	s+=tr("A teacher must respect the maximum number of gaps per real day"); s+="\n";
28935 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
28936 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
28937 	s+=tr("Teacher=%1").arg(this->teacherName); s+="\n";
28938 	s+=tr("Maximum gaps per real day=%1").arg(this->maxGaps); s+="\n";
28939 	s+=tr("Allow one day exception of plus one=%1").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));s+="\n";
28940 
28941 	if(!active){
28942 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
28943 		s+="\n";
28944 	}
28945 	if(!comments.isEmpty()){
28946 		s+=tr("Comments=%1").arg(comments);
28947 		s+="\n";
28948 	}
28949 
28950 	return s;
28951 }
28952 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)28953 double ConstraintTeacherMaxGapsPerRealDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
28954 {
28955 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
28956 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
28957 		c.teachersMatrixReady=true;
28958 		c.subgroupsMatrixReady=true;
28959 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
28960 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
28961 
28962 		c.changedForMatrixCalculation=false;
28963 	}
28964 
28965 	int tg;
28966 	int i, j, k;
28967 	int totalGaps;
28968 
28969 	totalGaps=0;
28970 
28971 	i=this->teacherIndex;
28972 
28973 	int real_d, double_h;
28974 
28975 	int except;
28976 	if(allowOneDayExceptionPlusOne)
28977 		except=1;
28978 	else
28979 		except=0;
28980 
28981 	for(real_d=0; real_d<r.nDaysPerWeek/2; real_d++){
28982 		tg=0;
28983 		for(double_h=0; double_h<2*r.nHoursPerDay; double_h++){
28984 			if(double_h<r.nHoursPerDay)
28985 				j=2*real_d;
28986 			else
28987 				j=2*real_d+1;
28988 			k=double_h%r.nHoursPerDay;
28989 			if(teachersMatrix[i][j][k]>0){
28990 				assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
28991 				break;
28992 			}
28993 		}
28994 
28995 		int cnt=0;
28996 		for(; double_h<2*r.nHoursPerDay; double_h++){
28997 			if(double_h<r.nHoursPerDay)
28998 				j=2*real_d;
28999 			else
29000 				j=2*real_d+1;
29001 			k=double_h%r.nHoursPerDay;
29002 			if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
29003 				if(teachersMatrix[i][j][k]>0){
29004 					tg+=cnt;
29005 					cnt=0;
29006 				}
29007 				else
29008 					cnt++;
29009 			}
29010 		}
29011 		if(tg==this->maxGaps+1 && except>0)
29012 			except--;
29013 		else if(tg>this->maxGaps && except>0){
29014 			assert(tg>this->maxGaps+1);
29015 			except--;
29016 			totalGaps+=tg-maxGaps-1;
29017 			//assert(this->weightPercentage<100); partial solutions might break this rule
29018 			if(conflictsString!=nullptr){
29019 				QString s=tr("Time constraint teacher max gaps per real day broken for teacher: %1, real day number %2, conflicts factor increase=%3")
29020 					.arg(r.internalTeachersList[i]->name)
29021 					.arg(real_d)
29022 					.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps-1)*weightPercentage/100));
29023 
29024 				*conflictsString+= s+"\n";
29025 
29026 				dl.append(s);
29027 				cl.append((tg-maxGaps-1)*weightPercentage/100);
29028 			}
29029 		}
29030 		else if(tg>this->maxGaps){
29031 			assert(except==0);
29032 			totalGaps+=tg-maxGaps;
29033 			//assert(this->weightPercentage<100); partial solutions might break this rule
29034 			if(conflictsString!=nullptr){
29035 				QString s=tr("Time constraint teacher max gaps per real day broken for teacher: %1, real day number %2, conflicts factor increase=%3")
29036 					.arg(r.internalTeachersList[i]->name)
29037 					.arg(real_d)
29038 					.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps)*weightPercentage/100));
29039 
29040 				*conflictsString+= s+"\n";
29041 
29042 				dl.append(s);
29043 				cl.append((tg-maxGaps)*weightPercentage/100);
29044 			}
29045 		}
29046 	}
29047 
29048 	if(c.nPlacedActivities==r.nInternalActivities)
29049 		if(weightPercentage==100)
29050 			assert(totalGaps==0); //for partial solutions this rule might be broken
29051 	return weightPercentage/100 * totalGaps;
29052 }
29053 
isRelatedToActivity(Rules & r,Activity * a)29054 bool ConstraintTeacherMaxGapsPerRealDay::isRelatedToActivity(Rules& r, Activity* a)
29055 {
29056 	Q_UNUSED(r);
29057 	Q_UNUSED(a);
29058 
29059 	return false;
29060 }
29061 
isRelatedToTeacher(Teacher * t)29062 bool ConstraintTeacherMaxGapsPerRealDay::isRelatedToTeacher(Teacher* t)
29063 {
29064 	if(this->teacherName==t->name)
29065 		return true;
29066 	return false;
29067 }
29068 
isRelatedToSubject(Subject * s)29069 bool ConstraintTeacherMaxGapsPerRealDay::isRelatedToSubject(Subject* s)
29070 {
29071 	Q_UNUSED(s);
29072 
29073 	return false;
29074 }
29075 
isRelatedToActivityTag(ActivityTag * s)29076 bool ConstraintTeacherMaxGapsPerRealDay::isRelatedToActivityTag(ActivityTag* s)
29077 {
29078 	Q_UNUSED(s);
29079 
29080 	return false;
29081 }
29082 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)29083 bool ConstraintTeacherMaxGapsPerRealDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
29084 {
29085 	Q_UNUSED(r);
29086 	Q_UNUSED(s);
29087 
29088 	return false;
29089 }
29090 
hasWrongDayOrHour(Rules & r)29091 bool ConstraintTeacherMaxGapsPerRealDay::hasWrongDayOrHour(Rules& r)
29092 {
29093 	if(maxGaps>2*r.nHoursPerDay)
29094 		return true;
29095 
29096 	return false;
29097 }
29098 
canRepairWrongDayOrHour(Rules & r)29099 bool ConstraintTeacherMaxGapsPerRealDay::canRepairWrongDayOrHour(Rules& r)
29100 {
29101 	assert(hasWrongDayOrHour(r));
29102 
29103 	return true;
29104 }
29105 
repairWrongDayOrHour(Rules & r)29106 bool ConstraintTeacherMaxGapsPerRealDay::repairWrongDayOrHour(Rules& r)
29107 {
29108 	assert(hasWrongDayOrHour(r));
29109 
29110 	if(maxGaps>2*r.nHoursPerDay)
29111 		maxGaps=2*r.nHoursPerDay;
29112 
29113 	return true;
29114 }
29115 
29116 ////////////////////////////////////////////////////////////////////////////////////////////
29117 ////////////////////////////////////////////////////////////////////////////////////////////
29118 
ConstraintStudentsMaxHoursDailyRealDays()29119 ConstraintStudentsMaxHoursDailyRealDays::ConstraintStudentsMaxHoursDailyRealDays()
29120 	: TimeConstraint()
29121 {
29122 	this->type = CONSTRAINT_STUDENTS_MAX_HOURS_DAILY_REAL_DAYS;
29123 	this->maxHoursDaily = -1;
29124 }
29125 
ConstraintStudentsMaxHoursDailyRealDays(double wp,int maxnh)29126 ConstraintStudentsMaxHoursDailyRealDays::ConstraintStudentsMaxHoursDailyRealDays(double wp, int maxnh)
29127 	: TimeConstraint(wp)
29128 {
29129 	this->maxHoursDaily = maxnh;
29130 	this->type = CONSTRAINT_STUDENTS_MAX_HOURS_DAILY_REAL_DAYS;
29131 }
29132 
computeInternalStructure(QWidget * parent,Rules & r)29133 bool ConstraintStudentsMaxHoursDailyRealDays::computeInternalStructure(QWidget* parent, Rules& r)
29134 {
29135 	Q_UNUSED(parent);
29136 	Q_UNUSED(r);
29137 
29138 	return true;
29139 }
29140 
hasInactiveActivities(Rules & r)29141 bool ConstraintStudentsMaxHoursDailyRealDays::hasInactiveActivities(Rules& r)
29142 {
29143 	Q_UNUSED(r);
29144 	return false;
29145 }
29146 
getXmlDescription(Rules & r)29147 QString ConstraintStudentsMaxHoursDailyRealDays::getXmlDescription(Rules& r)
29148 {
29149 	Q_UNUSED(r);
29150 
29151 	QString s="<ConstraintStudentsMaxHoursDailyRealDays>\n";
29152 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
29153 	if(this->maxHoursDaily>=0)
29154 		s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
29155 	else
29156 		assert(0);
29157 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
29158 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
29159 	s+="</ConstraintStudentsMaxHoursDailyRealDays>\n";
29160 	return s;
29161 }
29162 
getDescription(Rules & r)29163 QString ConstraintStudentsMaxHoursDailyRealDays::getDescription(Rules& r)
29164 {
29165 	Q_UNUSED(r);
29166 
29167 	QString begin=QString("");
29168 	if(!active)
29169 		begin="X - ";
29170 
29171 	QString end=QString("");
29172 	if(!comments.isEmpty())
29173 		end=", "+tr("C: %1", "Comments").arg(comments);
29174 
29175 	QString s;
29176 	s+=tr("Students max hours daily per real day");s+=", ";
29177 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
29178 	s+=tr("MH:%1", "Max hours (daily)").arg(this->maxHoursDaily);
29179 
29180 	return begin+s+end;
29181 }
29182 
getDetailedDescription(Rules & r)29183 QString ConstraintStudentsMaxHoursDailyRealDays::getDetailedDescription(Rules& r)
29184 {
29185 	Q_UNUSED(r);
29186 
29187 	QString s=tr("Time constraint");s+="\n";
29188 	s+=tr("All students must respect the maximum number of hours daily per real day");s+="\n";
29189 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
29190 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
29191 
29192 	if(!active){
29193 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
29194 		s+="\n";
29195 	}
29196 	if(!comments.isEmpty()){
29197 		s+=tr("Comments=%1").arg(comments);
29198 		s+="\n";
29199 	}
29200 
29201 	return s;
29202 }
29203 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)29204 double ConstraintStudentsMaxHoursDailyRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
29205 {
29206 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
29207 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
29208 		c.teachersMatrixReady=true;
29209 		c.subgroupsMatrixReady=true;
29210 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
29211 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
29212 
29213 		c.changedForMatrixCalculation=false;
29214 	}
29215 
29216 	int tmp;
29217 	int too_much;
29218 
29219 	assert(this->maxHoursDaily>=0);
29220 
29221 	if(1){
29222 		too_much=0;
29223 		for(int i=0; i<r.nInternalSubgroups; i++)
29224 			for(int j=0; j<r.nDaysPerWeek/2; j++){
29225 				tmp=0;
29226 				for(int k=0; k<r.nHoursPerDay; k++){
29227 					if(subgroupsMatrix[i][2*j][k]>=1)
29228 						tmp++;
29229 				}
29230 				for(int k=0; k<r.nHoursPerDay; k++){
29231 					if(subgroupsMatrix[i][2*j+1][k]>=1)
29232 						tmp++;
29233 				}
29234 				if(this->maxHoursDaily>=0 && tmp > this->maxHoursDaily){ //we would like no more than maxHoursDaily hours per day.
29235 					too_much += 1; //tmp - this->maxHoursDaily;
29236 
29237 					if(conflictsString!=nullptr){
29238 						QString s=tr("Time constraint students max hours daily per real day broken for subgroup: %1, real day: %2, length=%3, conflict increase=%4")
29239 						 .arg(r.internalSubgroupsList[i]->name)
29240 						 .arg(j/*r.daysOfTheWeek[j]*/)
29241 						 .arg(CustomFETString::number(tmp))
29242 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*1));
29243 
29244 						dl.append(s);
29245 						cl.append(weightPercentage/100*1);
29246 
29247 						*conflictsString+= s+"\n";
29248 					}
29249 				}
29250 			}
29251 	}
29252 
29253 	assert(too_much>=0);
29254 	if(weightPercentage==100)
29255 		assert(too_much==0);
29256 	return too_much * weightPercentage/100;
29257 }
29258 
isRelatedToActivity(Rules & r,Activity * a)29259 bool ConstraintStudentsMaxHoursDailyRealDays::isRelatedToActivity(Rules& r, Activity* a)
29260 {
29261 	Q_UNUSED(r);
29262 	Q_UNUSED(a);
29263 
29264 	return false;
29265 }
29266 
isRelatedToTeacher(Teacher * t)29267 bool ConstraintStudentsMaxHoursDailyRealDays::isRelatedToTeacher(Teacher* t)
29268 {
29269 	Q_UNUSED(t);
29270 
29271 	return false;
29272 }
29273 
isRelatedToSubject(Subject * s)29274 bool ConstraintStudentsMaxHoursDailyRealDays::isRelatedToSubject(Subject* s)
29275 {
29276 	Q_UNUSED(s);
29277 
29278 	return false;
29279 }
29280 
isRelatedToActivityTag(ActivityTag * s)29281 bool ConstraintStudentsMaxHoursDailyRealDays::isRelatedToActivityTag(ActivityTag* s)
29282 {
29283 	Q_UNUSED(s);
29284 
29285 	return false;
29286 }
29287 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)29288 bool ConstraintStudentsMaxHoursDailyRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
29289 {
29290 	Q_UNUSED(r);
29291 	Q_UNUSED(s);
29292 
29293 	return true;
29294 }
29295 
hasWrongDayOrHour(Rules & r)29296 bool ConstraintStudentsMaxHoursDailyRealDays::hasWrongDayOrHour(Rules& r)
29297 {
29298 	if(maxHoursDaily>2*r.nHoursPerDay)
29299 		return true;
29300 
29301 	return false;
29302 }
29303 
canRepairWrongDayOrHour(Rules & r)29304 bool ConstraintStudentsMaxHoursDailyRealDays::canRepairWrongDayOrHour(Rules& r)
29305 {
29306 	assert(hasWrongDayOrHour(r));
29307 
29308 	return true;
29309 }
29310 
repairWrongDayOrHour(Rules & r)29311 bool ConstraintStudentsMaxHoursDailyRealDays::repairWrongDayOrHour(Rules& r)
29312 {
29313 	assert(hasWrongDayOrHour(r));
29314 
29315 	if(maxHoursDaily>2*r.nHoursPerDay)
29316 		maxHoursDaily=2*r.nHoursPerDay;
29317 
29318 	return true;
29319 }
29320 
29321 ////////////////////////////////////////////////////////////////////////////////////////////
29322 ////////////////////////////////////////////////////////////////////////////////////////////
29323 
ConstraintStudentsSetMaxHoursDailyRealDays()29324 ConstraintStudentsSetMaxHoursDailyRealDays::ConstraintStudentsSetMaxHoursDailyRealDays()
29325 	: TimeConstraint()
29326 {
29327 	this->type = CONSTRAINT_STUDENTS_SET_MAX_HOURS_DAILY_REAL_DAYS;
29328 	this->maxHoursDaily = -1;
29329 }
29330 
ConstraintStudentsSetMaxHoursDailyRealDays(double wp,int maxnh,QString s)29331 ConstraintStudentsSetMaxHoursDailyRealDays::ConstraintStudentsSetMaxHoursDailyRealDays(double wp, int maxnh, QString s)
29332 	: TimeConstraint(wp)
29333 {
29334 	this->maxHoursDaily = maxnh;
29335 	this->students = s;
29336 	this->type = CONSTRAINT_STUDENTS_SET_MAX_HOURS_DAILY_REAL_DAYS;
29337 }
29338 
hasInactiveActivities(Rules & r)29339 bool ConstraintStudentsSetMaxHoursDailyRealDays::hasInactiveActivities(Rules& r)
29340 {
29341 	Q_UNUSED(r);
29342 	return false;
29343 }
29344 
getXmlDescription(Rules & r)29345 QString ConstraintStudentsSetMaxHoursDailyRealDays::getXmlDescription(Rules& r)
29346 {
29347 	Q_UNUSED(r);
29348 
29349 	QString s="<ConstraintStudentsSetMaxHoursDailyRealDays>\n";
29350 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
29351 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
29352 	s+="	<Students>"+protect(this->students)+"</Students>\n";
29353 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
29354 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
29355 	s+="</ConstraintStudentsSetMaxHoursDailyRealDays>\n";
29356 	return s;
29357 }
29358 
getDescription(Rules & r)29359 QString ConstraintStudentsSetMaxHoursDailyRealDays::getDescription(Rules& r)
29360 {
29361 	Q_UNUSED(r);
29362 
29363 	QString begin=QString("");
29364 	if(!active)
29365 		begin="X - ";
29366 
29367 	QString end=QString("");
29368 	if(!comments.isEmpty())
29369 		end=", "+tr("C: %1", "Comments").arg(comments);
29370 
29371 	QString s;
29372 	s+=tr("Students set max hours daily per real day");s+=", ";
29373 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
29374 	s+=tr("St:%1", "Students (set)").arg(this->students); s+=", ";
29375 	s+=tr("MH:%1", "Max hours (daily)").arg(this->maxHoursDaily);
29376 
29377 	return begin+s+end;
29378 }
29379 
getDetailedDescription(Rules & r)29380 QString ConstraintStudentsSetMaxHoursDailyRealDays::getDetailedDescription(Rules& r)
29381 {
29382 	Q_UNUSED(r);
29383 
29384 	QString s=tr("Time constraint");s+="\n";
29385 	s+=tr("A students set must respect the maximum number of hours daily per real day");s+="\n";
29386 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
29387 	s+=tr("Students set=%1").arg(this->students);s+="\n";
29388 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
29389 
29390 	if(!active){
29391 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
29392 		s+="\n";
29393 	}
29394 	if(!comments.isEmpty()){
29395 		s+=tr("Comments=%1").arg(comments);
29396 		s+="\n";
29397 	}
29398 
29399 	return s;
29400 }
29401 
computeInternalStructure(QWidget * parent,Rules & r)29402 bool ConstraintStudentsSetMaxHoursDailyRealDays::computeInternalStructure(QWidget* parent, Rules& r)
29403 {
29404 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
29405 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
29406 
29407 	if(ss==nullptr){
29408 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
29409 		 tr("Constraint students set max hours daily is wrong because it refers to inexistent students set."
29410 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
29411 
29412 		return false;
29413 	}
29414 
29415 	assert(ss!=nullptr);
29416 
29417 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
29418 	/*this->iSubgroupsList.clear();
29419 	if(ss->type==STUDENTS_SUBGROUP){
29420 		int tmp;
29421 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
29422 		assert(tmp>=0);
29423 		assert(tmp<r.nInternalSubgroups);
29424 		if(!this->iSubgroupsList.contains(tmp))
29425 			this->iSubgroupsList.append(tmp);
29426 	}
29427 	else if(ss->type==STUDENTS_GROUP){
29428 		StudentsGroup* stg=(StudentsGroup*)ss;
29429 		for(int i=0; i<stg->subgroupsList.size(); i++){
29430 			StudentsSubgroup* sts=stg->subgroupsList[i];
29431 			int tmp;
29432 			tmp=sts->indexInInternalSubgroupsList;
29433 			assert(tmp>=0);
29434 			assert(tmp<r.nInternalSubgroups);
29435 			if(!this->iSubgroupsList.contains(tmp))
29436 				this->iSubgroupsList.append(tmp);
29437 		}
29438 	}
29439 	else if(ss->type==STUDENTS_YEAR){
29440 		StudentsYear* sty=(StudentsYear*)ss;
29441 		for(int i=0; i<sty->groupsList.size(); i++){
29442 			StudentsGroup* stg=sty->groupsList[i];
29443 			for(int j=0; j<stg->subgroupsList.size(); j++){
29444 				StudentsSubgroup* sts=stg->subgroupsList[j];
29445 				int tmp;
29446 				tmp=sts->indexInInternalSubgroupsList;
29447 				assert(tmp>=0);
29448 				assert(tmp<r.nInternalSubgroups);
29449 				if(!this->iSubgroupsList.contains(tmp))
29450 					this->iSubgroupsList.append(tmp);
29451 			}
29452 		}
29453 	}
29454 	else
29455 		assert(0);*/
29456 
29457 	return true;
29458 }
29459 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)29460 double ConstraintStudentsSetMaxHoursDailyRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
29461 {
29462 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
29463 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
29464 		c.teachersMatrixReady=true;
29465 		c.subgroupsMatrixReady=true;
29466 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
29467 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
29468 
29469 		c.changedForMatrixCalculation=false;
29470 	}
29471 
29472 	int tmp;
29473 	int too_much;
29474 
29475 	assert(this->maxHoursDaily>=0);
29476 
29477 	if(1){
29478 		too_much=0;
29479 		for(int sg=0; sg<this->iSubgroupsList.count(); sg++){
29480 			int i=iSubgroupsList.at(sg);
29481 			for(int j=0; j<r.nDaysPerWeek/2; j++){
29482 				tmp=0;
29483 				for(int k=0; k<r.nHoursPerDay; k++){
29484 					if(subgroupsMatrix[i][2*j][k]>=1)
29485 						tmp++;
29486 				}
29487 				for(int k=0; k<r.nHoursPerDay; k++){
29488 					if(subgroupsMatrix[i][2*j+1][k]>=1)
29489 						tmp++;
29490 				}
29491 				if(this->maxHoursDaily>=0 && tmp > this->maxHoursDaily){ //we would like no more than max_hours_daily hours per day.
29492 					too_much += 1; //tmp - this->maxHoursDaily;
29493 
29494 					if(conflictsString!=nullptr){
29495 						QString s=tr("Time constraint students set max hours daily per real day broken for subgroup: %1, real day: %2, length=%3, conflicts increase=%4")
29496 						 .arg(r.internalSubgroupsList[i]->name)
29497 						 .arg(j/*r.daysOfTheWeek[j]*/)
29498 						 .arg(CustomFETString::number(tmp))
29499 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision( 1 *weightPercentage/100));
29500 
29501 						dl.append(s);
29502 						cl.append( 1 *weightPercentage/100);
29503 
29504 						*conflictsString+= s+"\n";
29505 					}
29506 				}
29507 			}
29508 		}
29509 	}
29510 
29511 	assert(too_much>=0);
29512 	if(weightPercentage==100)
29513 		assert(too_much==0);
29514 	return too_much * weightPercentage / 100.0;
29515 }
29516 
isRelatedToActivity(Rules & r,Activity * a)29517 bool ConstraintStudentsSetMaxHoursDailyRealDays::isRelatedToActivity(Rules& r, Activity* a)
29518 {
29519 	Q_UNUSED(r);
29520 	Q_UNUSED(a);
29521 
29522 	return false;
29523 }
29524 
isRelatedToTeacher(Teacher * t)29525 bool ConstraintStudentsSetMaxHoursDailyRealDays::isRelatedToTeacher(Teacher* t)
29526 {
29527 	Q_UNUSED(t);
29528 
29529 	return false;
29530 }
29531 
isRelatedToSubject(Subject * s)29532 bool ConstraintStudentsSetMaxHoursDailyRealDays::isRelatedToSubject(Subject* s)
29533 {
29534 	Q_UNUSED(s);
29535 
29536 	return false;
29537 }
29538 
isRelatedToActivityTag(ActivityTag * s)29539 bool ConstraintStudentsSetMaxHoursDailyRealDays::isRelatedToActivityTag(ActivityTag* s)
29540 {
29541 	Q_UNUSED(s);
29542 
29543 	return false;
29544 }
29545 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)29546 bool ConstraintStudentsSetMaxHoursDailyRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
29547 {
29548 	return r.setsShareStudents(this->students, s->name);
29549 }
29550 
hasWrongDayOrHour(Rules & r)29551 bool ConstraintStudentsSetMaxHoursDailyRealDays::hasWrongDayOrHour(Rules& r)
29552 {
29553 	if(maxHoursDaily>2*r.nHoursPerDay)
29554 		return true;
29555 
29556 	return false;
29557 }
29558 
canRepairWrongDayOrHour(Rules & r)29559 bool ConstraintStudentsSetMaxHoursDailyRealDays::canRepairWrongDayOrHour(Rules& r)
29560 {
29561 	assert(hasWrongDayOrHour(r));
29562 
29563 	return true;
29564 }
29565 
repairWrongDayOrHour(Rules & r)29566 bool ConstraintStudentsSetMaxHoursDailyRealDays::repairWrongDayOrHour(Rules& r)
29567 {
29568 	assert(hasWrongDayOrHour(r));
29569 
29570 	if(maxHoursDaily>2*r.nHoursPerDay)
29571 		maxHoursDaily=2*r.nHoursPerDay;
29572 
29573 	return true;
29574 }
29575 
29576 ////////////////////////////////////////////////////////////////////////////////////////////
29577 ////////////////////////////////////////////////////////////////////////////////////////////
29578 
ConstraintTeachersMinHoursPerMorning()29579 ConstraintTeachersMinHoursPerMorning::ConstraintTeachersMinHoursPerMorning()
29580 	: TimeConstraint()
29581 {
29582 	this->type=CONSTRAINT_TEACHERS_MIN_HOURS_PER_MORNING;
29583 
29584 	this->allowEmptyMornings=true;
29585 }
29586 
ConstraintTeachersMinHoursPerMorning(double wp,int minhours,bool _allowEmptyMornings)29587 ConstraintTeachersMinHoursPerMorning::ConstraintTeachersMinHoursPerMorning(double wp, int minhours, bool _allowEmptyMornings)
29588  : TimeConstraint(wp)
29589  {
29590 	assert(minhours>0);
29591 	this->minHoursPerMorning=minhours;
29592 
29593 	this->allowEmptyMornings=_allowEmptyMornings;
29594 
29595 	this->type=CONSTRAINT_TEACHERS_MIN_HOURS_PER_MORNING;
29596 }
29597 
computeInternalStructure(QWidget * parent,Rules & r)29598 bool ConstraintTeachersMinHoursPerMorning::computeInternalStructure(QWidget* parent, Rules& r)
29599 {
29600 	Q_UNUSED(r);
29601 
29602 	if(allowEmptyMornings==false){
29603 		QString s=tr("Cannot generate a timetable with a constraint teachers min hours per morning with allow empty days=false. Please modify it,"
29604 			" so that it allows empty days. If you need a facility like that, please use constraint teachers min days per week");
29605 		s+="\n\n";
29606 		s+=tr("Constraint is:")+"\n"+this->getDetailedDescription(r);
29607 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"), s);
29608 
29609 		return false;
29610 	}
29611 
29612 	return true;
29613 }
29614 
hasInactiveActivities(Rules & r)29615 bool ConstraintTeachersMinHoursPerMorning::hasInactiveActivities(Rules& r)
29616 {
29617 	Q_UNUSED(r);
29618 	return false;
29619 }
29620 
getXmlDescription(Rules & r)29621 QString ConstraintTeachersMinHoursPerMorning::getXmlDescription(Rules& r){
29622 	Q_UNUSED(r);
29623 
29624 	QString s="<ConstraintTeachersMinHoursPerMorning>\n";
29625 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
29626 	s+="	<Minimum_Hours_Per_Morning>"+CustomFETString::number(this->minHoursPerMorning)+"</Minimum_Hours_Per_Morning>\n";
29627 	if(this->allowEmptyMornings)
29628 		s+="	<Allow_Empty_Mornings>true</Allow_Empty_Mornings>\n";
29629 	else
29630 		s+="	<Allow_Empty_Mornings>false</Allow_Empty_Mornings>\n";
29631 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
29632 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
29633 	s+="</ConstraintTeachersMinHoursPerMorning>\n";
29634 	return s;
29635 }
29636 
getDescription(Rules & r)29637 QString ConstraintTeachersMinHoursPerMorning::getDescription(Rules& r){
29638 	Q_UNUSED(r);
29639 
29640 	QString begin=QString("");
29641 	if(!active)
29642 		begin="X - ";
29643 
29644 	QString end=QString("");
29645 	if(!comments.isEmpty())
29646 		end=", "+tr("C: %1", "Comments").arg(comments);
29647 
29648 	QString s;
29649 	s+=tr("Teachers min hours per morning");s+=", ";
29650 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
29651 	s+=tr("mH:%1", "Min hours (per morning)").arg(this->minHoursPerMorning);s+=", ";
29652 	s+=tr("AEM:%1", "Allow empty mornings").arg(yesNoTranslated(this->allowEmptyMornings));
29653 
29654 	return begin+s+end;
29655 }
29656 
getDetailedDescription(Rules & r)29657 QString ConstraintTeachersMinHoursPerMorning::getDetailedDescription(Rules& r){
29658 	Q_UNUSED(r);
29659 
29660 	QString s=tr("Time constraint");s+="\n";
29661 	s+=tr("All teachers must respect the minimum number of hours per morning"); s+="\n";
29662 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
29663 	s+=tr("Minimum hours per morning=%1").arg(this->minHoursPerMorning);s+="\n";
29664 	s+=tr("Allow empty mornings=%1").arg(yesNoTranslated(this->allowEmptyMornings));s+="\n";
29665 
29666 	if(!active){
29667 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
29668 		s+="\n";
29669 	}
29670 	if(!comments.isEmpty()){
29671 		s+=tr("Comments=%1").arg(comments);
29672 		s+="\n";
29673 	}
29674 
29675 	return s;
29676 }
29677 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)29678 double ConstraintTeachersMinHoursPerMorning::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
29679 {
29680 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
29681 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
29682 		c.teachersMatrixReady=true;
29683 		c.subgroupsMatrixReady=true;
29684 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
29685 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
29686 
29687 		c.changedForMatrixCalculation=false;
29688 	}
29689 
29690 	assert(this->allowEmptyMornings==true);
29691 
29692 	int nbroken;
29693 
29694 	//without logging
29695 	if(conflictsString==nullptr){
29696 		nbroken=0;
29697 		for(int i=0; i<r.nInternalTeachers; i++){
29698 			for(int d=0; d<r.nDaysPerWeek; d++){
29699 				if(d%2==1)
29700 					continue;
29701 				int n_hours_per_morning=0;
29702 				for(int h=0; h<r.nHoursPerDay; h++)
29703 					if(teachersMatrix[i][d][h]>0)
29704 						n_hours_per_morning++;
29705 
29706 				if(n_hours_per_morning>0 && n_hours_per_morning<this->minHoursPerMorning){
29707 					nbroken++;
29708 				}
29709 			}
29710 		}
29711 	}
29712 	//with logging
29713 	else{
29714 		nbroken=0;
29715 		for(int i=0; i<r.nInternalTeachers; i++){
29716 			for(int d=0; d<r.nDaysPerWeek; d++){
29717 				if(d%2==1)
29718 					continue;
29719 				int n_hours_per_morning=0;
29720 				for(int h=0; h<r.nHoursPerDay; h++)
29721 					if(teachersMatrix[i][d][h]>0)
29722 						n_hours_per_morning++;
29723 
29724 				if(n_hours_per_morning>0 && n_hours_per_morning<this->minHoursPerMorning){
29725 					nbroken++;
29726 
29727 					if(conflictsString!=nullptr){
29728 						QString s=(tr("Time constraint teachers min %1 hours per morning broken for teacher %2, on day %3, length=%4.")
29729 						 .arg(CustomFETString::number(this->minHoursPerMorning))
29730 						 .arg(r.internalTeachersList[i]->name)
29731 						 .arg(r.daysOfTheWeek[d])
29732 						 .arg(n_hours_per_morning)
29733 						 )
29734 						 +
29735 						 " "
29736 						 +
29737 						 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
29738 
29739 						dl.append(s);
29740 						cl.append(weightPercentage/100);
29741 
29742 						*conflictsString+= s+"\n";
29743 					}
29744 				}
29745 			}
29746 		}
29747 	}
29748 
29749 	if(c.nPlacedActivities==r.nInternalActivities)
29750 		if(weightPercentage==100)
29751 			assert(nbroken==0);
29752 	return weightPercentage/100 * nbroken;
29753 }
29754 
isRelatedToActivity(Rules & r,Activity * a)29755 bool ConstraintTeachersMinHoursPerMorning::isRelatedToActivity(Rules& r, Activity* a)
29756 {
29757 	Q_UNUSED(a);
29758 	Q_UNUSED(r);
29759 
29760 	return false;
29761 }
29762 
isRelatedToTeacher(Teacher * t)29763 bool ConstraintTeachersMinHoursPerMorning::isRelatedToTeacher(Teacher* t)
29764 {
29765 	Q_UNUSED(t);
29766 
29767 	return true;
29768 }
29769 
isRelatedToSubject(Subject * s)29770 bool ConstraintTeachersMinHoursPerMorning::isRelatedToSubject(Subject* s)
29771 {
29772 	Q_UNUSED(s);
29773 
29774 	return false;
29775 }
29776 
isRelatedToActivityTag(ActivityTag * s)29777 bool ConstraintTeachersMinHoursPerMorning::isRelatedToActivityTag(ActivityTag* s)
29778 {
29779 	Q_UNUSED(s);
29780 
29781 	return false;
29782 }
29783 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)29784 bool ConstraintTeachersMinHoursPerMorning::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
29785 {
29786 	Q_UNUSED(r);
29787 	Q_UNUSED(s);
29788 
29789 	return false;
29790 }
29791 
hasWrongDayOrHour(Rules & r)29792 bool ConstraintTeachersMinHoursPerMorning::hasWrongDayOrHour(Rules& r)
29793 {
29794 	if(minHoursPerMorning>r.nHoursPerDay)
29795 		return true;
29796 
29797 	return false;
29798 }
29799 
canRepairWrongDayOrHour(Rules & r)29800 bool ConstraintTeachersMinHoursPerMorning::canRepairWrongDayOrHour(Rules& r)
29801 {
29802 	assert(hasWrongDayOrHour(r));
29803 
29804 	return true;
29805 }
29806 
repairWrongDayOrHour(Rules & r)29807 bool ConstraintTeachersMinHoursPerMorning::repairWrongDayOrHour(Rules& r)
29808 {
29809 	assert(hasWrongDayOrHour(r));
29810 
29811 	if(minHoursPerMorning>r.nHoursPerDay)
29812 		minHoursPerMorning=r.nHoursPerDay;
29813 
29814 	return true;
29815 }
29816 
29817 ///////////////////////////////////////////////////////////////////////////////////////////
29818 ///////////////////////////////////////////////////////////////////////////////////////////
29819 
ConstraintTeacherMinHoursPerMorning()29820 ConstraintTeacherMinHoursPerMorning::ConstraintTeacherMinHoursPerMorning()
29821 	: TimeConstraint()
29822 {
29823 	this->type=CONSTRAINT_TEACHER_MIN_HOURS_PER_MORNING;
29824 
29825 	this->allowEmptyMornings=true;
29826 }
29827 
ConstraintTeacherMinHoursPerMorning(double wp,int minhours,const QString & teacher,bool _allowEmptyMornings)29828 ConstraintTeacherMinHoursPerMorning::ConstraintTeacherMinHoursPerMorning(double wp, int minhours, const QString& teacher, bool _allowEmptyMornings)
29829  : TimeConstraint(wp)
29830  {
29831 	assert(minhours>0);
29832 	this->minHoursPerMorning=minhours;
29833 	this->teacherName=teacher;
29834 
29835 	this->allowEmptyMornings=_allowEmptyMornings;
29836 
29837 	this->type=CONSTRAINT_TEACHER_MIN_HOURS_PER_MORNING;
29838 }
29839 
computeInternalStructure(QWidget * parent,Rules & r)29840 bool ConstraintTeacherMinHoursPerMorning::computeInternalStructure(QWidget* parent, Rules& r)
29841 {
29842 	//this->teacher_ID=r.searchTeacher(this->teacherName);
29843 	teacher_ID=r.teachersHash.value(teacherName, -1);
29844 	assert(this->teacher_ID>=0);
29845 
29846 	if(allowEmptyMornings==false){
29847 		QString s=tr("Cannot generate a timetable with a constraint teacher min hours per morning with allow empty days=false. Please modify it,"
29848 			" so that it allows empty days. If you need a facility like that, please use constraint teacher min days per week");
29849 		s+="\n\n";
29850 		s+=tr("Constraint is:")+"\n"+this->getDetailedDescription(r);
29851 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"), s);
29852 
29853 		return false;
29854 	}
29855 
29856 	return true;
29857 }
29858 
hasInactiveActivities(Rules & r)29859 bool ConstraintTeacherMinHoursPerMorning::hasInactiveActivities(Rules& r)
29860 {
29861 	Q_UNUSED(r);
29862 	return false;
29863 }
29864 
getXmlDescription(Rules & r)29865 QString ConstraintTeacherMinHoursPerMorning::getXmlDescription(Rules& r){
29866 	Q_UNUSED(r);
29867 
29868 	QString s="<ConstraintTeacherMinHoursPerMorning>\n";
29869 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
29870 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
29871 	s+="	<Minimum_Hours_Per_Morning>"+CustomFETString::number(this->minHoursPerMorning)+"</Minimum_Hours_Per_Morning>\n";
29872 	if(this->allowEmptyMornings)
29873 		s+="	<Allow_Empty_Mornings>true</Allow_Empty_Mornings>\n";
29874 	else
29875 		s+="	<Allow_Empty_Mornings>false</Allow_Empty_Mornings>\n";
29876 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
29877 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
29878 	s+="</ConstraintTeacherMinHoursPerMorning>\n";
29879 	return s;
29880 }
29881 
getDescription(Rules & r)29882 QString ConstraintTeacherMinHoursPerMorning::getDescription(Rules& r){
29883 	Q_UNUSED(r);
29884 
29885 	QString begin=QString("");
29886 	if(!active)
29887 		begin="X - ";
29888 
29889 	QString end=QString("");
29890 	if(!comments.isEmpty())
29891 		end=", "+tr("C: %1", "Comments").arg(comments);
29892 
29893 	QString s;
29894 	s+=tr("Teacher min hours per morning");s+=", ";
29895 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
29896 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
29897 	s+=tr("mH:%1", "Minimum hours (per morning)").arg(this->minHoursPerMorning);s+=", ";
29898 	s+=tr("AEM:%1", "Allow empty mornings").arg(yesNoTranslated(this->allowEmptyMornings));
29899 
29900 	return begin+s+end;
29901 }
29902 
getDetailedDescription(Rules & r)29903 QString ConstraintTeacherMinHoursPerMorning::getDetailedDescription(Rules& r){
29904 	Q_UNUSED(r);
29905 
29906 	QString s=tr("Time constraint");s+="\n";
29907 	s+=tr("A teacher must respect the minimum number of hours per morning");s+="\n";
29908 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
29909 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
29910 	s+=tr("Minimum hours per morning=%1").arg(this->minHoursPerMorning);s+="\n";
29911 	s+=tr("Allow empty mornings=%1").arg(yesNoTranslated(this->allowEmptyMornings));s+="\n";
29912 
29913 	if(!active){
29914 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
29915 		s+="\n";
29916 	}
29917 	if(!comments.isEmpty()){
29918 		s+=tr("Comments=%1").arg(comments);
29919 		s+="\n";
29920 	}
29921 
29922 	return s;
29923 }
29924 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)29925 double ConstraintTeacherMinHoursPerMorning::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
29926 {
29927 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
29928 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
29929 		c.teachersMatrixReady=true;
29930 		c.subgroupsMatrixReady=true;
29931 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
29932 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
29933 
29934 		c.changedForMatrixCalculation=false;
29935 	}
29936 
29937 	assert(this->allowEmptyMornings==true);
29938 
29939 	int nbroken;
29940 
29941 	//without logging
29942 	if(conflictsString==nullptr){
29943 		nbroken=0;
29944 		int i=this->teacher_ID;
29945 		for(int d=0; d<r.nDaysPerWeek; d++){
29946 			if(d%2==1)
29947 				continue;
29948 			int n_hours_per_morning=0;
29949 			for(int h=0; h<r.nHoursPerDay; h++)
29950 				if(teachersMatrix[i][d][h]>0)
29951 					n_hours_per_morning++;
29952 
29953 			if(n_hours_per_morning>0 && n_hours_per_morning<this->minHoursPerMorning){
29954 				nbroken++;
29955 			}
29956 		}
29957 	}
29958 	//with logging
29959 	else{
29960 		nbroken=0;
29961 		int i=this->teacher_ID;
29962 		for(int d=0; d<r.nDaysPerWeek; d++){
29963 			if(d%2==1)
29964 				continue;
29965 			int n_hours_per_morning=0;
29966 			for(int h=0; h<r.nHoursPerDay; h++)
29967 				if(teachersMatrix[i][d][h]>0)
29968 					n_hours_per_morning++;
29969 
29970 			if(n_hours_per_morning>0 && n_hours_per_morning<this->minHoursPerMorning){
29971 				nbroken++;
29972 
29973 				if(conflictsString!=nullptr){
29974 					QString s=(tr(
29975 					 "Time constraint teacher min %1 hours per morning broken for teacher %2, on day %3, length=%4.")
29976 					 .arg(CustomFETString::number(this->minHoursPerMorning))
29977 					 .arg(r.internalTeachersList[i]->name)
29978 					 .arg(r.daysOfTheWeek[d])
29979 					 .arg(n_hours_per_morning)
29980 					 )
29981 					 +" "
29982 					 +
29983 					 tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100));
29984 
29985 					dl.append(s);
29986 					cl.append(weightPercentage/100);
29987 
29988 					*conflictsString+= s+"\n";
29989 				}
29990 			}
29991 		}
29992 	}
29993 
29994 	if(c.nPlacedActivities==r.nInternalActivities)
29995 		if(weightPercentage==100)
29996 			assert(nbroken==0);
29997 
29998 	return weightPercentage/100 * nbroken;
29999 }
30000 
isRelatedToActivity(Rules & r,Activity * a)30001 bool ConstraintTeacherMinHoursPerMorning::isRelatedToActivity(Rules& r, Activity* a)
30002 {
30003 	Q_UNUSED(r);
30004 	Q_UNUSED(a);
30005 
30006 	return false;
30007 }
30008 
isRelatedToTeacher(Teacher * t)30009 bool ConstraintTeacherMinHoursPerMorning::isRelatedToTeacher(Teacher* t)
30010 {
30011 	if(this->teacherName==t->name)
30012 		return true;
30013 	return false;
30014 }
30015 
isRelatedToSubject(Subject * s)30016 bool ConstraintTeacherMinHoursPerMorning::isRelatedToSubject(Subject* s)
30017 {
30018 	Q_UNUSED(s);
30019 
30020 	return false;
30021 }
30022 
isRelatedToActivityTag(ActivityTag * s)30023 bool ConstraintTeacherMinHoursPerMorning::isRelatedToActivityTag(ActivityTag* s)
30024 {
30025 	Q_UNUSED(s);
30026 
30027 	return false;
30028 }
30029 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)30030 bool ConstraintTeacherMinHoursPerMorning::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
30031 {
30032 	Q_UNUSED(r);
30033 	Q_UNUSED(s);
30034 
30035 	return false;
30036 }
30037 
hasWrongDayOrHour(Rules & r)30038 bool ConstraintTeacherMinHoursPerMorning::hasWrongDayOrHour(Rules& r)
30039 {
30040 	if(minHoursPerMorning>r.nHoursPerDay)
30041 		return true;
30042 
30043 	return false;
30044 }
30045 
canRepairWrongDayOrHour(Rules & r)30046 bool ConstraintTeacherMinHoursPerMorning::canRepairWrongDayOrHour(Rules& r)
30047 {
30048 	assert(hasWrongDayOrHour(r));
30049 
30050 	return true;
30051 }
30052 
repairWrongDayOrHour(Rules & r)30053 bool ConstraintTeacherMinHoursPerMorning::repairWrongDayOrHour(Rules& r)
30054 {
30055 	assert(hasWrongDayOrHour(r));
30056 
30057 	if(minHoursPerMorning>r.nHoursPerDay)
30058 		minHoursPerMorning=r.nHoursPerDay;
30059 
30060 	return true;
30061 }
30062 
30063 ///////////////////////////////////////////////////////////////////////////////////////////
30064 ///////////////////////////////////////////////////////////////////////////////////////////
30065 
ConstraintTeachersMinHoursDailyRealDays()30066 ConstraintTeachersMinHoursDailyRealDays::ConstraintTeachersMinHoursDailyRealDays()
30067 	: TimeConstraint()
30068 {
30069 	this->type=CONSTRAINT_TEACHERS_MIN_HOURS_DAILY_REAL_DAYS;
30070 
30071 	this->allowEmptyDays=true;
30072 }
30073 
ConstraintTeachersMinHoursDailyRealDays(double wp,int minhours,bool _allowEmptyDays)30074 ConstraintTeachersMinHoursDailyRealDays::ConstraintTeachersMinHoursDailyRealDays(double wp, int minhours, bool _allowEmptyDays)
30075  : TimeConstraint(wp)
30076  {
30077 	assert(minhours>0);
30078 	this->minHoursDaily=minhours;
30079 
30080 	this->allowEmptyDays=_allowEmptyDays;
30081 
30082 	this->type=CONSTRAINT_TEACHERS_MIN_HOURS_DAILY_REAL_DAYS;
30083 }
30084 
computeInternalStructure(QWidget * parent,Rules & r)30085 bool ConstraintTeachersMinHoursDailyRealDays::computeInternalStructure(QWidget* parent, Rules& r)
30086 {
30087 	Q_UNUSED(r);
30088 
30089 	if(allowEmptyDays==false){
30090 		QString s=tr("Cannot generate a timetable with a constraint teachers min hours daily for real days with allow empty days=false. Please modify it,"
30091 			" so that it allows empty days. If you need a facility like that, please use constraint teachers min days per week");
30092 		s+="\n\n";
30093 		s+=tr("Constraint is:")+"\n"+this->getDetailedDescription(r);
30094 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"), s);
30095 
30096 		return false;
30097 	}
30098 
30099 	return true;
30100 }
30101 
hasInactiveActivities(Rules & r)30102 bool ConstraintTeachersMinHoursDailyRealDays::hasInactiveActivities(Rules& r)
30103 {
30104 	Q_UNUSED(r);
30105 	return false;
30106 }
30107 
getXmlDescription(Rules & r)30108 QString ConstraintTeachersMinHoursDailyRealDays::getXmlDescription(Rules& r){
30109 	Q_UNUSED(r);
30110 
30111 	QString s="<ConstraintTeachersMinHoursDailyRealDays>\n";
30112 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
30113 	s+="	<Minimum_Hours_Daily>"+CustomFETString::number(this->minHoursDaily)+"</Minimum_Hours_Daily>\n";
30114 	if(this->allowEmptyDays)
30115 		s+="	<Allow_Empty_Days>true</Allow_Empty_Days>\n";
30116 	else
30117 		s+="	<Allow_Empty_Days>false</Allow_Empty_Days>\n";
30118 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
30119 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
30120 	s+="</ConstraintTeachersMinHoursDailyRealDays>\n";
30121 	return s;
30122 }
30123 
getDescription(Rules & r)30124 QString ConstraintTeachersMinHoursDailyRealDays::getDescription(Rules& r){
30125 	Q_UNUSED(r);
30126 
30127 	QString begin=QString("");
30128 	if(!active)
30129 		begin="X - ";
30130 
30131 	QString end=QString("");
30132 	if(!comments.isEmpty())
30133 		end=", "+tr("C: %1", "Comments").arg(comments);
30134 
30135 	QString s;
30136 	s+=tr("Teachers min hours daily for real days");s+=", ";
30137 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
30138 	s+=tr("mH:%1", "Min hours (daily)").arg(this->minHoursDaily);s+=", ";
30139 	s+=tr("AED:%1", "Allow empty days").arg(yesNoTranslated(this->allowEmptyDays));
30140 
30141 	return begin+s+end;
30142 }
30143 
getDetailedDescription(Rules & r)30144 QString ConstraintTeachersMinHoursDailyRealDays::getDetailedDescription(Rules& r){
30145 	Q_UNUSED(r);
30146 
30147 	QString s=tr("Time constraint");s+="\n";
30148 	s+=tr("All teachers must respect the minimum number of hours daily for real days"); s+="\n";
30149 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
30150 	s+=tr("Minimum hours daily=%1").arg(this->minHoursDaily);s+="\n";
30151 	s+=tr("Allow empty days=%1").arg(yesNoTranslated(this->allowEmptyDays));s+="\n";
30152 
30153 	if(!active){
30154 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
30155 		s+="\n";
30156 	}
30157 	if(!comments.isEmpty()){
30158 		s+=tr("Comments=%1").arg(comments);
30159 		s+="\n";
30160 	}
30161 
30162 	return s;
30163 }
30164 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)30165 double ConstraintTeachersMinHoursDailyRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
30166 {
30167 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
30168 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
30169 		c.teachersMatrixReady=true;
30170 		c.subgroupsMatrixReady=true;
30171 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
30172 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
30173 
30174 		c.changedForMatrixCalculation=false;
30175 	}
30176 
30177 	assert(this->allowEmptyDays==true);
30178 
30179 	int nbroken;
30180 
30181 	//without logging
30182 	if(conflictsString==nullptr){
30183 		nbroken=0;
30184 		for(int i=0; i<r.nInternalTeachers; i++){
30185 			for(int d=0; d<r.nDaysPerWeek/2; d++){
30186 				int n_hours_daily=0;
30187 				for(int h=0; h<r.nHoursPerDay; h++)
30188 					if(teachersMatrix[i][2*d][h]>0)
30189 						n_hours_daily++;
30190 				for(int h=0; h<r.nHoursPerDay; h++)
30191 					if(teachersMatrix[i][2*d+1][h]>0)
30192 						n_hours_daily++;
30193 
30194 				if(n_hours_daily>0 && n_hours_daily<this->minHoursDaily){
30195 					nbroken++;
30196 				}
30197 			}
30198 		}
30199 	}
30200 	//with logging
30201 	else{
30202 		nbroken=0;
30203 		for(int i=0; i<r.nInternalTeachers; i++){
30204 			for(int d=0; d<r.nDaysPerWeek/2; d++){
30205 				int n_hours_daily=0;
30206 				for(int h=0; h<r.nHoursPerDay; h++)
30207 					if(teachersMatrix[i][2*d][h]>0)
30208 						n_hours_daily++;
30209 				for(int h=0; h<r.nHoursPerDay; h++)
30210 					if(teachersMatrix[i][2*d+1][h]>0)
30211 						n_hours_daily++;
30212 
30213 				if(n_hours_daily>0 && n_hours_daily<this->minHoursDaily){
30214 					nbroken++;
30215 
30216 					if(conflictsString!=nullptr){
30217 						QString s=(tr("Time constraint teachers min %1 hours daily for real days broken for teacher %2, on real day %3, length=%4.")
30218 						 .arg(CustomFETString::number(this->minHoursDaily))
30219 						 .arg(r.internalTeachersList[i]->name)
30220 						 .arg(d/*r.daysOfTheWeek[d]*/)
30221 						 .arg(n_hours_daily)
30222 						 )
30223 						 +
30224 						 " "
30225 						 +
30226 						 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100)));
30227 
30228 						dl.append(s);
30229 						cl.append(weightPercentage/100);
30230 
30231 						*conflictsString+= s+"\n";
30232 					}
30233 				}
30234 			}
30235 		}
30236 	}
30237 
30238 	if(c.nPlacedActivities==r.nInternalActivities)
30239 		if(weightPercentage==100)
30240 			assert(nbroken==0);
30241 	return weightPercentage/100 * nbroken;
30242 }
30243 
isRelatedToActivity(Rules & r,Activity * a)30244 bool ConstraintTeachersMinHoursDailyRealDays::isRelatedToActivity(Rules& r, Activity* a)
30245 {
30246 	Q_UNUSED(a);
30247 	Q_UNUSED(r);
30248 
30249 	return false;
30250 }
30251 
isRelatedToTeacher(Teacher * t)30252 bool ConstraintTeachersMinHoursDailyRealDays::isRelatedToTeacher(Teacher* t)
30253 {
30254 	Q_UNUSED(t);
30255 
30256 	return true;
30257 }
30258 
isRelatedToSubject(Subject * s)30259 bool ConstraintTeachersMinHoursDailyRealDays::isRelatedToSubject(Subject* s)
30260 {
30261 	Q_UNUSED(s);
30262 
30263 	return false;
30264 }
30265 
isRelatedToActivityTag(ActivityTag * s)30266 bool ConstraintTeachersMinHoursDailyRealDays::isRelatedToActivityTag(ActivityTag* s)
30267 {
30268 	Q_UNUSED(s);
30269 
30270 	return false;
30271 }
30272 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)30273 bool ConstraintTeachersMinHoursDailyRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
30274 {
30275 	Q_UNUSED(r);
30276 	Q_UNUSED(s);
30277 
30278 	return false;
30279 }
30280 
hasWrongDayOrHour(Rules & r)30281 bool ConstraintTeachersMinHoursDailyRealDays::hasWrongDayOrHour(Rules& r)
30282 {
30283 	if(minHoursDaily>2*r.nHoursPerDay)
30284 		return true;
30285 
30286 	return false;
30287 }
30288 
canRepairWrongDayOrHour(Rules & r)30289 bool ConstraintTeachersMinHoursDailyRealDays::canRepairWrongDayOrHour(Rules& r)
30290 {
30291 	assert(hasWrongDayOrHour(r));
30292 
30293 	return true;
30294 }
30295 
repairWrongDayOrHour(Rules & r)30296 bool ConstraintTeachersMinHoursDailyRealDays::repairWrongDayOrHour(Rules& r)
30297 {
30298 	assert(hasWrongDayOrHour(r));
30299 
30300 	if(minHoursDaily>2*r.nHoursPerDay)
30301 		minHoursDaily=2*r.nHoursPerDay;
30302 
30303 	return true;
30304 }
30305 
30306 ///////////////////////////////////////////////////////////////////////////////////////////
30307 ///////////////////////////////////////////////////////////////////////////////////////////
30308 
ConstraintTeacherMinHoursDailyRealDays()30309 ConstraintTeacherMinHoursDailyRealDays::ConstraintTeacherMinHoursDailyRealDays()
30310 	: TimeConstraint()
30311 {
30312 	this->type=CONSTRAINT_TEACHER_MIN_HOURS_DAILY_REAL_DAYS;
30313 
30314 	this->allowEmptyDays=true;
30315 }
30316 
ConstraintTeacherMinHoursDailyRealDays(double wp,int minhours,const QString & teacher,bool _allowEmptyDays)30317 ConstraintTeacherMinHoursDailyRealDays::ConstraintTeacherMinHoursDailyRealDays(double wp, int minhours, const QString& teacher, bool _allowEmptyDays)
30318  : TimeConstraint(wp)
30319  {
30320 	assert(minhours>0);
30321 	this->minHoursDaily=minhours;
30322 	this->teacherName=teacher;
30323 
30324 	this->allowEmptyDays=_allowEmptyDays;
30325 
30326 	this->type=CONSTRAINT_TEACHER_MIN_HOURS_DAILY_REAL_DAYS;
30327 }
30328 
computeInternalStructure(QWidget * parent,Rules & r)30329 bool ConstraintTeacherMinHoursDailyRealDays::computeInternalStructure(QWidget* parent, Rules& r)
30330 {
30331 	//this->teacher_ID=r.searchTeacher(this->teacherName);
30332 	teacher_ID=r.teachersHash.value(teacherName, -1);
30333 	assert(this->teacher_ID>=0);
30334 
30335 	if(allowEmptyDays==false){
30336 		QString s=tr("Cannot generate a timetable with a constraint teacher min hours daily for real days with allow empty days=false. Please modify it,"
30337 			" so that it allows empty days. If you need a facility like that, please use constraint teacher min days per week");
30338 		s+="\n\n";
30339 		s+=tr("Constraint is:")+"\n"+this->getDetailedDescription(r);
30340 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"), s);
30341 
30342 		return false;
30343 	}
30344 
30345 	return true;
30346 }
30347 
hasInactiveActivities(Rules & r)30348 bool ConstraintTeacherMinHoursDailyRealDays::hasInactiveActivities(Rules& r)
30349 {
30350 	Q_UNUSED(r);
30351 	return false;
30352 }
30353 
getXmlDescription(Rules & r)30354 QString ConstraintTeacherMinHoursDailyRealDays::getXmlDescription(Rules& r){
30355 	Q_UNUSED(r);
30356 
30357 	QString s="<ConstraintTeacherMinHoursDailyRealDays>\n";
30358 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
30359 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
30360 	s+="	<Minimum_Hours_Daily>"+CustomFETString::number(this->minHoursDaily)+"</Minimum_Hours_Daily>\n";
30361 	if(this->allowEmptyDays)
30362 		s+="	<Allow_Empty_Days>true</Allow_Empty_Days>\n";
30363 	else
30364 		s+="	<Allow_Empty_Days>false</Allow_Empty_Days>\n";
30365 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
30366 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
30367 	s+="</ConstraintTeacherMinHoursDailyRealDays>\n";
30368 	return s;
30369 }
30370 
getDescription(Rules & r)30371 QString ConstraintTeacherMinHoursDailyRealDays::getDescription(Rules& r){
30372 	Q_UNUSED(r);
30373 
30374 	QString begin=QString("");
30375 	if(!active)
30376 		begin="X - ";
30377 
30378 	QString end=QString("");
30379 	if(!comments.isEmpty())
30380 		end=", "+tr("C: %1", "Comments").arg(comments);
30381 
30382 	QString s;
30383 	s+=tr("Teacher min hours daily for real days");s+=", ";
30384 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
30385 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
30386 	s+=tr("mH:%1", "Minimum hours (daily)").arg(this->minHoursDaily);s+=", ";
30387 	s+=tr("AED:%1", "Allow empty days").arg(yesNoTranslated(this->allowEmptyDays));
30388 
30389 	return begin+s+end;
30390 }
30391 
getDetailedDescription(Rules & r)30392 QString ConstraintTeacherMinHoursDailyRealDays::getDetailedDescription(Rules& r){
30393 	Q_UNUSED(r);
30394 
30395 	QString s=tr("Time constraint");s+="\n";
30396 	s+=tr("A teacher must respect the minimum number of hours daily for real days");s+="\n";
30397 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
30398 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
30399 	s+=tr("Minimum hours daily=%1").arg(this->minHoursDaily);s+="\n";
30400 	s+=tr("Allow empty days=%1").arg(yesNoTranslated(this->allowEmptyDays));s+="\n";
30401 
30402 	if(!active){
30403 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
30404 		s+="\n";
30405 	}
30406 	if(!comments.isEmpty()){
30407 		s+=tr("Comments=%1").arg(comments);
30408 		s+="\n";
30409 	}
30410 
30411 	return s;
30412 }
30413 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)30414 double ConstraintTeacherMinHoursDailyRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
30415 {
30416 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
30417 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
30418 		c.teachersMatrixReady=true;
30419 		c.subgroupsMatrixReady=true;
30420 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
30421 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
30422 
30423 		c.changedForMatrixCalculation=false;
30424 	}
30425 
30426 	assert(this->allowEmptyDays==true);
30427 
30428 	int nbroken;
30429 
30430 	//without logging
30431 	if(conflictsString==nullptr){
30432 		nbroken=0;
30433 		int i=this->teacher_ID;
30434 		for(int d=0; d<r.nDaysPerWeek/2; d++){
30435 			int n_hours_daily=0;
30436 			for(int h=0; h<r.nHoursPerDay; h++)
30437 				if(teachersMatrix[i][2*d][h]>0)
30438 					n_hours_daily++;
30439 			for(int h=0; h<r.nHoursPerDay; h++)
30440 				if(teachersMatrix[i][2*d+1][h]>0)
30441 					n_hours_daily++;
30442 
30443 			if(n_hours_daily>0 && n_hours_daily<this->minHoursDaily){
30444 				nbroken++;
30445 			}
30446 		}
30447 	}
30448 	//with logging
30449 	else{
30450 		nbroken=0;
30451 		int i=this->teacher_ID;
30452 		for(int d=0; d<r.nDaysPerWeek/2; d++){
30453 			int n_hours_daily=0;
30454 			for(int h=0; h<r.nHoursPerDay; h++)
30455 				if(teachersMatrix[i][2*d][h]>0)
30456 					n_hours_daily++;
30457 			for(int h=0; h<r.nHoursPerDay; h++)
30458 				if(teachersMatrix[i][2*d+1][h]>0)
30459 					n_hours_daily++;
30460 
30461 			if(n_hours_daily>0 && n_hours_daily<this->minHoursDaily){
30462 				nbroken++;
30463 
30464 				if(conflictsString!=nullptr){
30465 					QString s=(tr(
30466 					 "Time constraint teacher min %1 hours daily for real days broken for teacher %2, on real day %3, length=%4.")
30467 					 .arg(CustomFETString::number(this->minHoursDaily))
30468 					 .arg(r.internalTeachersList[i]->name)
30469 					 .arg(d/*r.daysOfTheWeek[d]*/)
30470 					 .arg(n_hours_daily)
30471 					 )
30472 					 +" "
30473 					 +
30474 					 tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100));
30475 
30476 					dl.append(s);
30477 					cl.append(weightPercentage/100);
30478 
30479 					*conflictsString+= s+"\n";
30480 				}
30481 			}
30482 		}
30483 	}
30484 
30485 	if(c.nPlacedActivities==r.nInternalActivities)
30486 		if(weightPercentage==100)
30487 			assert(nbroken==0);
30488 
30489 	return weightPercentage/100 * nbroken;
30490 }
30491 
isRelatedToActivity(Rules & r,Activity * a)30492 bool ConstraintTeacherMinHoursDailyRealDays::isRelatedToActivity(Rules& r, Activity* a)
30493 {
30494 	Q_UNUSED(r);
30495 	Q_UNUSED(a);
30496 
30497 	return false;
30498 }
30499 
isRelatedToTeacher(Teacher * t)30500 bool ConstraintTeacherMinHoursDailyRealDays::isRelatedToTeacher(Teacher* t)
30501 {
30502 	if(this->teacherName==t->name)
30503 		return true;
30504 	return false;
30505 }
30506 
isRelatedToSubject(Subject * s)30507 bool ConstraintTeacherMinHoursDailyRealDays::isRelatedToSubject(Subject* s)
30508 {
30509 	Q_UNUSED(s);
30510 
30511 	return false;
30512 }
30513 
isRelatedToActivityTag(ActivityTag * s)30514 bool ConstraintTeacherMinHoursDailyRealDays::isRelatedToActivityTag(ActivityTag* s)
30515 {
30516 	Q_UNUSED(s);
30517 
30518 	return false;
30519 }
30520 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)30521 bool ConstraintTeacherMinHoursDailyRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
30522 {
30523 	Q_UNUSED(r);
30524 	Q_UNUSED(s);
30525 
30526 	return false;
30527 }
30528 
hasWrongDayOrHour(Rules & r)30529 bool ConstraintTeacherMinHoursDailyRealDays::hasWrongDayOrHour(Rules& r)
30530 {
30531 	if(minHoursDaily>2*r.nHoursPerDay)
30532 		return true;
30533 
30534 	return false;
30535 }
30536 
canRepairWrongDayOrHour(Rules & r)30537 bool ConstraintTeacherMinHoursDailyRealDays::canRepairWrongDayOrHour(Rules& r)
30538 {
30539 	assert(hasWrongDayOrHour(r));
30540 
30541 	return true;
30542 }
30543 
repairWrongDayOrHour(Rules & r)30544 bool ConstraintTeacherMinHoursDailyRealDays::repairWrongDayOrHour(Rules& r)
30545 {
30546 	assert(hasWrongDayOrHour(r));
30547 
30548 	if(minHoursDaily>2*r.nHoursPerDay)
30549 		minHoursDaily=2*r.nHoursPerDay;
30550 
30551 	return true;
30552 }
30553 
30554 ///////////////////////////////////////////////////////////////////////////////////////////
30555 ///////////////////////////////////////////////////////////////////////////////////////////
30556 
ConstraintTeacherMinRealDaysPerWeek()30557 ConstraintTeacherMinRealDaysPerWeek::ConstraintTeacherMinRealDaysPerWeek()
30558 	: TimeConstraint()
30559 {
30560 	this->type=CONSTRAINT_TEACHER_MIN_REAL_DAYS_PER_WEEK;
30561 }
30562 
ConstraintTeacherMinRealDaysPerWeek(double wp,int mindays,const QString & teacher)30563 ConstraintTeacherMinRealDaysPerWeek::ConstraintTeacherMinRealDaysPerWeek(double wp, int mindays, const QString& teacher)
30564  : TimeConstraint(wp)
30565  {
30566 	assert(mindays>0);
30567 	this->minDaysPerWeek=mindays;
30568 	this->teacherName=teacher;
30569 
30570 	this->type=CONSTRAINT_TEACHER_MIN_REAL_DAYS_PER_WEEK;
30571 }
30572 
computeInternalStructure(QWidget * parent,Rules & r)30573 bool ConstraintTeacherMinRealDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
30574 {
30575 	Q_UNUSED(parent);
30576 
30577 	//this->teacher_ID=r.searchTeacher(this->teacherName);
30578 	teacher_ID=r.teachersHash.value(teacherName, -1);
30579 	assert(this->teacher_ID>=0);
30580 	return true;
30581 }
30582 
hasInactiveActivities(Rules & r)30583 bool ConstraintTeacherMinRealDaysPerWeek::hasInactiveActivities(Rules& r)
30584 {
30585 	Q_UNUSED(r);
30586 	return false;
30587 }
30588 
getXmlDescription(Rules & r)30589 QString ConstraintTeacherMinRealDaysPerWeek::getXmlDescription(Rules& r){
30590 	Q_UNUSED(r);
30591 
30592 	QString s="<ConstraintTeacherMinRealDaysPerWeek>\n";
30593 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
30594 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
30595 	s+="	<Minimum_Days_Per_Week>"+CustomFETString::number(this->minDaysPerWeek)+"</Minimum_Days_Per_Week>\n";
30596 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
30597 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
30598 	s+="</ConstraintTeacherMinRealDaysPerWeek>\n";
30599 	return s;
30600 }
30601 
getDescription(Rules & r)30602 QString ConstraintTeacherMinRealDaysPerWeek::getDescription(Rules& r){
30603 	Q_UNUSED(r);
30604 
30605 	QString begin=QString("");
30606 	if(!active)
30607 		begin="X - ";
30608 
30609 	QString end=QString("");
30610 	if(!comments.isEmpty())
30611 		end=", "+tr("C: %1", "Comments").arg(comments);
30612 
30613 	QString s;
30614 	s+=tr("Teacher min real days per week");s+=", ";
30615 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
30616 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
30617 	s+=tr("mD:%1", "Minimum days per week").arg(this->minDaysPerWeek);
30618 
30619 	return begin+s+end;
30620 }
30621 
getDetailedDescription(Rules & r)30622 QString ConstraintTeacherMinRealDaysPerWeek::getDetailedDescription(Rules& r){
30623 	Q_UNUSED(r);
30624 
30625 	QString s=tr("Time constraint");s+="\n";
30626 	s+=tr("A teacher must respect the minimum number of real days per week");s+="\n";
30627 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
30628 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
30629 	s+=tr("Minimum days per week=%1").arg(this->minDaysPerWeek);s+="\n";
30630 
30631 	if(!active){
30632 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
30633 		s+="\n";
30634 	}
30635 	if(!comments.isEmpty()){
30636 		s+=tr("Comments=%1").arg(comments);
30637 		s+="\n";
30638 	}
30639 
30640 	return s;
30641 }
30642 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)30643 double ConstraintTeacherMinRealDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
30644 {
30645 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
30646 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
30647 		c.teachersMatrixReady=true;
30648 		c.subgroupsMatrixReady=true;
30649 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
30650 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
30651 
30652 		c.changedForMatrixCalculation=false;
30653 	}
30654 
30655 	int nbroken;
30656 
30657 	nbroken=0;
30658 	int i=this->teacher_ID;
30659 	int nd=0;
30660 	for(int d=0; d<r.nDaysPerWeek/2; d++){
30661 		for(int h=0; h<r.nHoursPerDay; h++){
30662 			if(teachersMatrix[i][2*d][h]>0 || teachersMatrix[i][2*d+1][h]>0){
30663 				nd++;
30664 				break;
30665 			}
30666 		}
30667 	}
30668 
30669 	if(nd<this->minDaysPerWeek){
30670 		nbroken+=this->minDaysPerWeek-nd;
30671 
30672 		if(conflictsString!=nullptr){
30673 			QString s=(tr(
30674 			 "Time constraint teacher min %1 real days per week broken for teacher %2.")
30675 			 .arg(CustomFETString::number(this->minDaysPerWeek))
30676 			 .arg(r.internalTeachersList[i]->name)
30677 			 )
30678 			 +" "
30679 			 +
30680 			 tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(double(nbroken)*weightPercentage/100));
30681 
30682 			dl.append(s);
30683 			cl.append(double(nbroken)*weightPercentage/100);
30684 
30685 			*conflictsString+= s+"\n";
30686 		}
30687 	}
30688 
30689 	if(c.nPlacedActivities==r.nInternalActivities)
30690 		if(weightPercentage==100)
30691 			assert(nbroken==0);
30692 
30693 	return weightPercentage/100 * nbroken;
30694 }
30695 
isRelatedToActivity(Rules & r,Activity * a)30696 bool ConstraintTeacherMinRealDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
30697 {
30698 	Q_UNUSED(r);
30699 	Q_UNUSED(a);
30700 
30701 	return false;
30702 }
30703 
isRelatedToTeacher(Teacher * t)30704 bool ConstraintTeacherMinRealDaysPerWeek::isRelatedToTeacher(Teacher* t)
30705 {
30706 	if(this->teacherName==t->name)
30707 		return true;
30708 	return false;
30709 }
30710 
isRelatedToSubject(Subject * s)30711 bool ConstraintTeacherMinRealDaysPerWeek::isRelatedToSubject(Subject* s)
30712 {
30713 	Q_UNUSED(s);
30714 
30715 	return false;
30716 }
30717 
isRelatedToActivityTag(ActivityTag * s)30718 bool ConstraintTeacherMinRealDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
30719 {
30720 	Q_UNUSED(s);
30721 
30722 	return false;
30723 }
30724 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)30725 bool ConstraintTeacherMinRealDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
30726 {
30727 	Q_UNUSED(r);
30728 	Q_UNUSED(s);
30729 
30730 	return false;
30731 }
30732 
hasWrongDayOrHour(Rules & r)30733 bool ConstraintTeacherMinRealDaysPerWeek::hasWrongDayOrHour(Rules& r)
30734 {
30735 	if(minDaysPerWeek>r.nDaysPerWeek)
30736 		return true;
30737 
30738 	return false;
30739 }
30740 
canRepairWrongDayOrHour(Rules & r)30741 bool ConstraintTeacherMinRealDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
30742 {
30743 	assert(hasWrongDayOrHour(r));
30744 
30745 	return true;
30746 }
30747 
repairWrongDayOrHour(Rules & r)30748 bool ConstraintTeacherMinRealDaysPerWeek::repairWrongDayOrHour(Rules& r)
30749 {
30750 	assert(hasWrongDayOrHour(r));
30751 
30752 	if(minDaysPerWeek>r.nDaysPerWeek)
30753 		minDaysPerWeek=r.nDaysPerWeek;
30754 
30755 	return true;
30756 }
30757 
30758 ///////////////////////////////////////////////////////////////////////////////////////////
30759 ///////////////////////////////////////////////////////////////////////////////////////////
30760 
ConstraintTeachersMinRealDaysPerWeek()30761 ConstraintTeachersMinRealDaysPerWeek::ConstraintTeachersMinRealDaysPerWeek()
30762 	: TimeConstraint()
30763 {
30764 	this->type=CONSTRAINT_TEACHERS_MIN_REAL_DAYS_PER_WEEK;
30765 }
30766 
ConstraintTeachersMinRealDaysPerWeek(double wp,int mindays)30767 ConstraintTeachersMinRealDaysPerWeek::ConstraintTeachersMinRealDaysPerWeek(double wp, int mindays)
30768  : TimeConstraint(wp)
30769  {
30770 	assert(mindays>0);
30771 	this->minDaysPerWeek=mindays;
30772 
30773 	this->type=CONSTRAINT_TEACHERS_MIN_REAL_DAYS_PER_WEEK;
30774 }
30775 
computeInternalStructure(QWidget * parent,Rules & r)30776 bool ConstraintTeachersMinRealDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
30777 {
30778 	Q_UNUSED(parent);
30779 	Q_UNUSED(r);
30780 
30781 	return true;
30782 }
30783 
hasInactiveActivities(Rules & r)30784 bool ConstraintTeachersMinRealDaysPerWeek::hasInactiveActivities(Rules& r)
30785 {
30786 	Q_UNUSED(r);
30787 	return false;
30788 }
30789 
getXmlDescription(Rules & r)30790 QString ConstraintTeachersMinRealDaysPerWeek::getXmlDescription(Rules& r){
30791 	Q_UNUSED(r);
30792 
30793 	QString s="<ConstraintTeachersMinRealDaysPerWeek>\n";
30794 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
30795 	s+="	<Minimum_Days_Per_Week>"+CustomFETString::number(this->minDaysPerWeek)+"</Minimum_Days_Per_Week>\n";
30796 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
30797 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
30798 	s+="</ConstraintTeachersMinRealDaysPerWeek>\n";
30799 	return s;
30800 }
30801 
getDescription(Rules & r)30802 QString ConstraintTeachersMinRealDaysPerWeek::getDescription(Rules& r){
30803 	Q_UNUSED(r);
30804 
30805 	QString begin=QString("");
30806 	if(!active)
30807 		begin="X - ";
30808 
30809 	QString end=QString("");
30810 	if(!comments.isEmpty())
30811 		end=", "+tr("C: %1", "Comments").arg(comments);
30812 
30813 	QString s;
30814 	s+=tr("Teachers min real days per week");s+=", ";
30815 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
30816 	s+=tr("mD:%1", "Minimum days per week").arg(this->minDaysPerWeek);
30817 
30818 	return begin+s+end;
30819 }
30820 
getDetailedDescription(Rules & r)30821 QString ConstraintTeachersMinRealDaysPerWeek::getDetailedDescription(Rules& r){
30822 	Q_UNUSED(r);
30823 
30824 	QString s=tr("Time constraint");s+="\n";
30825 	s+=tr("All teachers must respect the minimum number of real days per week");s+="\n";
30826 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
30827 	s+=tr("Minimum days per week=%1").arg(this->minDaysPerWeek);s+="\n";
30828 
30829 	if(!active){
30830 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
30831 		s+="\n";
30832 	}
30833 	if(!comments.isEmpty()){
30834 		s+=tr("Comments=%1").arg(comments);
30835 		s+="\n";
30836 	}
30837 
30838 	return s;
30839 }
30840 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)30841 double ConstraintTeachersMinRealDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
30842 {
30843 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
30844 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
30845 		c.teachersMatrixReady=true;
30846 		c.subgroupsMatrixReady=true;
30847 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
30848 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
30849 
30850 		c.changedForMatrixCalculation=false;
30851 	}
30852 
30853 	int nbrokentotal=0;
30854 	for(int i=0; i<r.nInternalTeachers; i++){
30855 		int nbroken;
30856 
30857 		nbroken=0;
30858 		//int i=this->teacher_ID;
30859 		int nd=0;
30860 		for(int d=0; d<r.nDaysPerWeek/2; d++){
30861 			for(int h=0; h<r.nHoursPerDay; h++){
30862 				if(teachersMatrix[i][2*d][h]>0 || teachersMatrix[i][2*d+1][h]>0){
30863 					nd++;
30864 					break;
30865 				}
30866 			}
30867 		}
30868 
30869 		if(nd<this->minDaysPerWeek){
30870 			nbroken+=this->minDaysPerWeek-nd;
30871 			nbrokentotal+=nbroken;
30872 
30873 			if(conflictsString!=nullptr){
30874 				QString s=(tr(
30875 				 "Time constraint teachers min %1 real days per week broken for teacher %2.")
30876 				 .arg(CustomFETString::number(this->minDaysPerWeek))
30877 				 .arg(r.internalTeachersList[i]->name)
30878 				 )
30879 				 +" "
30880 				 +
30881 				 tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(double(nbroken)*weightPercentage/100));
30882 
30883 				dl.append(s);
30884 				cl.append(double(nbroken)*weightPercentage/100);
30885 
30886 				*conflictsString+= s+"\n";
30887 			}
30888 		}
30889 	}
30890 
30891 	if(c.nPlacedActivities==r.nInternalActivities)
30892 		if(weightPercentage==100)
30893 			assert(nbrokentotal==0);
30894 
30895 	return weightPercentage/100 * nbrokentotal;
30896 }
30897 
isRelatedToActivity(Rules & r,Activity * a)30898 bool ConstraintTeachersMinRealDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
30899 {
30900 	Q_UNUSED(r);
30901 	Q_UNUSED(a);
30902 
30903 	return false;
30904 }
30905 
isRelatedToTeacher(Teacher * t)30906 bool ConstraintTeachersMinRealDaysPerWeek::isRelatedToTeacher(Teacher* t)
30907 {
30908 	Q_UNUSED(t);
30909 	return true;
30910 }
30911 
isRelatedToSubject(Subject * s)30912 bool ConstraintTeachersMinRealDaysPerWeek::isRelatedToSubject(Subject* s)
30913 {
30914 	Q_UNUSED(s);
30915 
30916 	return false;
30917 }
30918 
isRelatedToActivityTag(ActivityTag * s)30919 bool ConstraintTeachersMinRealDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
30920 {
30921 	Q_UNUSED(s);
30922 
30923 	return false;
30924 }
30925 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)30926 bool ConstraintTeachersMinRealDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
30927 {
30928 	Q_UNUSED(r);
30929 	Q_UNUSED(s);
30930 
30931 	return false;
30932 }
30933 
hasWrongDayOrHour(Rules & r)30934 bool ConstraintTeachersMinRealDaysPerWeek::hasWrongDayOrHour(Rules& r)
30935 {
30936 	if(minDaysPerWeek>r.nDaysPerWeek)
30937 		return true;
30938 
30939 	return false;
30940 }
30941 
canRepairWrongDayOrHour(Rules & r)30942 bool ConstraintTeachersMinRealDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
30943 {
30944 	assert(hasWrongDayOrHour(r));
30945 
30946 	return true;
30947 }
30948 
repairWrongDayOrHour(Rules & r)30949 bool ConstraintTeachersMinRealDaysPerWeek::repairWrongDayOrHour(Rules& r)
30950 {
30951 	assert(hasWrongDayOrHour(r));
30952 
30953 	if(minDaysPerWeek>r.nDaysPerWeek)
30954 		minDaysPerWeek=r.nDaysPerWeek;
30955 
30956 	return true;
30957 }
30958 
30959 ///////////////////////////////////////////////////////////////////////////////////////////
30960 ///////////////////////////////////////////////////////////////////////////////////////////
30961 
30962 //morning
30963 
ConstraintTeacherMorningIntervalMaxDaysPerWeek()30964 ConstraintTeacherMorningIntervalMaxDaysPerWeek::ConstraintTeacherMorningIntervalMaxDaysPerWeek()
30965 	: TimeConstraint()
30966 {
30967 	this->type=CONSTRAINT_TEACHER_MORNING_INTERVAL_MAX_DAYS_PER_WEEK;
30968 }
30969 
ConstraintTeacherMorningIntervalMaxDaysPerWeek(double wp,int maxnd,QString tn,int sh,int eh)30970 ConstraintTeacherMorningIntervalMaxDaysPerWeek::ConstraintTeacherMorningIntervalMaxDaysPerWeek(double wp, int maxnd, QString tn, int sh, int eh)
30971 	 : TimeConstraint(wp)
30972 {
30973 	this->teacherName = tn;
30974 	this->maxDaysPerWeek=maxnd;
30975 	this->type=CONSTRAINT_TEACHER_MORNING_INTERVAL_MAX_DAYS_PER_WEEK;
30976 	this->startHour=sh;
30977 	this->endHour=eh;
30978 	assert(sh<eh);
30979 	assert(sh>=0);
30980 }
30981 
computeInternalStructure(QWidget * parent,Rules & r)30982 bool ConstraintTeacherMorningIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
30983 {
30984 	//this->teacher_ID=r.searchTeacher(this->teacherName);
30985 	teacher_ID=r.teachersHash.value(teacherName, -1);
30986 	assert(this->teacher_ID>=0);
30987 	if(this->startHour>=this->endHour){
30988 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
30989 		 tr("Constraint teacher morning interval max days per week is wrong because start hour >= end hour."
30990 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
30991 
30992 		return false;
30993 	}
30994 	if(this->startHour<0){
30995 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
30996 		 tr("Constraint teacher morning interval max days per week is wrong because start hour < first hour of the day."
30997 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
30998 
30999 		return false;
31000 	}
31001 	if(this->endHour>r.nHoursPerDay){
31002 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
31003 		 tr("Constraint teacher morning interval max days per week is wrong because end hour > number of hours per day."
31004 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
31005 
31006 		return false;
31007 	}
31008 	return true;
31009 }
31010 
hasInactiveActivities(Rules & r)31011 bool ConstraintTeacherMorningIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
31012 {
31013 	Q_UNUSED(r);
31014 	return false;
31015 }
31016 
getXmlDescription(Rules & r)31017 QString ConstraintTeacherMorningIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
31018 {
31019 	Q_UNUSED(r);
31020 
31021 	QString s="<ConstraintTeacherMorningIntervalMaxDaysPerWeek>\n";
31022 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
31023 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
31024 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
31025 	if(this->endHour < r.nHoursPerDay){
31026 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
31027 	}
31028 	else{
31029 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
31030 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
31031 	}
31032 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
31033 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
31034 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
31035 	s+="</ConstraintTeacherMorningIntervalMaxDaysPerWeek>\n";
31036 	return s;
31037 }
31038 
getDescription(Rules & r)31039 QString ConstraintTeacherMorningIntervalMaxDaysPerWeek::getDescription(Rules& r){
31040 	Q_UNUSED(r);
31041 
31042 	QString begin=QString("");
31043 	if(!active)
31044 		begin="X - ";
31045 
31046 	QString end=QString("");
31047 	if(!comments.isEmpty())
31048 		end=", "+tr("C: %1", "Comments").arg(comments);
31049 
31050 	QString s=tr("Teacher morning interval max days per week");s+=", ";
31051 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
31052 	s+=tr("T:%1", "Abbreviation for teacher").arg(this->teacherName);s+=", ";
31053 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);s+=", ";
31054 	if(this->endHour<r.nHoursPerDay)
31055 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
31056 	else
31057 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
31058 	s+=", ";
31059 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
31060 
31061 	return begin+s+end;
31062 }
31063 
getDetailedDescription(Rules & r)31064 QString ConstraintTeacherMorningIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r){
31065 	Q_UNUSED(r);
31066 
31067 	QString s=tr("Time constraint");s+="\n";
31068 	s+=tr("A teacher respects working in an hourly morning interval a maximum number of days per week");s+="\n";
31069 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
31070 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
31071 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
31072 
31073 	if(this->endHour<r.nHoursPerDay)
31074 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
31075 	else
31076 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
31077 	s+="\n";
31078 
31079 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
31080 
31081 	if(!active){
31082 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
31083 		s+="\n";
31084 	}
31085 	if(!comments.isEmpty()){
31086 		s+=tr("Comments=%1").arg(comments);
31087 		s+="\n";
31088 	}
31089 
31090 	return s;
31091 }
31092 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)31093 double ConstraintTeacherMorningIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
31094 {
31095 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
31096 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
31097 		c.teachersMatrixReady=true;
31098 		c.subgroupsMatrixReady=true;
31099 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
31100 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
31101 
31102 		c.changedForMatrixCalculation=false;
31103 	}
31104 
31105 	int nbroken;
31106 
31107 	int t=this->teacher_ID;
31108 
31109 	nbroken=0;
31110 	Matrix1D<bool> ocDay;
31111 	ocDay.resize(r.nDaysPerWeek);
31112 	for(int d=0; d<r.nDaysPerWeek; d+=2){ //mornings
31113 		ocDay[d]=false;
31114 		for(int h=startHour; h<endHour; h++){
31115 			if(teachersMatrix[t][d][h]>0){
31116 				ocDay[d]=true;
31117 			}
31118 		}
31119 	}
31120 	int nOcDays=0;
31121 	for(int d=0; d<r.nDaysPerWeek; d+=2) //mornings
31122 		if(ocDay[d])
31123 			nOcDays++;
31124 	if(nOcDays > this->maxDaysPerWeek){
31125 		nbroken+=nOcDays-this->maxDaysPerWeek;
31126 
31127 		if(nbroken>0){
31128 			QString s= tr("Time constraint teacher morning interval max days per week broken for teacher: %1, allowed %2 days, required %3 days.")
31129 			 .arg(r.internalTeachersList[t]->name)
31130 			 .arg(this->maxDaysPerWeek)
31131 			 .arg(nOcDays);
31132 			s+=" ";
31133 			s += tr("This increases the conflicts total by %1")
31134 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
31135 
31136 			dl.append(s);
31137 			cl.append(nbroken*weightPercentage/100);
31138 
31139 			*conflictsString += s+"\n";
31140 		}
31141 	}
31142 
31143 	if(weightPercentage==100)
31144 		assert(nbroken==0);
31145 	return weightPercentage/100 * nbroken;
31146 }
31147 
isRelatedToActivity(Rules & r,Activity * a)31148 bool ConstraintTeacherMorningIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
31149 {
31150 	Q_UNUSED(r);
31151 	Q_UNUSED(a);
31152 
31153 	return false;
31154 }
31155 
isRelatedToTeacher(Teacher * t)31156 bool ConstraintTeacherMorningIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
31157 {
31158 	if(this->teacherName==t->name)
31159 		return true;
31160 	return false;
31161 }
31162 
isRelatedToSubject(Subject * s)31163 bool ConstraintTeacherMorningIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
31164 {
31165 	Q_UNUSED(s);
31166 
31167 	return false;
31168 }
31169 
isRelatedToActivityTag(ActivityTag * s)31170 bool ConstraintTeacherMorningIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
31171 {
31172 	Q_UNUSED(s);
31173 
31174 	return false;
31175 }
31176 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)31177 bool ConstraintTeacherMorningIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
31178 {
31179 	Q_UNUSED(r);
31180 	Q_UNUSED(s);
31181 
31182 	return false;
31183 }
31184 
hasWrongDayOrHour(Rules & r)31185 bool ConstraintTeacherMorningIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
31186 {
31187 	if(this->startHour>=r.nHoursPerDay)
31188 		return true;
31189 	if(this->endHour>r.nHoursPerDay)
31190 		return true;
31191 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
31192 		return true;
31193 
31194 	return false;
31195 }
31196 
canRepairWrongDayOrHour(Rules & r)31197 bool ConstraintTeacherMorningIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
31198 {
31199 	assert(hasWrongDayOrHour(r));
31200 
31201 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
31202 		return true;
31203 
31204 	return false;
31205 }
31206 
repairWrongDayOrHour(Rules & r)31207 bool ConstraintTeacherMorningIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
31208 {
31209 	assert(hasWrongDayOrHour(r));
31210 
31211 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
31212 
31213 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
31214 		this->maxDaysPerWeek=r.nDaysPerWeek/2;
31215 
31216 	return true;
31217 }
31218 
31219 ///////////////////////////////////////////////////////////////////////////////////////////
31220 ///////////////////////////////////////////////////////////////////////////////////////////
31221 
31222 //morning
31223 
ConstraintTeachersMorningIntervalMaxDaysPerWeek()31224 ConstraintTeachersMorningIntervalMaxDaysPerWeek::ConstraintTeachersMorningIntervalMaxDaysPerWeek()
31225 	: TimeConstraint()
31226 {
31227 	this->type=CONSTRAINT_TEACHERS_MORNING_INTERVAL_MAX_DAYS_PER_WEEK;
31228 }
31229 
ConstraintTeachersMorningIntervalMaxDaysPerWeek(double wp,int maxnd,int sh,int eh)31230 ConstraintTeachersMorningIntervalMaxDaysPerWeek::ConstraintTeachersMorningIntervalMaxDaysPerWeek(double wp, int maxnd, int sh, int eh)
31231 	 : TimeConstraint(wp)
31232 {
31233 	this->maxDaysPerWeek=maxnd;
31234 	this->type=CONSTRAINT_TEACHERS_MORNING_INTERVAL_MAX_DAYS_PER_WEEK;
31235 	this->startHour=sh;
31236 	this->endHour=eh;
31237 	assert(sh<eh);
31238 	assert(sh>=0);
31239 }
31240 
computeInternalStructure(QWidget * parent,Rules & r)31241 bool ConstraintTeachersMorningIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
31242 {
31243 	if(this->startHour>=this->endHour){
31244 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
31245 		 tr("Constraint teachers morning interval max days per week is wrong because start hour >= end hour."
31246 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
31247 
31248 		return false;
31249 	}
31250 	if(this->startHour<0){
31251 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
31252 		 tr("Constraint teachers morning interval max days per week is wrong because start hour < first hour of the day."
31253 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
31254 
31255 		return false;
31256 	}
31257 	if(this->endHour>r.nHoursPerDay){
31258 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
31259 		 tr("Constraint teachers morning interval max days per week is wrong because end hour > number of hours per day."
31260 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
31261 
31262 		return false;
31263 	}
31264 	return true;
31265 }
31266 
hasInactiveActivities(Rules & r)31267 bool ConstraintTeachersMorningIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
31268 {
31269 	Q_UNUSED(r);
31270 	return false;
31271 }
31272 
getXmlDescription(Rules & r)31273 QString ConstraintTeachersMorningIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
31274 {
31275 	Q_UNUSED(r);
31276 
31277 	QString s="<ConstraintTeachersMorningIntervalMaxDaysPerWeek>\n";
31278 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
31279 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
31280 	if(this->endHour < r.nHoursPerDay){
31281 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
31282 	}
31283 	else{
31284 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
31285 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
31286 	}
31287 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
31288 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
31289 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
31290 	s+="</ConstraintTeachersMorningIntervalMaxDaysPerWeek>\n";
31291 	return s;
31292 }
31293 
getDescription(Rules & r)31294 QString ConstraintTeachersMorningIntervalMaxDaysPerWeek::getDescription(Rules& r){
31295 	Q_UNUSED(r);
31296 
31297 	QString begin=QString("");
31298 	if(!active)
31299 		begin="X - ";
31300 
31301 	QString end=QString("");
31302 	if(!comments.isEmpty())
31303 		end=", "+tr("C: %1", "Comments").arg(comments);
31304 
31305 	QString s=tr("Teachers morning interval max days per week");s+=", ";
31306 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
31307 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);
31308 	s+=", ";
31309 	if(this->endHour<r.nHoursPerDay)
31310 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
31311 	else
31312 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
31313 	s+=", ";
31314 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
31315 
31316 	return begin+s+end;
31317 }
31318 
getDetailedDescription(Rules & r)31319 QString ConstraintTeachersMorningIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r){
31320 	Q_UNUSED(r);
31321 
31322 	QString s=tr("Time constraint");s+="\n";
31323 	s+=tr("All teachers respect working in an hourly morning interval a maximum number of days per week");s+="\n";
31324 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
31325 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
31326 
31327 	if(this->endHour<r.nHoursPerDay)
31328 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
31329 	else
31330 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
31331 	s+="\n";
31332 
31333 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
31334 
31335 	if(!active){
31336 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
31337 		s+="\n";
31338 	}
31339 	if(!comments.isEmpty()){
31340 		s+=tr("Comments=%1").arg(comments);
31341 		s+="\n";
31342 	}
31343 
31344 	return s;
31345 }
31346 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)31347 double ConstraintTeachersMorningIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
31348 {
31349 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
31350 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
31351 		c.teachersMatrixReady=true;
31352 		c.subgroupsMatrixReady=true;
31353 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
31354 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
31355 
31356 		c.changedForMatrixCalculation=false;
31357 	}
31358 
31359 	int nbroken=0;
31360 
31361 	Matrix1D<bool> ocDay;
31362 	ocDay.resize(r.nDaysPerWeek);
31363 	for(int t=0; t<r.nInternalTeachers; t++){
31364 		for(int d=0; d<r.nDaysPerWeek; d+=2){ //mornings
31365 			ocDay[d]=false;
31366 			for(int h=startHour; h<endHour; h++){
31367 				if(teachersMatrix[t][d][h]>0){
31368 					ocDay[d]=true;
31369 				}
31370 			}
31371 		}
31372 		int nOcDays=0;
31373 		for(int d=0; d<r.nDaysPerWeek; d+=2) //mornings
31374 			if(ocDay[d])
31375 				nOcDays++;
31376 		if(nOcDays > this->maxDaysPerWeek){
31377 			nbroken+=nOcDays-this->maxDaysPerWeek;
31378 
31379 			if(nOcDays-this->maxDaysPerWeek>0){
31380 				QString s= tr("Time constraint teachers morning interval max days per week broken for teacher: %1, allowed %2 days, required %3 days.")
31381 				 .arg(r.internalTeachersList[t]->name)
31382 				 .arg(this->maxDaysPerWeek)
31383 				 .arg(nOcDays);
31384 				s+=" ";
31385 				s += tr("This increases the conflicts total by %1")
31386 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
31387 
31388 				dl.append(s);
31389 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
31390 
31391 				*conflictsString += s+"\n";
31392 			}
31393 		}
31394 	}
31395 
31396 	if(weightPercentage==100)
31397 		assert(nbroken==0);
31398 	return weightPercentage/100 * nbroken;
31399 }
31400 
isRelatedToActivity(Rules & r,Activity * a)31401 bool ConstraintTeachersMorningIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
31402 {
31403 	Q_UNUSED(r);
31404 	Q_UNUSED(a);
31405 
31406 	return false;
31407 }
31408 
isRelatedToTeacher(Teacher * t)31409 bool ConstraintTeachersMorningIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
31410 {
31411 	Q_UNUSED(t);
31412 
31413 	return true;
31414 }
31415 
isRelatedToSubject(Subject * s)31416 bool ConstraintTeachersMorningIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
31417 {
31418 	Q_UNUSED(s);
31419 
31420 	return false;
31421 }
31422 
isRelatedToActivityTag(ActivityTag * s)31423 bool ConstraintTeachersMorningIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
31424 {
31425 	Q_UNUSED(s);
31426 
31427 	return false;
31428 }
31429 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)31430 bool ConstraintTeachersMorningIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
31431 {
31432 	Q_UNUSED(r);
31433 	Q_UNUSED(s);
31434 
31435 	return false;
31436 }
31437 
hasWrongDayOrHour(Rules & r)31438 bool ConstraintTeachersMorningIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
31439 {
31440 	if(this->startHour>=r.nHoursPerDay)
31441 		return true;
31442 	if(this->endHour>r.nHoursPerDay)
31443 		return true;
31444 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
31445 		return true;
31446 
31447 	return false;
31448 }
31449 
canRepairWrongDayOrHour(Rules & r)31450 bool ConstraintTeachersMorningIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
31451 {
31452 	assert(hasWrongDayOrHour(r));
31453 
31454 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
31455 		return true;
31456 
31457 	return false;
31458 }
31459 
repairWrongDayOrHour(Rules & r)31460 bool ConstraintTeachersMorningIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
31461 {
31462 	assert(hasWrongDayOrHour(r));
31463 
31464 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
31465 
31466 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
31467 		this->maxDaysPerWeek=r.nDaysPerWeek/2;
31468 
31469 	return true;
31470 }
31471 
31472 ///////////////////////////////////////////////////////////////////////////////////////////
31473 ///////////////////////////////////////////////////////////////////////////////////////////
31474 
31475 //afternoon
31476 
ConstraintTeacherAfternoonIntervalMaxDaysPerWeek()31477 ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::ConstraintTeacherAfternoonIntervalMaxDaysPerWeek()
31478 	: TimeConstraint()
31479 {
31480 	this->type=CONSTRAINT_TEACHER_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK;
31481 }
31482 
ConstraintTeacherAfternoonIntervalMaxDaysPerWeek(double wp,int maxnd,QString tn,int sh,int eh)31483 ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::ConstraintTeacherAfternoonIntervalMaxDaysPerWeek(double wp, int maxnd, QString tn, int sh, int eh)
31484 	 : TimeConstraint(wp)
31485 {
31486 	this->teacherName = tn;
31487 	this->maxDaysPerWeek=maxnd;
31488 	this->type=CONSTRAINT_TEACHER_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK;
31489 	this->startHour=sh;
31490 	this->endHour=eh;
31491 	assert(sh<eh);
31492 	assert(sh>=0);
31493 }
31494 
computeInternalStructure(QWidget * parent,Rules & r)31495 bool ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
31496 {
31497 	//this->teacher_ID=r.searchTeacher(this->teacherName);
31498 	teacher_ID=r.teachersHash.value(teacherName, -1);
31499 	assert(this->teacher_ID>=0);
31500 	if(this->startHour>=this->endHour){
31501 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
31502 		 tr("Constraint teacher afternoon interval max days per week is wrong because start hour >= end hour."
31503 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
31504 
31505 		return false;
31506 	}
31507 	if(this->startHour<0){
31508 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
31509 		 tr("Constraint teacher afternoon interval max days per week is wrong because start hour < first hour of the day."
31510 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
31511 
31512 		return false;
31513 	}
31514 	if(this->endHour>r.nHoursPerDay){
31515 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
31516 		 tr("Constraint teacher afternoon interval max days per week is wrong because end hour > number of hours per day."
31517 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
31518 
31519 		return false;
31520 	}
31521 	return true;
31522 }
31523 
hasInactiveActivities(Rules & r)31524 bool ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
31525 {
31526 	Q_UNUSED(r);
31527 	return false;
31528 }
31529 
getXmlDescription(Rules & r)31530 QString ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
31531 {
31532 	Q_UNUSED(r);
31533 
31534 	QString s="<ConstraintTeacherAfternoonIntervalMaxDaysPerWeek>\n";
31535 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
31536 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
31537 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
31538 	if(this->endHour < r.nHoursPerDay){
31539 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
31540 	}
31541 	else{
31542 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
31543 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
31544 	}
31545 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
31546 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
31547 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
31548 	s+="</ConstraintTeacherAfternoonIntervalMaxDaysPerWeek>\n";
31549 	return s;
31550 }
31551 
getDescription(Rules & r)31552 QString ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::getDescription(Rules& r){
31553 	Q_UNUSED(r);
31554 
31555 	QString begin=QString("");
31556 	if(!active)
31557 		begin="X - ";
31558 
31559 	QString end=QString("");
31560 	if(!comments.isEmpty())
31561 		end=", "+tr("C: %1", "Comments").arg(comments);
31562 
31563 	QString s=tr("Teacher afternoon interval max days per week");s+=", ";
31564 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
31565 	s+=tr("T:%1", "Abbreviation for teacher").arg(this->teacherName);s+=", ";
31566 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);s+=", ";
31567 	if(this->endHour<r.nHoursPerDay)
31568 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
31569 	else
31570 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
31571 	s+=", ";
31572 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
31573 
31574 	return begin+s+end;
31575 }
31576 
getDetailedDescription(Rules & r)31577 QString ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r){
31578 	Q_UNUSED(r);
31579 
31580 	QString s=tr("Time constraint");s+="\n";
31581 	s+=tr("A teacher respects working in an hourly afternoon interval a maximum number of days per week");s+="\n";
31582 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
31583 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
31584 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
31585 
31586 	if(this->endHour<r.nHoursPerDay)
31587 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
31588 	else
31589 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
31590 	s+="\n";
31591 
31592 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
31593 
31594 	if(!active){
31595 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
31596 		s+="\n";
31597 	}
31598 	if(!comments.isEmpty()){
31599 		s+=tr("Comments=%1").arg(comments);
31600 		s+="\n";
31601 	}
31602 
31603 	return s;
31604 }
31605 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)31606 double ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
31607 {
31608 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
31609 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
31610 		c.teachersMatrixReady=true;
31611 		c.subgroupsMatrixReady=true;
31612 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
31613 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
31614 
31615 		c.changedForMatrixCalculation=false;
31616 	}
31617 
31618 	int nbroken;
31619 
31620 	int t=this->teacher_ID;
31621 
31622 	nbroken=0;
31623 	Matrix1D<bool> ocDay;
31624 	ocDay.resize(r.nDaysPerWeek);
31625 	for(int d=1; d<r.nDaysPerWeek; d+=2){ //afternoon
31626 		ocDay[d]=false;
31627 		for(int h=startHour; h<endHour; h++){
31628 			if(teachersMatrix[t][d][h]>0){
31629 				ocDay[d]=true;
31630 			}
31631 		}
31632 	}
31633 	int nOcDays=0;
31634 	for(int d=1; d<r.nDaysPerWeek; d+=2) //afternoon
31635 		if(ocDay[d])
31636 			nOcDays++;
31637 	if(nOcDays > this->maxDaysPerWeek){
31638 		nbroken+=nOcDays-this->maxDaysPerWeek;
31639 
31640 		if(nbroken>0){
31641 			QString s= tr("Time constraint teacher afternoon interval max days per week broken for teacher: %1, allowed %2 days, required %3 days.")
31642 			 .arg(r.internalTeachersList[t]->name)
31643 			 .arg(this->maxDaysPerWeek)
31644 			 .arg(nOcDays);
31645 			s+=" ";
31646 			s += tr("This increases the conflicts total by %1")
31647 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
31648 
31649 			dl.append(s);
31650 			cl.append(nbroken*weightPercentage/100);
31651 
31652 			*conflictsString += s+"\n";
31653 		}
31654 	}
31655 
31656 	if(weightPercentage==100)
31657 		assert(nbroken==0);
31658 	return weightPercentage/100 * nbroken;
31659 }
31660 
isRelatedToActivity(Rules & r,Activity * a)31661 bool ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
31662 {
31663 	Q_UNUSED(r);
31664 	Q_UNUSED(a);
31665 
31666 	return false;
31667 }
31668 
isRelatedToTeacher(Teacher * t)31669 bool ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
31670 {
31671 	if(this->teacherName==t->name)
31672 		return true;
31673 	return false;
31674 }
31675 
isRelatedToSubject(Subject * s)31676 bool ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
31677 {
31678 	Q_UNUSED(s);
31679 
31680 	return false;
31681 }
31682 
isRelatedToActivityTag(ActivityTag * s)31683 bool ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
31684 {
31685 	Q_UNUSED(s);
31686 
31687 	return false;
31688 }
31689 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)31690 bool ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
31691 {
31692 	Q_UNUSED(r);
31693 	Q_UNUSED(s);
31694 
31695 	return false;
31696 }
31697 
hasWrongDayOrHour(Rules & r)31698 bool ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
31699 {
31700 	if(this->startHour>=r.nHoursPerDay)
31701 		return true;
31702 	if(this->endHour>r.nHoursPerDay)
31703 		return true;
31704 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
31705 		return true;
31706 
31707 	return false;
31708 }
31709 
canRepairWrongDayOrHour(Rules & r)31710 bool ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
31711 {
31712 	assert(hasWrongDayOrHour(r));
31713 
31714 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
31715 		return true;
31716 
31717 	return false;
31718 }
31719 
repairWrongDayOrHour(Rules & r)31720 bool ConstraintTeacherAfternoonIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
31721 {
31722 	assert(hasWrongDayOrHour(r));
31723 
31724 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
31725 
31726 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
31727 		this->maxDaysPerWeek=r.nDaysPerWeek/2;
31728 
31729 	return true;
31730 }
31731 
31732 ///////////////////////////////////////////////////////////////////////////////////////////
31733 ///////////////////////////////////////////////////////////////////////////////////////////
31734 
31735 //afternoon
31736 
ConstraintTeachersAfternoonIntervalMaxDaysPerWeek()31737 ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::ConstraintTeachersAfternoonIntervalMaxDaysPerWeek()
31738 	: TimeConstraint()
31739 {
31740 	this->type=CONSTRAINT_TEACHERS_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK;
31741 }
31742 
ConstraintTeachersAfternoonIntervalMaxDaysPerWeek(double wp,int maxnd,int sh,int eh)31743 ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::ConstraintTeachersAfternoonIntervalMaxDaysPerWeek(double wp, int maxnd, int sh, int eh)
31744 	 : TimeConstraint(wp)
31745 {
31746 	this->maxDaysPerWeek=maxnd;
31747 	this->type=CONSTRAINT_TEACHERS_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK;
31748 	this->startHour=sh;
31749 	this->endHour=eh;
31750 	assert(sh<eh);
31751 	assert(sh>=0);
31752 }
31753 
computeInternalStructure(QWidget * parent,Rules & r)31754 bool ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
31755 {
31756 	if(this->startHour>=this->endHour){
31757 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
31758 		 tr("Constraint teachers afternoon interval max days per week is wrong because start hour >= end hour."
31759 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
31760 
31761 		return false;
31762 	}
31763 	if(this->startHour<0){
31764 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
31765 		 tr("Constraint teachers afternoon interval max days per week is wrong because start hour < first hour of the day."
31766 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
31767 
31768 		return false;
31769 	}
31770 	if(this->endHour>r.nHoursPerDay){
31771 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
31772 		 tr("Constraint teachers afternoon interval max days per week is wrong because end hour > number of hours per day."
31773 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
31774 
31775 		return false;
31776 	}
31777 	return true;
31778 }
31779 
hasInactiveActivities(Rules & r)31780 bool ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
31781 {
31782 	Q_UNUSED(r);
31783 	return false;
31784 }
31785 
getXmlDescription(Rules & r)31786 QString ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
31787 {
31788 	Q_UNUSED(r);
31789 
31790 	QString s="<ConstraintTeachersAfternoonIntervalMaxDaysPerWeek>\n";
31791 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
31792 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
31793 	if(this->endHour < r.nHoursPerDay){
31794 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
31795 	}
31796 	else{
31797 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
31798 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
31799 	}
31800 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
31801 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
31802 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
31803 	s+="</ConstraintTeachersAfternoonIntervalMaxDaysPerWeek>\n";
31804 	return s;
31805 }
31806 
getDescription(Rules & r)31807 QString ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::getDescription(Rules& r){
31808 	Q_UNUSED(r);
31809 
31810 	QString begin=QString("");
31811 	if(!active)
31812 		begin="X - ";
31813 
31814 	QString end=QString("");
31815 	if(!comments.isEmpty())
31816 		end=", "+tr("C: %1", "Comments").arg(comments);
31817 
31818 	QString s=tr("Teachers afternoon interval max days per week");s+=", ";
31819 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
31820 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);
31821 	s+=", ";
31822 	if(this->endHour<r.nHoursPerDay)
31823 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
31824 	else
31825 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
31826 	s+=", ";
31827 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
31828 
31829 	return begin+s+end;
31830 }
31831 
getDetailedDescription(Rules & r)31832 QString ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r){
31833 	Q_UNUSED(r);
31834 
31835 	QString s=tr("Time constraint");s+="\n";
31836 	s+=tr("All teachers respect working in an hourly afternoon interval a maximum number of days per week");s+="\n";
31837 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
31838 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
31839 
31840 	if(this->endHour<r.nHoursPerDay)
31841 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
31842 	else
31843 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
31844 	s+="\n";
31845 
31846 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
31847 
31848 	if(!active){
31849 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
31850 		s+="\n";
31851 	}
31852 	if(!comments.isEmpty()){
31853 		s+=tr("Comments=%1").arg(comments);
31854 		s+="\n";
31855 	}
31856 
31857 	return s;
31858 }
31859 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)31860 double ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
31861 {
31862 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
31863 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
31864 		c.teachersMatrixReady=true;
31865 		c.subgroupsMatrixReady=true;
31866 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
31867 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
31868 
31869 		c.changedForMatrixCalculation=false;
31870 	}
31871 
31872 	int nbroken=0;
31873 
31874 	Matrix1D<bool> ocDay;
31875 	ocDay.resize(r.nDaysPerWeek);
31876 	for(int t=0; t<r.nInternalTeachers; t++){
31877 		for(int d=1; d<r.nDaysPerWeek; d+=2){ //afternoon
31878 			ocDay[d]=false;
31879 			for(int h=startHour; h<endHour; h++){
31880 				if(teachersMatrix[t][d][h]>0){
31881 					ocDay[d]=true;
31882 				}
31883 			}
31884 		}
31885 		int nOcDays=0;
31886 		for(int d=1; d<r.nDaysPerWeek; d+=2) //afternoon
31887 			if(ocDay[d])
31888 				nOcDays++;
31889 		if(nOcDays > this->maxDaysPerWeek){
31890 			nbroken+=nOcDays-this->maxDaysPerWeek;
31891 
31892 			if(nOcDays-this->maxDaysPerWeek>0){
31893 				QString s= tr("Time constraint teachers afternoon interval max days per week broken for teacher: %1, allowed %2 days, required %3 days.")
31894 				 .arg(r.internalTeachersList[t]->name)
31895 				 .arg(this->maxDaysPerWeek)
31896 				 .arg(nOcDays);
31897 				s+=" ";
31898 				s += tr("This increases the conflicts total by %1")
31899 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
31900 
31901 				dl.append(s);
31902 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
31903 
31904 				*conflictsString += s+"\n";
31905 			}
31906 		}
31907 	}
31908 
31909 	if(weightPercentage==100)
31910 		assert(nbroken==0);
31911 	return weightPercentage/100 * nbroken;
31912 }
31913 
isRelatedToActivity(Rules & r,Activity * a)31914 bool ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
31915 {
31916 	Q_UNUSED(r);
31917 	Q_UNUSED(a);
31918 
31919 	return false;
31920 }
31921 
isRelatedToTeacher(Teacher * t)31922 bool ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
31923 {
31924 	Q_UNUSED(t);
31925 
31926 	return true;
31927 }
31928 
isRelatedToSubject(Subject * s)31929 bool ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
31930 {
31931 	Q_UNUSED(s);
31932 
31933 	return false;
31934 }
31935 
isRelatedToActivityTag(ActivityTag * s)31936 bool ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
31937 {
31938 	Q_UNUSED(s);
31939 
31940 	return false;
31941 }
31942 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)31943 bool ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
31944 {
31945 	Q_UNUSED(r);
31946 	Q_UNUSED(s);
31947 
31948 	return false;
31949 }
31950 
hasWrongDayOrHour(Rules & r)31951 bool ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
31952 {
31953 	if(this->startHour>=r.nHoursPerDay)
31954 		return true;
31955 	if(this->endHour>r.nHoursPerDay)
31956 		return true;
31957 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
31958 		return true;
31959 
31960 	return false;
31961 }
31962 
canRepairWrongDayOrHour(Rules & r)31963 bool ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
31964 {
31965 	assert(hasWrongDayOrHour(r));
31966 
31967 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
31968 		return true;
31969 
31970 	return false;
31971 }
31972 
repairWrongDayOrHour(Rules & r)31973 bool ConstraintTeachersAfternoonIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
31974 {
31975 	assert(hasWrongDayOrHour(r));
31976 
31977 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
31978 
31979 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
31980 		this->maxDaysPerWeek=r.nDaysPerWeek/2;
31981 
31982 	return true;
31983 }
31984 
31985 ///////////////////////////////////////////////////////////////////////////////////////////
31986 ///////////////////////////////////////////////////////////////////////////////////////////
31987 
ConstraintTeachersActivityTagMaxHoursDailyRealDays()31988 ConstraintTeachersActivityTagMaxHoursDailyRealDays::ConstraintTeachersActivityTagMaxHoursDailyRealDays()
31989 	: TimeConstraint()
31990 {
31991 	this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS;
31992 }
31993 
ConstraintTeachersActivityTagMaxHoursDailyRealDays(double wp,int maxhours,const QString & activityTag)31994 ConstraintTeachersActivityTagMaxHoursDailyRealDays::ConstraintTeachersActivityTagMaxHoursDailyRealDays(double wp, int maxhours, const QString& activityTag)
31995  : TimeConstraint(wp)
31996  {
31997 	assert(maxhours>0);
31998 	this->maxHoursDaily=maxhours;
31999 	this->activityTagName=activityTag;
32000 
32001 	this->type=CONSTRAINT_TEACHERS_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS;
32002 }
32003 
computeInternalStructure(QWidget * parent,Rules & r)32004 bool ConstraintTeachersActivityTagMaxHoursDailyRealDays::computeInternalStructure(QWidget* parent, Rules& r)
32005 {
32006 	Q_UNUSED(parent);
32007 
32008 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
32009 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
32010 	assert(this->activityTagIndex>=0);
32011 
32012 	this->canonicalTeachersList.clear();
32013 	for(int i=0; i<r.nInternalTeachers; i++){
32014 		bool found=false;
32015 
32016 		Teacher* tch=r.internalTeachersList[i];
32017 		for(int actIndex : qAsConst(tch->activitiesForTeacher)){
32018 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
32019 				found=true;
32020 				break;
32021 			}
32022 		}
32023 
32024 		if(found)
32025 			this->canonicalTeachersList.append(i);
32026 	}
32027 
32028 	return true;
32029 }
32030 
hasInactiveActivities(Rules & r)32031 bool ConstraintTeachersActivityTagMaxHoursDailyRealDays::hasInactiveActivities(Rules& r)
32032 {
32033 	Q_UNUSED(r);
32034 	return false;
32035 }
32036 
getXmlDescription(Rules & r)32037 QString ConstraintTeachersActivityTagMaxHoursDailyRealDays::getXmlDescription(Rules& r){
32038 	Q_UNUSED(r);
32039 
32040 	QString s="<ConstraintTeachersActivityTagMaxHoursDailyRealDays>\n";
32041 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
32042 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
32043 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
32044 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
32045 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
32046 	s+="</ConstraintTeachersActivityTagMaxHoursDailyRealDays>\n";
32047 	return s;
32048 }
32049 
getDescription(Rules & r)32050 QString ConstraintTeachersActivityTagMaxHoursDailyRealDays::getDescription(Rules& r){
32051 	Q_UNUSED(r);
32052 
32053 	QString begin=QString("");
32054 	if(!active)
32055 		begin="X - ";
32056 
32057 	QString end=QString("");
32058 	if(!comments.isEmpty())
32059 		end=", "+tr("C: %1", "Comments").arg(comments);
32060 
32061 	QString s;
32062 	s+="! ";
32063 	s+=tr("Teachers for activity tag %1 have max %2 hours daily per real day").arg(this->activityTagName).arg(this->maxHoursDaily);s+=", ";
32064 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
32065 
32066 	return begin+s+end;
32067 }
32068 
getDetailedDescription(Rules & r)32069 QString ConstraintTeachersActivityTagMaxHoursDailyRealDays::getDetailedDescription(Rules& r){
32070 	Q_UNUSED(r);
32071 
32072 	QString s=tr("Time constraint");s+="\n";
32073 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
32074 	s+=tr("All teachers, for an activity tag, must respect the maximum number of hours daily per real day");s+="\n";
32075 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
32076 	s+=tr("Activity tag=%1").arg(this->activityTagName); s+="\n";
32077 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily); s+="\n";
32078 
32079 	if(!active){
32080 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
32081 		s+="\n";
32082 	}
32083 	if(!comments.isEmpty()){
32084 		s+=tr("Comments=%1").arg(comments);
32085 		s+="\n";
32086 	}
32087 
32088 	return s;
32089 }
32090 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)32091 double ConstraintTeachersActivityTagMaxHoursDailyRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
32092 {
32093 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
32094 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
32095 		c.teachersMatrixReady=true;
32096 		c.subgroupsMatrixReady=true;
32097 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
32098 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
32099 
32100 		c.changedForMatrixCalculation=false;
32101 	}
32102 
32103 	int nbroken;
32104 
32105 	nbroken=0;
32106 	Matrix2D<int> crtTeacherTimetableActivityTag;
32107 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
32108 	for(int i : qAsConst(this->canonicalTeachersList)){
32109 		Teacher* tch=r.internalTeachersList[i];
32110 		for(int d=0; d<r.nDaysPerWeek; d++)
32111 			for(int h=0; h<r.nHoursPerDay; h++)
32112 				crtTeacherTimetableActivityTag[d][h]=-1;
32113 
32114 		for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
32115 			int d=c.times[ai]%r.nDaysPerWeek;
32116 			int h=c.times[ai]/r.nDaysPerWeek;
32117 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
32118 				assert(h+dur<r.nHoursPerDay);
32119 				assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
32120 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
32121 					crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
32122 			}
32123 		}
32124 
32125 		for(int d=0; d<r.nDaysPerWeek/2; d++){
32126 			int d1=d*2;
32127 			int d2=d*2+1;
32128 			int nd=0;
32129 			for(int h=0; h<r.nHoursPerDay; h++)
32130 				if(crtTeacherTimetableActivityTag[d1][h]==this->activityTagIndex)
32131 					nd++;
32132 			for(int h=0; h<r.nHoursPerDay; h++)
32133 				if(crtTeacherTimetableActivityTag[d2][h]==this->activityTagIndex)
32134 					nd++;
32135 
32136 			if(nd>this->maxHoursDaily){
32137 				nbroken++;
32138 
32139 				if(conflictsString!=nullptr){
32140 					QString s=(tr("Time constraint teachers activity tag %1 max %2 hours daily per real day broken for teacher %3, on real day %4, length=%5.")
32141 					 .arg(this->activityTagName)
32142 					 .arg(CustomFETString::number(this->maxHoursDaily))
32143 					 .arg(r.internalTeachersList[i]->name)
32144 					 .arg(d/*r.daysOfTheWeek[d]*/)
32145 					 .arg(nd)
32146 					 )
32147 					 +
32148 					 " "
32149 					 +
32150 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
32151 
32152 					dl.append(s);
32153 					cl.append(weightPercentage/100.0);
32154 
32155 					*conflictsString+= s+"\n";
32156 				}
32157 			}
32158 		}
32159 	}
32160 
32161 	if(weightPercentage==100.0)
32162 		assert(nbroken==0);
32163 	return weightPercentage/100.0 * nbroken;
32164 }
32165 
isRelatedToActivity(Rules & r,Activity * a)32166 bool ConstraintTeachersActivityTagMaxHoursDailyRealDays::isRelatedToActivity(Rules& r, Activity* a)
32167 {
32168 	Q_UNUSED(r);
32169 	Q_UNUSED(a);
32170 
32171 	return false;
32172 }
32173 
isRelatedToTeacher(Teacher * t)32174 bool ConstraintTeachersActivityTagMaxHoursDailyRealDays::isRelatedToTeacher(Teacher* t)
32175 {
32176 	Q_UNUSED(t);
32177 
32178 	return true;
32179 }
32180 
isRelatedToSubject(Subject * s)32181 bool ConstraintTeachersActivityTagMaxHoursDailyRealDays::isRelatedToSubject(Subject* s)
32182 {
32183 	Q_UNUSED(s);
32184 
32185 	return false;
32186 }
32187 
isRelatedToActivityTag(ActivityTag * s)32188 bool ConstraintTeachersActivityTagMaxHoursDailyRealDays::isRelatedToActivityTag(ActivityTag* s)
32189 {
32190 	return s->name==this->activityTagName;
32191 }
32192 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)32193 bool ConstraintTeachersActivityTagMaxHoursDailyRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
32194 {
32195 	Q_UNUSED(r);
32196 	Q_UNUSED(s);
32197 
32198 	return false;
32199 }
32200 
hasWrongDayOrHour(Rules & r)32201 bool ConstraintTeachersActivityTagMaxHoursDailyRealDays::hasWrongDayOrHour(Rules& r)
32202 {
32203 	if(maxHoursDaily>2*r.nHoursPerDay)
32204 		return true;
32205 
32206 	return false;
32207 }
32208 
canRepairWrongDayOrHour(Rules & r)32209 bool ConstraintTeachersActivityTagMaxHoursDailyRealDays::canRepairWrongDayOrHour(Rules& r)
32210 {
32211 	assert(hasWrongDayOrHour(r));
32212 
32213 	return true;
32214 }
32215 
repairWrongDayOrHour(Rules & r)32216 bool ConstraintTeachersActivityTagMaxHoursDailyRealDays::repairWrongDayOrHour(Rules& r)
32217 {
32218 	assert(hasWrongDayOrHour(r));
32219 
32220 	if(maxHoursDaily>2*r.nHoursPerDay)
32221 		maxHoursDaily=2*r.nHoursPerDay;
32222 
32223 	return true;
32224 }
32225 
32226 ///////////////////////////////////////////////////////////////////////////////////////////
32227 ///////////////////////////////////////////////////////////////////////////////////////////
32228 
ConstraintTeacherActivityTagMaxHoursDailyRealDays()32229 ConstraintTeacherActivityTagMaxHoursDailyRealDays::ConstraintTeacherActivityTagMaxHoursDailyRealDays()
32230 	: TimeConstraint()
32231 {
32232 	this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS;
32233 }
32234 
ConstraintTeacherActivityTagMaxHoursDailyRealDays(double wp,int maxhours,const QString & teacher,const QString & activityTag)32235 ConstraintTeacherActivityTagMaxHoursDailyRealDays::ConstraintTeacherActivityTagMaxHoursDailyRealDays(double wp, int maxhours, const QString& teacher, const QString& activityTag)
32236  : TimeConstraint(wp)
32237  {
32238 	assert(maxhours>0);
32239 	this->maxHoursDaily=maxhours;
32240 	this->teacherName=teacher;
32241 	this->activityTagName=activityTag;
32242 
32243 	this->type=CONSTRAINT_TEACHER_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS;
32244 }
32245 
computeInternalStructure(QWidget * parent,Rules & r)32246 bool ConstraintTeacherActivityTagMaxHoursDailyRealDays::computeInternalStructure(QWidget* parent, Rules& r)
32247 {
32248 	Q_UNUSED(parent);
32249 
32250 	//this->teacher_ID=r.searchTeacher(this->teacherName);
32251 	teacher_ID=r.teachersHash.value(teacherName, -1);
32252 	assert(this->teacher_ID>=0);
32253 
32254 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
32255 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
32256 	assert(this->activityTagIndex>=0);
32257 
32258 	this->canonicalTeachersList.clear();
32259 	int i=this->teacher_ID;
32260 	bool found=false;
32261 
32262 	Teacher* tch=r.internalTeachersList[i];
32263 	for(int actIndex : qAsConst(tch->activitiesForTeacher)){
32264 		if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
32265 			found=true;
32266 			break;
32267 		}
32268 	}
32269 
32270 	if(found)
32271 		this->canonicalTeachersList.append(i);
32272 
32273 	return true;
32274 }
32275 
hasInactiveActivities(Rules & r)32276 bool ConstraintTeacherActivityTagMaxHoursDailyRealDays::hasInactiveActivities(Rules& r)
32277 {
32278 	Q_UNUSED(r);
32279 	return false;
32280 }
32281 
getXmlDescription(Rules & r)32282 QString ConstraintTeacherActivityTagMaxHoursDailyRealDays::getXmlDescription(Rules& r){
32283 	Q_UNUSED(r);
32284 
32285 	QString s="<ConstraintTeacherActivityTagMaxHoursDailyRealDays>\n";
32286 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
32287 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
32288 	s+="	<Activity_Tag_Name>"+protect(this->activityTagName)+"</Activity_Tag_Name>\n";
32289 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
32290 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
32291 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
32292 	s+="</ConstraintTeacherActivityTagMaxHoursDailyRealDays>\n";
32293 	return s;
32294 }
32295 
getDescription(Rules & r)32296 QString ConstraintTeacherActivityTagMaxHoursDailyRealDays::getDescription(Rules& r){
32297 	Q_UNUSED(r);
32298 
32299 	QString begin=QString("");
32300 	if(!active)
32301 		begin="X - ";
32302 
32303 	QString end=QString("");
32304 	if(!comments.isEmpty())
32305 		end=", "+tr("C: %1", "Comments").arg(comments);
32306 
32307 	QString s;
32308 	s+="! ";
32309 	s+=tr("Teacher %1 for activity tag %2 has max %3 hours daily per real day").arg(this->teacherName).arg(this->activityTagName).arg(this->maxHoursDaily);s+=", ";
32310 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
32311 
32312 	return begin+s+end;
32313 }
32314 
getDetailedDescription(Rules & r)32315 QString ConstraintTeacherActivityTagMaxHoursDailyRealDays::getDetailedDescription(Rules& r){
32316 	Q_UNUSED(r);
32317 
32318 	QString s=tr("Time constraint");s+="\n";
32319 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
32320 	s+=tr("A teacher for an activity tag must respect the maximum number of hours daily per real day");s+="\n";
32321 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
32322 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
32323 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
32324 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily); s+="\n";
32325 
32326 	if(!active){
32327 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
32328 		s+="\n";
32329 	}
32330 	if(!comments.isEmpty()){
32331 		s+=tr("Comments=%1").arg(comments);
32332 		s+="\n";
32333 	}
32334 
32335 	return s;
32336 }
32337 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)32338 double ConstraintTeacherActivityTagMaxHoursDailyRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
32339 {
32340 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
32341 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
32342 		c.teachersMatrixReady=true;
32343 		c.subgroupsMatrixReady=true;
32344 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
32345 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
32346 
32347 		c.changedForMatrixCalculation=false;
32348 	}
32349 
32350 	int nbroken;
32351 
32352 	nbroken=0;
32353 	Matrix2D<int> crtTeacherTimetableActivityTag;
32354 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
32355 	for(int i : qAsConst(this->canonicalTeachersList)){
32356 		Teacher* tch=r.internalTeachersList[i];
32357 		for(int d=0; d<r.nDaysPerWeek; d++)
32358 			for(int h=0; h<r.nHoursPerDay; h++)
32359 				crtTeacherTimetableActivityTag[d][h]=-1;
32360 
32361 		for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
32362 			int d=c.times[ai]%r.nDaysPerWeek;
32363 			int h=c.times[ai]/r.nDaysPerWeek;
32364 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
32365 				assert(h+dur<r.nHoursPerDay);
32366 				assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
32367 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
32368 					crtTeacherTimetableActivityTag[d][h+dur]=this->activityTagIndex;
32369 			}
32370 		}
32371 
32372 		for(int d=0; d<r.nDaysPerWeek/2; d++){
32373 			int d1=2*d;
32374 			int d2=2*d+1;
32375 			int nd=0;
32376 			for(int h=0; h<r.nHoursPerDay; h++)
32377 				if(crtTeacherTimetableActivityTag[d1][h]==this->activityTagIndex)
32378 					nd++;
32379 			for(int h=0; h<r.nHoursPerDay; h++)
32380 				if(crtTeacherTimetableActivityTag[d2][h]==this->activityTagIndex)
32381 					nd++;
32382 
32383 			if(nd>this->maxHoursDaily){
32384 				nbroken++;
32385 
32386 				if(conflictsString!=nullptr){
32387 					QString s=(tr("Time constraint teacher activity tag %1 max %2 hours daily per real day broken for teacher %3, on real day %4, length=%5.")
32388 					 .arg(this->activityTagName)
32389 					 .arg(CustomFETString::number(this->maxHoursDaily))
32390 					 .arg(r.internalTeachersList[i]->name)
32391 					 .arg(d/*r.daysOfTheWeek[d]*/)
32392 					 .arg(nd)
32393 					 )
32394 					 +
32395 					 " "
32396 					 +
32397 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
32398 
32399 					dl.append(s);
32400 					cl.append(weightPercentage/100.0);
32401 
32402 					*conflictsString+= s+"\n";
32403 				}
32404 			}
32405 		}
32406 	}
32407 
32408 	if(weightPercentage==100.0)
32409 		assert(nbroken==0);
32410 	return weightPercentage/100.0 * nbroken;
32411 }
32412 
isRelatedToActivity(Rules & r,Activity * a)32413 bool ConstraintTeacherActivityTagMaxHoursDailyRealDays::isRelatedToActivity(Rules& r, Activity* a)
32414 {
32415 	Q_UNUSED(r);
32416 	Q_UNUSED(a);
32417 
32418 	return false;
32419 }
32420 
isRelatedToTeacher(Teacher * t)32421 bool ConstraintTeacherActivityTagMaxHoursDailyRealDays::isRelatedToTeacher(Teacher* t)
32422 {
32423 	if(this->teacherName==t->name)
32424 		return true;
32425 	return false;
32426 }
32427 
isRelatedToSubject(Subject * s)32428 bool ConstraintTeacherActivityTagMaxHoursDailyRealDays::isRelatedToSubject(Subject* s)
32429 {
32430 	Q_UNUSED(s);
32431 
32432 	return false;
32433 }
32434 
isRelatedToActivityTag(ActivityTag * s)32435 bool ConstraintTeacherActivityTagMaxHoursDailyRealDays::isRelatedToActivityTag(ActivityTag* s)
32436 {
32437 	return this->activityTagName==s->name;
32438 }
32439 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)32440 bool ConstraintTeacherActivityTagMaxHoursDailyRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
32441 {
32442 	Q_UNUSED(r);
32443 	Q_UNUSED(s);
32444 
32445 	return false;
32446 }
32447 
hasWrongDayOrHour(Rules & r)32448 bool ConstraintTeacherActivityTagMaxHoursDailyRealDays::hasWrongDayOrHour(Rules& r)
32449 {
32450 	if(maxHoursDaily>2*r.nHoursPerDay)
32451 		return true;
32452 
32453 	return false;
32454 }
32455 
canRepairWrongDayOrHour(Rules & r)32456 bool ConstraintTeacherActivityTagMaxHoursDailyRealDays::canRepairWrongDayOrHour(Rules& r)
32457 {
32458 	assert(hasWrongDayOrHour(r));
32459 
32460 	return true;
32461 }
32462 
repairWrongDayOrHour(Rules & r)32463 bool ConstraintTeacherActivityTagMaxHoursDailyRealDays::repairWrongDayOrHour(Rules& r)
32464 {
32465 	assert(hasWrongDayOrHour(r));
32466 
32467 	if(maxHoursDaily>2*r.nHoursPerDay)
32468 		maxHoursDaily=2*r.nHoursPerDay;
32469 
32470 	return true;
32471 }
32472 
32473 ////////////////////////////////////////////////////////////////////////////////////////////
32474 ////////////////////////////////////////////////////////////////////////////////////////////
32475 
ConstraintStudentsActivityTagMaxHoursDailyRealDays()32476 ConstraintStudentsActivityTagMaxHoursDailyRealDays::ConstraintStudentsActivityTagMaxHoursDailyRealDays()
32477 	: TimeConstraint()
32478 {
32479 	this->type = CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS;
32480 	this->maxHoursDaily = -1;
32481 }
32482 
ConstraintStudentsActivityTagMaxHoursDailyRealDays(double wp,int maxnh,const QString & activityTag)32483 ConstraintStudentsActivityTagMaxHoursDailyRealDays::ConstraintStudentsActivityTagMaxHoursDailyRealDays(double wp, int maxnh, const QString& activityTag)
32484 	: TimeConstraint(wp)
32485 {
32486 	this->maxHoursDaily = maxnh;
32487 	this->activityTagName=activityTag;
32488 	this->type = CONSTRAINT_STUDENTS_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS;
32489 }
32490 
computeInternalStructure(QWidget * parent,Rules & r)32491 bool ConstraintStudentsActivityTagMaxHoursDailyRealDays::computeInternalStructure(QWidget* parent, Rules& r)
32492 {
32493 	Q_UNUSED(parent);
32494 
32495 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
32496 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
32497 	assert(this->activityTagIndex>=0);
32498 
32499 	this->canonicalSubgroupsList.clear();
32500 	for(int i=0; i<r.nInternalSubgroups; i++){
32501 		bool found=false;
32502 
32503 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
32504 		for(int actIndex : qAsConst(sbg->activitiesForSubgroup)){
32505 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
32506 				found=true;
32507 				break;
32508 			}
32509 		}
32510 
32511 		if(found)
32512 			this->canonicalSubgroupsList.append(i);
32513 	}
32514 
32515 	return true;
32516 }
32517 
hasInactiveActivities(Rules & r)32518 bool ConstraintStudentsActivityTagMaxHoursDailyRealDays::hasInactiveActivities(Rules& r)
32519 {
32520 	Q_UNUSED(r);
32521 	return false;
32522 }
32523 
getXmlDescription(Rules & r)32524 QString ConstraintStudentsActivityTagMaxHoursDailyRealDays::getXmlDescription(Rules& r)
32525 {
32526 	Q_UNUSED(r);
32527 
32528 	QString s="<ConstraintStudentsActivityTagMaxHoursDailyRealDays>\n";
32529 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
32530 
32531 	s+="	<Activity_Tag>"+protect(this->activityTagName)+"</Activity_Tag>\n";
32532 
32533 	if(this->maxHoursDaily>=0)
32534 		s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
32535 	else
32536 		assert(0);
32537 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
32538 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
32539 	s+="</ConstraintStudentsActivityTagMaxHoursDailyRealDays>\n";
32540 	return s;
32541 }
32542 
getDescription(Rules & r)32543 QString ConstraintStudentsActivityTagMaxHoursDailyRealDays::getDescription(Rules& r)
32544 {
32545 	Q_UNUSED(r);
32546 
32547 	QString begin=QString("");
32548 	if(!active)
32549 		begin="X - ";
32550 
32551 	QString end=QString("");
32552 	if(!comments.isEmpty())
32553 		end=", "+tr("C: %1", "Comments").arg(comments);
32554 
32555 	QString s;
32556 	s+="! ";
32557 	s+=tr("Students for activity tag %1 have max %2 hours daily per real day")
32558 		.arg(this->activityTagName).arg(this->maxHoursDaily); s+=", ";
32559 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
32560 
32561 	return begin+s+end;
32562 }
32563 
getDetailedDescription(Rules & r)32564 QString ConstraintStudentsActivityTagMaxHoursDailyRealDays::getDetailedDescription(Rules& r)
32565 {
32566 	Q_UNUSED(r);
32567 
32568 	QString s=tr("Time constraint");s+="\n";
32569 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
32570 	s+=tr("All students, for an activity tag, must respect the maximum number of hours daily per real day"); s+="\n";
32571 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
32572 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
32573 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
32574 
32575 	if(!active){
32576 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
32577 		s+="\n";
32578 	}
32579 	if(!comments.isEmpty()){
32580 		s+=tr("Comments=%1").arg(comments);
32581 		s+="\n";
32582 	}
32583 
32584 	return s;
32585 }
32586 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)32587 double ConstraintStudentsActivityTagMaxHoursDailyRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
32588 {
32589 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
32590 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
32591 		c.teachersMatrixReady=true;
32592 		c.subgroupsMatrixReady=true;
32593 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
32594 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
32595 
32596 		c.changedForMatrixCalculation=false;
32597 	}
32598 
32599 	int nbroken;
32600 
32601 	nbroken=0;
32602 
32603 	Matrix2D<int> crtSubgroupTimetableActivityTag;
32604 	crtSubgroupTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
32605 	for(int i : qAsConst(this->canonicalSubgroupsList)){
32606 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
32607 		for(int d=0; d<r.nDaysPerWeek; d++)
32608 			for(int h=0; h<r.nHoursPerDay; h++)
32609 				crtSubgroupTimetableActivityTag[d][h]=-1;
32610 		for(int ai : qAsConst(sbg->activitiesForSubgroup)) if(c.times[ai]!=UNALLOCATED_TIME){
32611 			int d=c.times[ai]%r.nDaysPerWeek;
32612 			int h=c.times[ai]/r.nDaysPerWeek;
32613 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
32614 				assert(h+dur<r.nHoursPerDay);
32615 				assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
32616 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
32617 					crtSubgroupTimetableActivityTag[d][h+dur]=this->activityTagIndex;
32618 			}
32619 		}
32620 
32621 		for(int d=0; d<r.nDaysPerWeek/2; d++){
32622 			int d1=d*2;
32623 			int d2=d*2+1;
32624 			int nd=0;
32625 			for(int h=0; h<r.nHoursPerDay; h++)
32626 				if(crtSubgroupTimetableActivityTag[d1][h]==this->activityTagIndex)
32627 					nd++;
32628 			for(int h=0; h<r.nHoursPerDay; h++)
32629 				if(crtSubgroupTimetableActivityTag[d2][h]==this->activityTagIndex)
32630 					nd++;
32631 
32632 			if(nd>this->maxHoursDaily){
32633 				nbroken++;
32634 
32635 				if(conflictsString!=nullptr){
32636 					QString s=(tr(
32637 					 "Time constraint students, activity tag %1, max %2 hours daily per real day, broken for subgroup %3, on real day %4, length=%5.")
32638 					 .arg(this->activityTagName)
32639 					 .arg(CustomFETString::number(this->maxHoursDaily))
32640 					 .arg(r.internalSubgroupsList[i]->name)
32641 					 .arg(d/*r.daysOfTheWeek[d]*/)
32642 					 .arg(nd)
32643 					 )
32644 					 +
32645 					 " "
32646 					 +
32647 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
32648 
32649 					dl.append(s);
32650 					cl.append(weightPercentage/100);
32651 
32652 					*conflictsString+= s+"\n";
32653 				}
32654 			}
32655 		}
32656 	}
32657 
32658 	if(weightPercentage==100.0)
32659 		assert(nbroken==0);
32660 	return weightPercentage/100.0 * nbroken;
32661 }
32662 
isRelatedToActivity(Rules & r,Activity * a)32663 bool ConstraintStudentsActivityTagMaxHoursDailyRealDays::isRelatedToActivity(Rules& r, Activity* a)
32664 {
32665 	Q_UNUSED(r);
32666 	Q_UNUSED(a);
32667 
32668 	return false;
32669 }
32670 
isRelatedToTeacher(Teacher * t)32671 bool ConstraintStudentsActivityTagMaxHoursDailyRealDays::isRelatedToTeacher(Teacher* t)
32672 {
32673 	Q_UNUSED(t);
32674 
32675 	return false;
32676 }
32677 
isRelatedToSubject(Subject * s)32678 bool ConstraintStudentsActivityTagMaxHoursDailyRealDays::isRelatedToSubject(Subject* s)
32679 {
32680 	Q_UNUSED(s);
32681 
32682 	return false;
32683 }
32684 
isRelatedToActivityTag(ActivityTag * s)32685 bool ConstraintStudentsActivityTagMaxHoursDailyRealDays::isRelatedToActivityTag(ActivityTag* s)
32686 {
32687 	return s->name==this->activityTagName;
32688 }
32689 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)32690 bool ConstraintStudentsActivityTagMaxHoursDailyRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
32691 {
32692 	Q_UNUSED(r);
32693 	Q_UNUSED(s);
32694 
32695 	return true;
32696 }
32697 
hasWrongDayOrHour(Rules & r)32698 bool ConstraintStudentsActivityTagMaxHoursDailyRealDays::hasWrongDayOrHour(Rules& r)
32699 {
32700 	if(maxHoursDaily>2*r.nHoursPerDay)
32701 		return true;
32702 
32703 	return false;
32704 }
32705 
canRepairWrongDayOrHour(Rules & r)32706 bool ConstraintStudentsActivityTagMaxHoursDailyRealDays::canRepairWrongDayOrHour(Rules& r)
32707 {
32708 	assert(hasWrongDayOrHour(r));
32709 
32710 	return true;
32711 }
32712 
repairWrongDayOrHour(Rules & r)32713 bool ConstraintStudentsActivityTagMaxHoursDailyRealDays::repairWrongDayOrHour(Rules& r)
32714 {
32715 	assert(hasWrongDayOrHour(r));
32716 
32717 	if(maxHoursDaily>2*r.nHoursPerDay)
32718 		maxHoursDaily=2*r.nHoursPerDay;
32719 
32720 	return true;
32721 }
32722 
32723 ////////////////////////////////////////////////////////////////////////////////////////////
32724 ////////////////////////////////////////////////////////////////////////////////////////////
32725 
ConstraintStudentsSetActivityTagMaxHoursDailyRealDays()32726 ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::ConstraintStudentsSetActivityTagMaxHoursDailyRealDays()
32727 	: TimeConstraint()
32728 {
32729 	this->type = CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS;
32730 	this->maxHoursDaily = -1;
32731 }
32732 
ConstraintStudentsSetActivityTagMaxHoursDailyRealDays(double wp,int maxnh,const QString & s,const QString & activityTag)32733 ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::ConstraintStudentsSetActivityTagMaxHoursDailyRealDays(double wp, int maxnh, const QString& s, const QString& activityTag)
32734 	: TimeConstraint(wp)
32735 {
32736 	this->maxHoursDaily = maxnh;
32737 	this->students = s;
32738 	this->activityTagName=activityTag;
32739 	this->type = CONSTRAINT_STUDENTS_SET_ACTIVITY_TAG_MAX_HOURS_DAILY_REAL_DAYS;
32740 }
32741 
hasInactiveActivities(Rules & r)32742 bool ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::hasInactiveActivities(Rules& r)
32743 {
32744 	Q_UNUSED(r);
32745 	return false;
32746 }
32747 
getXmlDescription(Rules & r)32748 QString ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::getXmlDescription(Rules& r)
32749 {
32750 	Q_UNUSED(r);
32751 
32752 	QString s="<ConstraintStudentsSetActivityTagMaxHoursDailyRealDays>\n";
32753 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
32754 	s+="	<Maximum_Hours_Daily>"+CustomFETString::number(this->maxHoursDaily)+"</Maximum_Hours_Daily>\n";
32755 	s+="	<Students>"+protect(this->students)+"</Students>\n";
32756 	s+="	<Activity_Tag>"+protect(this->activityTagName)+"</Activity_Tag>\n";
32757 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
32758 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
32759 	s+="</ConstraintStudentsSetActivityTagMaxHoursDailyRealDays>\n";
32760 	return s;
32761 }
32762 
getDescription(Rules & r)32763 QString ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::getDescription(Rules& r)
32764 {
32765 	Q_UNUSED(r);
32766 
32767 	QString begin=QString("");
32768 	if(!active)
32769 		begin="X - ";
32770 
32771 	QString end=QString("");
32772 	if(!comments.isEmpty())
32773 		end=", "+tr("C: %1", "Comments").arg(comments);
32774 
32775 	QString s;
32776 	s+="! ";
32777 	s+=tr("Students set %1 for activity tag %2 has max %3 hours daily per real day").arg(this->students).arg(this->activityTagName).arg(this->maxHoursDaily);
32778 	s+=", ";
32779 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
32780 
32781 	return begin+s+end;
32782 }
32783 
getDetailedDescription(Rules & r)32784 QString ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::getDetailedDescription(Rules& r)
32785 {
32786 	Q_UNUSED(r);
32787 
32788 	QString s=tr("Time constraint");s+="\n";
32789 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
32790 	s+=tr("A students set, for an activity tag, must respect the maximum number of hours daily per real day"); s+="\n";
32791 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
32792 	s+=tr("Students set=%1").arg(this->students);s+="\n";
32793 	s+=tr("Activity tag=%1").arg(this->activityTagName);s+="\n";
32794 	s+=tr("Maximum hours daily=%1").arg(this->maxHoursDaily);s+="\n";
32795 
32796 	if(!active){
32797 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
32798 		s+="\n";
32799 	}
32800 	if(!comments.isEmpty()){
32801 		s+=tr("Comments=%1").arg(comments);
32802 		s+="\n";
32803 	}
32804 
32805 	return s;
32806 }
32807 
computeInternalStructure(QWidget * parent,Rules & r)32808 bool ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::computeInternalStructure(QWidget* parent, Rules& r)
32809 {
32810 	//this->activityTagIndex=r.searchActivityTag(this->activityTagName);
32811 	activityTagIndex=r.activityTagsHash.value(activityTagName, -1);
32812 	assert(this->activityTagIndex>=0);
32813 
32814 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
32815 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
32816 
32817 	if(ss==nullptr){
32818 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
32819 		 tr("Constraint students set max hours daily is wrong because it refers to inexistent students set."
32820 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
32821 
32822 		return false;
32823 	}
32824 
32825 	assert(ss!=nullptr);
32826 
32827 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
32828 	/*this->iSubgroupsList.clear();
32829 	if(ss->type==STUDENTS_SUBGROUP){
32830 		int tmp;
32831 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
32832 		assert(tmp>=0);
32833 		assert(tmp<r.nInternalSubgroups);
32834 		if(!this->iSubgroupsList.contains(tmp))
32835 			this->iSubgroupsList.append(tmp);
32836 	}
32837 	else if(ss->type==STUDENTS_GROUP){
32838 		StudentsGroup* stg=(StudentsGroup*)ss;
32839 		for(int i=0; i<stg->subgroupsList.size(); i++){
32840 			StudentsSubgroup* sts=stg->subgroupsList[i];
32841 			int tmp;
32842 			tmp=sts->indexInInternalSubgroupsList;
32843 			assert(tmp>=0);
32844 			assert(tmp<r.nInternalSubgroups);
32845 			if(!this->iSubgroupsList.contains(tmp))
32846 				this->iSubgroupsList.append(tmp);
32847 		}
32848 	}
32849 	else if(ss->type==STUDENTS_YEAR){
32850 		StudentsYear* sty=(StudentsYear*)ss;
32851 		for(int i=0; i<sty->groupsList.size(); i++){
32852 			StudentsGroup* stg=sty->groupsList[i];
32853 			for(int j=0; j<stg->subgroupsList.size(); j++){
32854 				StudentsSubgroup* sts=stg->subgroupsList[j];
32855 				int tmp;
32856 				tmp=sts->indexInInternalSubgroupsList;
32857 				assert(tmp>=0);
32858 				assert(tmp<r.nInternalSubgroups);
32859 				if(!this->iSubgroupsList.contains(tmp))
32860 					this->iSubgroupsList.append(tmp);
32861 			}
32862 		}
32863 	}
32864 	else
32865 		assert(0);*/
32866 
32867 	/////////////
32868 	this->canonicalSubgroupsList.clear();
32869 	for(int i : qAsConst(this->iSubgroupsList)){
32870 		bool found=false;
32871 
32872 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
32873 		for(int actIndex : qAsConst(sbg->activitiesForSubgroup)){
32874 			if(r.internalActivitiesList[actIndex].iActivityTagsSet.contains(this->activityTagIndex)){
32875 				found=true;
32876 				break;
32877 			}
32878 		}
32879 
32880 		if(found)
32881 			this->canonicalSubgroupsList.append(i);
32882 	}
32883 
32884 
32885 	return true;
32886 }
32887 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)32888 double ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
32889 {
32890 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
32891 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
32892 		c.teachersMatrixReady=true;
32893 		c.subgroupsMatrixReady=true;
32894 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
32895 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
32896 
32897 		c.changedForMatrixCalculation=false;
32898 	}
32899 
32900 	int nbroken;
32901 
32902 	nbroken=0;
32903 
32904 	Matrix2D<int> crtSubgroupTimetableActivityTag;
32905 	crtSubgroupTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
32906 	for(int i : qAsConst(this->canonicalSubgroupsList)){
32907 		StudentsSubgroup* sbg=r.internalSubgroupsList[i];
32908 		for(int d=0; d<r.nDaysPerWeek; d++)
32909 			for(int h=0; h<r.nHoursPerDay; h++)
32910 				crtSubgroupTimetableActivityTag[d][h]=-1;
32911 		for(int ai : qAsConst(sbg->activitiesForSubgroup)) if(c.times[ai]!=UNALLOCATED_TIME){
32912 			int d=c.times[ai]%r.nDaysPerWeek;
32913 			int h=c.times[ai]/r.nDaysPerWeek;
32914 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
32915 				assert(h+dur<r.nHoursPerDay);
32916 				assert(crtSubgroupTimetableActivityTag[d][h+dur]==-1);
32917 				if(r.internalActivitiesList[ai].iActivityTagsSet.contains(this->activityTagIndex))
32918 					crtSubgroupTimetableActivityTag[d][h+dur]=this->activityTagIndex;
32919 			}
32920 		}
32921 
32922 		for(int d=0; d<r.nDaysPerWeek/2; d++){
32923 			int d1=2*d;
32924 			int d2=2*d+1;
32925 			int nd=0;
32926 			for(int h=0; h<r.nHoursPerDay; h++)
32927 				if(crtSubgroupTimetableActivityTag[d1][h]==this->activityTagIndex)
32928 					nd++;
32929 			for(int h=0; h<r.nHoursPerDay; h++)
32930 				if(crtSubgroupTimetableActivityTag[d2][h]==this->activityTagIndex)
32931 					nd++;
32932 
32933 			if(nd>this->maxHoursDaily){
32934 				nbroken++;
32935 
32936 				if(conflictsString!=nullptr){
32937 					QString s=(tr(
32938 					 "Time constraint students set, activity tag %1, max %2 hours daily per real day, broken for subgroup %3, on real day %4, length=%5.")
32939 					 .arg(this->activityTagName)
32940 					 .arg(CustomFETString::number(this->maxHoursDaily))
32941 					 .arg(r.internalSubgroupsList[i]->name)
32942 					 .arg(d/*r.daysOfTheWeek[d]*/)
32943 					 .arg(nd)
32944 					 )
32945 					 +
32946 					 " "
32947 					 +
32948 					 (tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100.0)));
32949 
32950 					dl.append(s);
32951 					cl.append(weightPercentage/100);
32952 
32953 					*conflictsString+= s+"\n";
32954 				}
32955 			}
32956 		}
32957 	}
32958 
32959 	if(weightPercentage==100.0)
32960 		assert(nbroken==0);
32961 	return weightPercentage/100.0 * nbroken;
32962 }
32963 
isRelatedToActivity(Rules & r,Activity * a)32964 bool ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::isRelatedToActivity(Rules& r, Activity* a)
32965 {
32966 	Q_UNUSED(r);
32967 	Q_UNUSED(a);
32968 
32969 	return false;
32970 }
32971 
isRelatedToTeacher(Teacher * t)32972 bool ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::isRelatedToTeacher(Teacher* t)
32973 {
32974 	Q_UNUSED(t);
32975 
32976 	return false;
32977 }
32978 
isRelatedToSubject(Subject * s)32979 bool ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::isRelatedToSubject(Subject* s)
32980 {
32981 	Q_UNUSED(s);
32982 
32983 	return false;
32984 }
32985 
isRelatedToActivityTag(ActivityTag * s)32986 bool ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::isRelatedToActivityTag(ActivityTag* s)
32987 {
32988 	Q_UNUSED(s);
32989 
32990 	return false;
32991 }
32992 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)32993 bool ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
32994 {
32995 	return r.setsShareStudents(this->students, s->name);
32996 }
32997 
hasWrongDayOrHour(Rules & r)32998 bool ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::hasWrongDayOrHour(Rules& r)
32999 {
33000 	if(maxHoursDaily>2*r.nHoursPerDay)
33001 		return true;
33002 
33003 	return false;
33004 }
33005 
canRepairWrongDayOrHour(Rules & r)33006 bool ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::canRepairWrongDayOrHour(Rules& r)
33007 {
33008 	assert(hasWrongDayOrHour(r));
33009 
33010 	return true;
33011 }
33012 
repairWrongDayOrHour(Rules & r)33013 bool ConstraintStudentsSetActivityTagMaxHoursDailyRealDays::repairWrongDayOrHour(Rules& r)
33014 {
33015 	assert(hasWrongDayOrHour(r));
33016 
33017 	if(maxHoursDaily>2*r.nHoursPerDay)
33018 		maxHoursDaily=2*r.nHoursPerDay;
33019 
33020 	return true;
33021 }
33022 
33023 ////////////////////////////////////////////////////////////////////////////////////////////
33024 ////////////////////////////////////////////////////////////////////////////////////////////
33025 
ConstraintStudentsMaxGapsPerRealDay()33026 ConstraintStudentsMaxGapsPerRealDay::ConstraintStudentsMaxGapsPerRealDay()
33027 	: TimeConstraint()
33028 {
33029 	this->type = CONSTRAINT_STUDENTS_MAX_GAPS_PER_REAL_DAY;
33030 }
33031 
ConstraintStudentsMaxGapsPerRealDay(double wp,int mg)33032 ConstraintStudentsMaxGapsPerRealDay::ConstraintStudentsMaxGapsPerRealDay(double wp, int mg)
33033 	: TimeConstraint(wp)
33034 {
33035 	this->type = CONSTRAINT_STUDENTS_MAX_GAPS_PER_REAL_DAY;
33036 	this->maxGaps=mg;
33037 }
33038 
computeInternalStructure(QWidget * parent,Rules & r)33039 bool ConstraintStudentsMaxGapsPerRealDay::computeInternalStructure(QWidget* parent, Rules& r)
33040 {
33041 	Q_UNUSED(parent);
33042 	Q_UNUSED(r);
33043 
33044 	return true;
33045 }
33046 
hasInactiveActivities(Rules & r)33047 bool ConstraintStudentsMaxGapsPerRealDay::hasInactiveActivities(Rules& r)
33048 {
33049 	Q_UNUSED(r);
33050 	return false;
33051 }
33052 
getXmlDescription(Rules & r)33053 QString ConstraintStudentsMaxGapsPerRealDay::getXmlDescription(Rules& r)
33054 {
33055 	Q_UNUSED(r);
33056 
33057 	QString s="<ConstraintStudentsMaxGapsPerRealDay>\n";
33058 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
33059 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
33060 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
33061 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
33062 	s+="</ConstraintStudentsMaxGapsPerRealDay>\n";
33063 	return s;
33064 }
33065 
getDescription(Rules & r)33066 QString ConstraintStudentsMaxGapsPerRealDay::getDescription(Rules& r)
33067 {
33068 	Q_UNUSED(r);
33069 
33070 	QString begin=QString("");
33071 	if(!active)
33072 		begin="X - ";
33073 
33074 	QString end=QString("");
33075 	if(!comments.isEmpty())
33076 		end=", "+tr("C: %1", "Comments").arg(comments);
33077 
33078 	QString s;
33079 	s+="! ";
33080 	s+=tr("Students max gaps per real day");s+=", ";
33081 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
33082 	s+=tr("MG:%1", "Max gaps (per real day)").arg(this->maxGaps);
33083 
33084 	return begin+s+end;
33085 }
33086 
getDetailedDescription(Rules & r)33087 QString ConstraintStudentsMaxGapsPerRealDay::getDetailedDescription(Rules& r)
33088 {
33089 	Q_UNUSED(r);
33090 
33091 	QString s=tr("Time constraint");s+="\n";
33092 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
33093 	s+=tr("All students must respect the maximum number of gaps per real day");s+="\n";
33094 	s+=tr("(breaks and students set not available not counted)");s+="\n";
33095 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
33096 	s+=tr("Maximum gaps per real day=%1").arg(this->maxGaps);s+="\n";
33097 
33098 	if(!active){
33099 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
33100 		s+="\n";
33101 	}
33102 	if(!comments.isEmpty()){
33103 		s+=tr("Comments=%1").arg(comments);
33104 		s+="\n";
33105 	}
33106 
33107 	return s;
33108 }
33109 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)33110 double ConstraintStudentsMaxGapsPerRealDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
33111 {
33112 	//returns a number equal to the number of gaps of the subgroups (in hours)
33113 
33114 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
33115 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
33116 		c.teachersMatrixReady=true;
33117 		c.subgroupsMatrixReady=true;
33118 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
33119 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
33120 
33121 		c.changedForMatrixCalculation=false;
33122 	}
33123 
33124 	int nGaps;
33125 	int tmp;
33126 	int i;
33127 
33128 	int tIllegalGaps=0;
33129 
33130 	for(i=0; i<r.nInternalSubgroups; i++){
33131 		for(int real_d=0; real_d<r.nDaysPerWeek/2; real_d++){
33132 			nGaps=0;
33133 
33134 			int double_h;
33135 
33136 			int k;
33137 			tmp=0;
33138 			for(double_h=0; double_h<2*r.nHoursPerDay; double_h++){
33139 				int j;
33140 				if(double_h<r.nHoursPerDay)
33141 					j=2*real_d;
33142 				else
33143 					j=2*real_d+1;
33144 				k=double_h%r.nHoursPerDay;
33145 				if(subgroupsMatrix[i][j][k]>0){
33146 					assert(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]);
33147 					break;
33148 				}
33149 			}
33150 			for(; double_h<2*r.nHoursPerDay; double_h++){
33151 				int j;
33152 				if(double_h<r.nHoursPerDay)
33153 					j=2*real_d;
33154 				else
33155 					j=2*real_d+1;
33156 				k=double_h%r.nHoursPerDay;
33157 				if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
33158 					if(subgroupsMatrix[i][j][k]>0){
33159 						nGaps+=tmp;
33160 						tmp=0;
33161 					}
33162 					else
33163 						tmp++;
33164 				}
33165 			}
33166 
33167 			int illegalGaps=nGaps-this->maxGaps;
33168 			if(illegalGaps<0)
33169 				illegalGaps=0;
33170 
33171 			if(illegalGaps>0 && conflictsString!=nullptr){
33172 				QString s=tr("Time constraint students max gaps per real day broken for subgroup: %1, it has %2 extra gaps, on real day number %3, conflicts increase=%4")
33173 				 .arg(r.internalSubgroupsList[i]->name)
33174 				 .arg(illegalGaps)
33175 				 .arg(real_d)
33176 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision(illegalGaps*weightPercentage/100));
33177 
33178 				dl.append(s);
33179 				cl.append(illegalGaps*weightPercentage/100);
33180 
33181 				*conflictsString+= s+"\n";
33182 			}
33183 
33184 			tIllegalGaps+=illegalGaps;
33185 		}
33186 	}
33187 
33188 	if(c.nPlacedActivities==r.nInternalActivities)
33189 		if(weightPercentage==100)    //for partial solutions it might be broken
33190 			assert(tIllegalGaps==0);
33191 	return weightPercentage/100 * tIllegalGaps;
33192 }
33193 
isRelatedToActivity(Rules & r,Activity * a)33194 bool ConstraintStudentsMaxGapsPerRealDay::isRelatedToActivity(Rules& r, Activity* a)
33195 {
33196 	Q_UNUSED(r);
33197 	Q_UNUSED(a);
33198 
33199 	return false;
33200 }
33201 
isRelatedToTeacher(Teacher * t)33202 bool ConstraintStudentsMaxGapsPerRealDay::isRelatedToTeacher(Teacher* t)
33203 {
33204 	Q_UNUSED(t);
33205 
33206 	return false;
33207 }
33208 
isRelatedToSubject(Subject * s)33209 bool ConstraintStudentsMaxGapsPerRealDay::isRelatedToSubject(Subject* s)
33210 {
33211 	Q_UNUSED(s);
33212 
33213 	return false;
33214 }
33215 
isRelatedToActivityTag(ActivityTag * s)33216 bool ConstraintStudentsMaxGapsPerRealDay::isRelatedToActivityTag(ActivityTag* s)
33217 {
33218 	Q_UNUSED(s);
33219 
33220 	return false;
33221 }
33222 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)33223 bool ConstraintStudentsMaxGapsPerRealDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
33224 {
33225 	Q_UNUSED(r);
33226 	Q_UNUSED(s);
33227 
33228 	return true;
33229 }
33230 
hasWrongDayOrHour(Rules & r)33231 bool ConstraintStudentsMaxGapsPerRealDay::hasWrongDayOrHour(Rules& r)
33232 {
33233 	if(maxGaps>2*r.nHoursPerDay)
33234 		return true;
33235 
33236 	return false;
33237 }
33238 
canRepairWrongDayOrHour(Rules & r)33239 bool ConstraintStudentsMaxGapsPerRealDay::canRepairWrongDayOrHour(Rules& r)
33240 {
33241 	assert(hasWrongDayOrHour(r));
33242 
33243 	return true;
33244 }
33245 
repairWrongDayOrHour(Rules & r)33246 bool ConstraintStudentsMaxGapsPerRealDay::repairWrongDayOrHour(Rules& r)
33247 {
33248 	assert(hasWrongDayOrHour(r));
33249 
33250 	if(maxGaps>2*r.nHoursPerDay)
33251 		maxGaps=2*r.nHoursPerDay;
33252 
33253 	return true;
33254 }
33255 
33256 ////////////////////////////////////////////////////////////////////////////////////////////
33257 ////////////////////////////////////////////////////////////////////////////////////////////
33258 
ConstraintStudentsSetMaxGapsPerRealDay()33259 ConstraintStudentsSetMaxGapsPerRealDay::ConstraintStudentsSetMaxGapsPerRealDay()
33260 	: TimeConstraint()
33261 {
33262 	this->type = CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_REAL_DAY;
33263 }
33264 
ConstraintStudentsSetMaxGapsPerRealDay(double wp,int mg,const QString & st)33265 ConstraintStudentsSetMaxGapsPerRealDay::ConstraintStudentsSetMaxGapsPerRealDay(double wp, int mg, const QString& st )
33266 	: TimeConstraint(wp)
33267 {
33268 	this->type = CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_REAL_DAY;
33269 	this->maxGaps=mg;
33270 	this->students = st;
33271 }
33272 
computeInternalStructure(QWidget * parent,Rules & r)33273 bool ConstraintStudentsSetMaxGapsPerRealDay::computeInternalStructure(QWidget* parent, Rules& r){
33274 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
33275 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
33276 
33277 	if(ss==nullptr){
33278 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
33279 		 tr("Constraint students set max gaps per real day is wrong because it refers to inexistent students set."
33280 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
33281 
33282 		return false;
33283 	}
33284 
33285 	assert(ss!=nullptr);
33286 
33287 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
33288 	/*this->iSubgroupsList.clear();
33289 	if(ss->type==STUDENTS_SUBGROUP){
33290 		int tmp;
33291 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
33292 		assert(tmp>=0);
33293 		assert(tmp<r.nInternalSubgroups);
33294 		if(!this->iSubgroupsList.contains(tmp))
33295 			this->iSubgroupsList.append(tmp);
33296 	}
33297 	else if(ss->type==STUDENTS_GROUP){
33298 		StudentsGroup* stg=(StudentsGroup*)ss;
33299 		for(int i=0; i<stg->subgroupsList.size(); i++){
33300 			StudentsSubgroup* sts=stg->subgroupsList[i];
33301 			int tmp;
33302 			tmp=sts->indexInInternalSubgroupsList;
33303 			assert(tmp>=0);
33304 			assert(tmp<r.nInternalSubgroups);
33305 			if(!this->iSubgroupsList.contains(tmp))
33306 				this->iSubgroupsList.append(tmp);
33307 		}
33308 	}
33309 	else if(ss->type==STUDENTS_YEAR){
33310 		StudentsYear* sty=(StudentsYear*)ss;
33311 		for(int i=0; i<sty->groupsList.size(); i++){
33312 			StudentsGroup* stg=sty->groupsList[i];
33313 			for(int j=0; j<stg->subgroupsList.size(); j++){
33314 				StudentsSubgroup* sts=stg->subgroupsList[j];
33315 				int tmp;
33316 				tmp=sts->indexInInternalSubgroupsList;
33317 				assert(tmp>=0);
33318 				assert(tmp<r.nInternalSubgroups);
33319 				if(!this->iSubgroupsList.contains(tmp))
33320 					this->iSubgroupsList.append(tmp);
33321 			}
33322 		}
33323 	}
33324 	else
33325 		assert(0);*/
33326 
33327 	return true;
33328 }
33329 
hasInactiveActivities(Rules & r)33330 bool ConstraintStudentsSetMaxGapsPerRealDay::hasInactiveActivities(Rules& r)
33331 {
33332 	Q_UNUSED(r);
33333 	return false;
33334 }
33335 
getXmlDescription(Rules & r)33336 QString ConstraintStudentsSetMaxGapsPerRealDay::getXmlDescription(Rules& r){
33337 	Q_UNUSED(r);
33338 
33339 	QString s="<ConstraintStudentsSetMaxGapsPerRealDay>\n";
33340 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
33341 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
33342 	s+="	<Students>"; s+=protect(this->students); s+="</Students>\n";
33343 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
33344 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
33345 	s+="</ConstraintStudentsSetMaxGapsPerRealDay>\n";
33346 	return s;
33347 }
33348 
getDescription(Rules & r)33349 QString ConstraintStudentsSetMaxGapsPerRealDay::getDescription(Rules& r){
33350 	Q_UNUSED(r);
33351 
33352 	QString begin=QString("");
33353 	if(!active)
33354 		begin="X - ";
33355 
33356 	QString end=QString("");
33357 	if(!comments.isEmpty())
33358 		end=", "+tr("C: %1", "Comments").arg(comments);
33359 
33360 	QString s;
33361 	s+="! ";
33362 	s+=tr("Students set max gaps per real day"); s+=", ";
33363 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage)); s+=", ";
33364 	s+=tr("MG:%1", "Max gaps (per real day)").arg(this->maxGaps);s+=", ";
33365 	s+=tr("St:%1", "Students").arg(this->students);
33366 
33367 	return begin+s+end;
33368 }
33369 
getDetailedDescription(Rules & r)33370 QString ConstraintStudentsSetMaxGapsPerRealDay::getDetailedDescription(Rules& r){
33371 	Q_UNUSED(r);
33372 
33373 	QString s=tr("Time constraint");s+="\n";
33374 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
33375 	s+=tr("A students set must respect the maximum number of gaps per real day");s+="\n";
33376 	s+=tr("(breaks and students set not available not counted)");s+="\n";
33377 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
33378 	s+=tr("Maximum gaps per real day=%1").arg(this->maxGaps);s+="\n";
33379 	s+=tr("Students=%1").arg(this->students); s+="\n";
33380 
33381 	if(!active){
33382 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
33383 		s+="\n";
33384 	}
33385 	if(!comments.isEmpty()){
33386 		s+=tr("Comments=%1").arg(comments);
33387 		s+="\n";
33388 	}
33389 
33390 	return s;
33391 }
33392 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)33393 double ConstraintStudentsSetMaxGapsPerRealDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
33394 {
33395 	//OLD COMMENT
33396 	//returns a number equal to the number of gaps of the subgroups (in hours)
33397 
33398 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
33399 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
33400 		c.teachersMatrixReady=true;
33401 		c.subgroupsMatrixReady=true;
33402 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
33403 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
33404 
33405 		c.changedForMatrixCalculation=false;
33406 	}
33407 
33408 	int nGaps;
33409 	int tmp;
33410 
33411 	int tIllegalGaps=0;
33412 
33413 	for(int sg=0; sg<this->iSubgroupsList.count(); sg++){
33414 		int i=this->iSubgroupsList.at(sg);
33415 		for(int real_d=0; real_d<r.nDaysPerWeek/2; real_d++){
33416 			nGaps=0;
33417 
33418 			tmp=0;
33419 
33420 			int double_h;
33421 
33422 			for(double_h=0; double_h<2*r.nHoursPerDay; double_h++){
33423 				int j;
33424 				if(double_h<r.nHoursPerDay)
33425 					j=2*real_d;
33426 				else
33427 					j=2*real_d+1;
33428 				int k=double_h%r.nHoursPerDay;
33429 				if(subgroupsMatrix[i][j][k]>0){
33430 					assert(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]);
33431 					break;
33432 				}
33433 			}
33434 			for(; double_h<2*r.nHoursPerDay; double_h++){
33435 				int j;
33436 				if(double_h<r.nHoursPerDay)
33437 					j=2*real_d;
33438 				else
33439 					j=2*real_d+1;
33440 				int k=double_h%r.nHoursPerDay;
33441 				if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
33442 					if(subgroupsMatrix[i][j][k]>0){
33443 						nGaps+=tmp;
33444 						tmp=0;
33445 					}
33446 					else
33447 						tmp++;
33448 				}
33449 			}
33450 
33451 			int illegalGaps=nGaps-this->maxGaps;
33452 			if(illegalGaps<0)
33453 				illegalGaps=0;
33454 
33455 			if(illegalGaps>0 && conflictsString!=nullptr){
33456 				QString s=tr("Time constraint students set max gaps per real day broken for subgroup: %1, extra gaps=%2, on real day number %3, conflicts increase=%4")
33457 				 .arg(r.internalSubgroupsList[i]->name)
33458 				 .arg(illegalGaps)
33459 				 .arg(real_d)
33460 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*illegalGaps));
33461 
33462 				dl.append(s);
33463 				cl.append(weightPercentage/100*illegalGaps);
33464 
33465 				*conflictsString+= s+"\n";
33466 			}
33467 
33468 			tIllegalGaps+=illegalGaps;
33469 		}
33470 	}
33471 
33472 	if(c.nPlacedActivities==r.nInternalActivities)
33473 		if(weightPercentage==100)     //for partial solutions it might be broken
33474 			assert(tIllegalGaps==0);
33475 	return weightPercentage/100 * tIllegalGaps;
33476 }
33477 
isRelatedToActivity(Rules & r,Activity * a)33478 bool ConstraintStudentsSetMaxGapsPerRealDay::isRelatedToActivity(Rules& r, Activity* a)
33479 {
33480 	Q_UNUSED(r);
33481 	Q_UNUSED(a);
33482 
33483 	return false;
33484 }
33485 
isRelatedToTeacher(Teacher * t)33486 bool ConstraintStudentsSetMaxGapsPerRealDay::isRelatedToTeacher(Teacher* t)
33487 {
33488 	Q_UNUSED(t);
33489 
33490 	return false;
33491 }
33492 
isRelatedToSubject(Subject * s)33493 bool ConstraintStudentsSetMaxGapsPerRealDay::isRelatedToSubject(Subject* s)
33494 {
33495 	Q_UNUSED(s);
33496 
33497 	return false;
33498 }
33499 
isRelatedToActivityTag(ActivityTag * s)33500 bool ConstraintStudentsSetMaxGapsPerRealDay::isRelatedToActivityTag(ActivityTag* s)
33501 {
33502 	Q_UNUSED(s);
33503 
33504 	return false;
33505 }
33506 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)33507 bool ConstraintStudentsSetMaxGapsPerRealDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
33508 {
33509 	return r.setsShareStudents(this->students, s->name);
33510 }
33511 
hasWrongDayOrHour(Rules & r)33512 bool ConstraintStudentsSetMaxGapsPerRealDay::hasWrongDayOrHour(Rules& r)
33513 {
33514 	if(maxGaps>2*r.nHoursPerDay)
33515 		return true;
33516 
33517 	return false;
33518 }
33519 
canRepairWrongDayOrHour(Rules & r)33520 bool ConstraintStudentsSetMaxGapsPerRealDay::canRepairWrongDayOrHour(Rules& r)
33521 {
33522 	assert(hasWrongDayOrHour(r));
33523 
33524 	return true;
33525 }
33526 
repairWrongDayOrHour(Rules & r)33527 bool ConstraintStudentsSetMaxGapsPerRealDay::repairWrongDayOrHour(Rules& r)
33528 {
33529 	assert(hasWrongDayOrHour(r));
33530 
33531 	if(maxGaps>2*r.nHoursPerDay)
33532 		maxGaps=2*r.nHoursPerDay;
33533 
33534 	return true;
33535 }
33536 
33537 ////////////////////////////////////////////////////////////////////////////////////////////
33538 ////////////////////////////////////////////////////////////////////////////////////////////
33539 
ConstraintStudentsSetMaxRealDaysPerWeek()33540 ConstraintStudentsSetMaxRealDaysPerWeek::ConstraintStudentsSetMaxRealDaysPerWeek()
33541 	: TimeConstraint()
33542 {
33543 	this->type=CONSTRAINT_STUDENTS_SET_MAX_REAL_DAYS_PER_WEEK;
33544 }
33545 
ConstraintStudentsSetMaxRealDaysPerWeek(double wp,int maxnd,QString sn)33546 ConstraintStudentsSetMaxRealDaysPerWeek::ConstraintStudentsSetMaxRealDaysPerWeek(double wp, int maxnd, QString sn)
33547 	 : TimeConstraint(wp)
33548 {
33549 	this->students = sn;
33550 	this->maxDaysPerWeek=maxnd;
33551 	this->type=CONSTRAINT_STUDENTS_SET_MAX_REAL_DAYS_PER_WEEK;
33552 }
33553 
computeInternalStructure(QWidget * parent,Rules & r)33554 bool ConstraintStudentsSetMaxRealDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
33555 {
33556 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
33557 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
33558 
33559 	if(ss==nullptr){
33560 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
33561 		 tr("Constraint students set max real days per week is wrong because it refers to inexistent students set."
33562 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
33563 
33564 		return false;
33565 	}
33566 
33567 	assert(ss!=nullptr);
33568 
33569 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
33570 	/*this->iSubgroupsList.clear();
33571 	if(ss->type==STUDENTS_SUBGROUP){
33572 		int tmp;
33573 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
33574 		assert(tmp>=0);
33575 		assert(tmp<r.nInternalSubgroups);
33576 		if(!this->iSubgroupsList.contains(tmp))
33577 			this->iSubgroupsList.append(tmp);
33578 	}
33579 	else if(ss->type==STUDENTS_GROUP){
33580 		StudentsGroup* stg=(StudentsGroup*)ss;
33581 		for(int i=0; i<stg->subgroupsList.size(); i++){
33582 			StudentsSubgroup* sts=stg->subgroupsList[i];
33583 			int tmp;
33584 			tmp=sts->indexInInternalSubgroupsList;
33585 			assert(tmp>=0);
33586 			assert(tmp<r.nInternalSubgroups);
33587 			if(!this->iSubgroupsList.contains(tmp))
33588 				this->iSubgroupsList.append(tmp);
33589 		}
33590 	}
33591 	else if(ss->type==STUDENTS_YEAR){
33592 		StudentsYear* sty=(StudentsYear*)ss;
33593 		for(int i=0; i<sty->groupsList.size(); i++){
33594 			StudentsGroup* stg=sty->groupsList[i];
33595 			for(int j=0; j<stg->subgroupsList.size(); j++){
33596 				StudentsSubgroup* sts=stg->subgroupsList[j];
33597 				int tmp;
33598 				tmp=sts->indexInInternalSubgroupsList;
33599 				assert(tmp>=0);
33600 				assert(tmp<r.nInternalSubgroups);
33601 				if(!this->iSubgroupsList.contains(tmp))
33602 					this->iSubgroupsList.append(tmp);
33603 			}
33604 		}
33605 	}
33606 	else
33607 		assert(0);*/
33608 
33609 	return true;
33610 }
33611 
hasInactiveActivities(Rules & r)33612 bool ConstraintStudentsSetMaxRealDaysPerWeek::hasInactiveActivities(Rules& r)
33613 {
33614 	Q_UNUSED(r);
33615 	return false;
33616 }
33617 
getXmlDescription(Rules & r)33618 QString ConstraintStudentsSetMaxRealDaysPerWeek::getXmlDescription(Rules& r)
33619 {
33620 	Q_UNUSED(r);
33621 
33622 	QString s="<ConstraintStudentsSetMaxRealDaysPerWeek>\n";
33623 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
33624 	s+="	<Students>"+protect(this->students)+"</Students>\n";
33625 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
33626 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
33627 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
33628 	s+="</ConstraintStudentsSetMaxRealDaysPerWeek>\n";
33629 	return s;
33630 }
33631 
getDescription(Rules & r)33632 QString ConstraintStudentsSetMaxRealDaysPerWeek::getDescription(Rules& r){
33633 	Q_UNUSED(r);
33634 
33635 	QString begin=QString("");
33636 	if(!active)
33637 		begin="X - ";
33638 
33639 	QString end=QString("");
33640 	if(!comments.isEmpty())
33641 		end=", "+tr("C: %1", "Comments").arg(comments);
33642 
33643 	QString s=tr("Students set max real days per week");s+=", ";
33644 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
33645 	s+=tr("St:%1", "Abbreviation for students (sets)").arg(this->students);s+=", ";
33646 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
33647 
33648 	return begin+s+end;
33649 }
33650 
getDetailedDescription(Rules & r)33651 QString ConstraintStudentsSetMaxRealDaysPerWeek::getDetailedDescription(Rules& r){
33652 	Q_UNUSED(r);
33653 
33654 	QString s=tr("Time constraint");s+="\n";
33655 	s+=tr("A students set must respect the maximum number of real days per week");s+="\n";
33656 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
33657 	s+=tr("Students set=%1").arg(this->students);s+="\n";
33658 
33659 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
33660 
33661 	if(!active){
33662 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
33663 		s+="\n";
33664 	}
33665 	if(!comments.isEmpty()){
33666 		s+=tr("Comments=%1").arg(comments);
33667 		s+="\n";
33668 	}
33669 
33670 	return s;
33671 }
33672 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)33673 double ConstraintStudentsSetMaxRealDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
33674 {
33675 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
33676 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
33677 		c.teachersMatrixReady=true;
33678 		c.subgroupsMatrixReady=true;
33679 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
33680 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
33681 
33682 		c.changedForMatrixCalculation=false;
33683 	}
33684 
33685 	int nbroken;
33686 
33687 	nbroken=0;
33688 
33689 	for(int sbg : qAsConst(this->iSubgroupsList)){
33690 		/*bool ocDay[MAX_DAYS_PER_WEEK];
33691 		for(int d=0; d<r.nDaysPerWeek; d++){
33692 			ocDay[d]=false;
33693 			for(int h=0; h<r.nHoursPerDay; h++){
33694 				if(subgroupsMatrix[sbg][d][h]>0){
33695 					ocDay[d]=true;
33696 				}
33697 			}
33698 		}*/
33699 		int nOcDays=0;
33700 		/*for(int d=0; d<r.nDaysPerWeek; d++)
33701 			if(ocDay[d])
33702 				nOcDays++;*/
33703 		for(int d=0; d<r.nDaysPerWeek/2; d++){
33704 			int nh=0;
33705 			for(int h=0; h<r.nHoursPerDay; h++)
33706 				nh += subgroupsMatrix[sbg][2*d][h]>=1 ? 1 : 0;
33707 			for(int h=0; h<r.nHoursPerDay; h++)
33708 				nh += subgroupsMatrix[sbg][2*d+1][h]>=1 ? 1 : 0;
33709 			if(nh>0)
33710 				nOcDays++;
33711 		}
33712 		if(nOcDays > this->maxDaysPerWeek){
33713 			nbroken+=nOcDays-this->maxDaysPerWeek;
33714 
33715 			if((nOcDays-this->maxDaysPerWeek)>0){
33716 				QString s= tr("Time constraint students set max real days per week broken for subgroup: %1, allowed %2 days, required %3 days.")
33717 				 .arg(r.internalSubgroupsList[sbg]->name)
33718 				 .arg(this->maxDaysPerWeek)
33719 				 .arg(nOcDays);
33720 				s+=" ";
33721 				s += tr("This increases the conflicts total by %1")
33722 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
33723 
33724 				dl.append(s);
33725 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
33726 
33727 				*conflictsString += s+"\n";
33728 			}
33729 		}
33730 	}
33731 
33732 	if(weightPercentage==100)
33733 		assert(nbroken==0);
33734 	return weightPercentage/100 * nbroken;
33735 }
33736 
isRelatedToActivity(Rules & r,Activity * a)33737 bool ConstraintStudentsSetMaxRealDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
33738 {
33739 	Q_UNUSED(r);
33740 	Q_UNUSED(a);
33741 
33742 	return false;
33743 }
33744 
isRelatedToTeacher(Teacher * t)33745 bool ConstraintStudentsSetMaxRealDaysPerWeek::isRelatedToTeacher(Teacher* t)
33746 {
33747 	Q_UNUSED(t);
33748 	return false;
33749 }
33750 
isRelatedToSubject(Subject * s)33751 bool ConstraintStudentsSetMaxRealDaysPerWeek::isRelatedToSubject(Subject* s)
33752 {
33753 	Q_UNUSED(s);
33754 
33755 	return false;
33756 }
33757 
isRelatedToActivityTag(ActivityTag * s)33758 bool ConstraintStudentsSetMaxRealDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
33759 {
33760 	Q_UNUSED(s);
33761 
33762 	return false;
33763 }
33764 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)33765 bool ConstraintStudentsSetMaxRealDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
33766 {
33767 	return r.setsShareStudents(this->students, s->name);
33768 }
33769 
hasWrongDayOrHour(Rules & r)33770 bool ConstraintStudentsSetMaxRealDaysPerWeek::hasWrongDayOrHour(Rules& r)
33771 {
33772 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
33773 		return true;
33774 
33775 	return false;
33776 }
33777 
canRepairWrongDayOrHour(Rules & r)33778 bool ConstraintStudentsSetMaxRealDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
33779 {
33780 	assert(hasWrongDayOrHour(r));
33781 
33782 	return true;
33783 }
33784 
repairWrongDayOrHour(Rules & r)33785 bool ConstraintStudentsSetMaxRealDaysPerWeek::repairWrongDayOrHour(Rules& r)
33786 {
33787 	assert(hasWrongDayOrHour(r));
33788 
33789 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
33790 		this->maxDaysPerWeek=r.nDaysPerWeek/2;
33791 
33792 	return true;
33793 }
33794 
33795 ///////////////////////////////////////////////////////////////////////////////////////////
33796 ///////////////////////////////////////////////////////////////////////////////////////////
33797 
ConstraintStudentsMaxRealDaysPerWeek()33798 ConstraintStudentsMaxRealDaysPerWeek::ConstraintStudentsMaxRealDaysPerWeek()
33799 	: TimeConstraint()
33800 {
33801 	this->type=CONSTRAINT_STUDENTS_MAX_REAL_DAYS_PER_WEEK;
33802 }
33803 
ConstraintStudentsMaxRealDaysPerWeek(double wp,int maxnd)33804 ConstraintStudentsMaxRealDaysPerWeek::ConstraintStudentsMaxRealDaysPerWeek(double wp, int maxnd)
33805 	 : TimeConstraint(wp)
33806 {
33807 	this->maxDaysPerWeek=maxnd;
33808 	this->type=CONSTRAINT_STUDENTS_MAX_REAL_DAYS_PER_WEEK;
33809 }
33810 
computeInternalStructure(QWidget * parent,Rules & r)33811 bool ConstraintStudentsMaxRealDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
33812 {
33813 	Q_UNUSED(parent);
33814 	Q_UNUSED(r);
33815 
33816 	return true;
33817 }
33818 
hasInactiveActivities(Rules & r)33819 bool ConstraintStudentsMaxRealDaysPerWeek::hasInactiveActivities(Rules& r)
33820 {
33821 	Q_UNUSED(r);
33822 	return false;
33823 }
33824 
getXmlDescription(Rules & r)33825 QString ConstraintStudentsMaxRealDaysPerWeek::getXmlDescription(Rules& r)
33826 {
33827 	Q_UNUSED(r);
33828 
33829 	QString s="<ConstraintStudentsMaxRealDaysPerWeek>\n";
33830 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
33831 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
33832 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
33833 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
33834 	s+="</ConstraintStudentsMaxRealDaysPerWeek>\n";
33835 	return s;
33836 }
33837 
getDescription(Rules & r)33838 QString ConstraintStudentsMaxRealDaysPerWeek::getDescription(Rules& r){
33839 	Q_UNUSED(r);
33840 
33841 	QString begin=QString("");
33842 	if(!active)
33843 		begin="X - ";
33844 
33845 	QString end=QString("");
33846 	if(!comments.isEmpty())
33847 		end=", "+tr("C: %1", "Comments").arg(comments);
33848 
33849 	QString s=tr("Students max real days per week");s+=", ";
33850 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
33851 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
33852 
33853 	return begin+s+end;
33854 }
33855 
getDetailedDescription(Rules & r)33856 QString ConstraintStudentsMaxRealDaysPerWeek::getDetailedDescription(Rules& r){
33857 	Q_UNUSED(r);
33858 
33859 	QString s=tr("Time constraint");s+="\n";
33860 	s+=tr("All students must respect the maximum number of real days per week");s+="\n";
33861 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
33862 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
33863 
33864 	if(!active){
33865 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
33866 		s+="\n";
33867 	}
33868 	if(!comments.isEmpty()){
33869 		s+=tr("Comments=%1").arg(comments);
33870 		s+="\n";
33871 	}
33872 
33873 	return s;
33874 }
33875 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)33876 double ConstraintStudentsMaxRealDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
33877 {
33878 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
33879 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
33880 		c.teachersMatrixReady=true;
33881 		c.subgroupsMatrixReady=true;
33882 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
33883 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
33884 
33885 		c.changedForMatrixCalculation=false;
33886 	}
33887 
33888 	int nbroken;
33889 
33890 	nbroken=0;
33891 
33892 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
33893 		/*bool ocDay[MAX_DAYS_PER_WEEK];
33894 		for(int d=0; d<r.nDaysPerWeek; d++){
33895 			ocDay[d]=false;
33896 			for(int h=0; h<r.nHoursPerDay; h++){
33897 				if(subgroupsMatrix[sbg][d][h]>0){
33898 					ocDay[d]=true;
33899 				}
33900 			}
33901 		}*/
33902 		int nOcDays=0;
33903 		/*for(int d=0; d<r.nDaysPerWeek; d++)
33904 			if(ocDay[d])
33905 				nOcDays++;*/
33906 		for(int d=0; d<r.nDaysPerWeek/2; d++){
33907 			int nh=0;
33908 			for(int h=0; h<r.nHoursPerDay; h++)
33909 				nh += subgroupsMatrix[sbg][2*d][h]>=1 ? 1 : 0;
33910 			for(int h=0; h<r.nHoursPerDay; h++)
33911 				nh += subgroupsMatrix[sbg][2*d+1][h]>=1 ? 1 : 0;
33912 			if(nh>0)
33913 				nOcDays++;
33914 		}
33915 		if(nOcDays > this->maxDaysPerWeek){
33916 			nbroken+=nOcDays-this->maxDaysPerWeek;
33917 
33918 			if((nOcDays-this->maxDaysPerWeek)>0){
33919 				QString s= tr("Time constraint students max real days per week broken for subgroup: %1, allowed %2 real days, required %3 real days.")
33920 				 .arg(r.internalSubgroupsList[sbg]->name)
33921 				 .arg(this->maxDaysPerWeek)
33922 				 .arg(nOcDays);
33923 				s+=" ";
33924 				s += tr("This increases the conflicts total by %1")
33925 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
33926 
33927 				dl.append(s);
33928 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
33929 
33930 				*conflictsString += s+"\n";
33931 			}
33932 		}
33933 	}
33934 
33935 	if(weightPercentage==100)
33936 		assert(nbroken==0);
33937 	return weightPercentage/100 * nbroken;
33938 }
33939 
isRelatedToActivity(Rules & r,Activity * a)33940 bool ConstraintStudentsMaxRealDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
33941 {
33942 	Q_UNUSED(r);
33943 	Q_UNUSED(a);
33944 
33945 	return false;
33946 }
33947 
isRelatedToTeacher(Teacher * t)33948 bool ConstraintStudentsMaxRealDaysPerWeek::isRelatedToTeacher(Teacher* t)
33949 {
33950 	Q_UNUSED(t);
33951 	return false;
33952 }
33953 
isRelatedToSubject(Subject * s)33954 bool ConstraintStudentsMaxRealDaysPerWeek::isRelatedToSubject(Subject* s)
33955 {
33956 	Q_UNUSED(s);
33957 
33958 	return false;
33959 }
33960 
isRelatedToActivityTag(ActivityTag * s)33961 bool ConstraintStudentsMaxRealDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
33962 {
33963 	Q_UNUSED(s);
33964 
33965 	return false;
33966 }
33967 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)33968 bool ConstraintStudentsMaxRealDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
33969 {
33970 	Q_UNUSED(r);
33971 	Q_UNUSED(s);
33972 	return true;
33973 }
33974 
hasWrongDayOrHour(Rules & r)33975 bool ConstraintStudentsMaxRealDaysPerWeek::hasWrongDayOrHour(Rules& r)
33976 {
33977 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
33978 		return true;
33979 
33980 	return false;
33981 }
33982 
canRepairWrongDayOrHour(Rules & r)33983 bool ConstraintStudentsMaxRealDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
33984 {
33985 	assert(hasWrongDayOrHour(r));
33986 
33987 	return true;
33988 }
33989 
repairWrongDayOrHour(Rules & r)33990 bool ConstraintStudentsMaxRealDaysPerWeek::repairWrongDayOrHour(Rules& r)
33991 {
33992 	assert(hasWrongDayOrHour(r));
33993 
33994 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
33995 		this->maxDaysPerWeek=r.nDaysPerWeek/2;
33996 
33997 	return true;
33998 }
33999 
34000 ///////////////////////////////////////////////////////////////////////////////////////////
34001 ///////////////////////////////////////////////////////////////////////////////////////////
34002 
ConstraintTeacherMaxSpanPerRealDay()34003 ConstraintTeacherMaxSpanPerRealDay::ConstraintTeacherMaxSpanPerRealDay()
34004 	: TimeConstraint()
34005 {
34006 	this->type=CONSTRAINT_TEACHER_MAX_SPAN_PER_REAL_DAY;
34007 	this->maxSpanPerDay = -1;
34008 	allowOneDayExceptionPlusOne=false;
34009 }
34010 
ConstraintTeacherMaxSpanPerRealDay(double wp,int maxspan,bool except,const QString & teacher)34011 ConstraintTeacherMaxSpanPerRealDay::ConstraintTeacherMaxSpanPerRealDay(double wp, int maxspan, bool except, const QString& teacher)
34012  : TimeConstraint(wp)
34013  {
34014 	assert(maxspan>0);
34015 	this->maxSpanPerDay=maxspan;
34016 	this->teacherName=teacher;
34017 
34018 	allowOneDayExceptionPlusOne=except;
34019 
34020 	this->type=CONSTRAINT_TEACHER_MAX_SPAN_PER_REAL_DAY;
34021 }
34022 
computeInternalStructure(QWidget * parent,Rules & r)34023 bool ConstraintTeacherMaxSpanPerRealDay::computeInternalStructure(QWidget* parent, Rules& r)
34024 {
34025 	Q_UNUSED(parent);
34026 
34027 	//this->teacher_ID=r.searchTeacher(this->teacherName);
34028 	teacher_ID=r.teachersHash.value(teacherName, -1);
34029 	assert(this->teacher_ID>=0);
34030 	return true;
34031 }
34032 
hasInactiveActivities(Rules & r)34033 bool ConstraintTeacherMaxSpanPerRealDay::hasInactiveActivities(Rules& r)
34034 {
34035 	Q_UNUSED(r);
34036 	return false;
34037 }
34038 
getXmlDescription(Rules & r)34039 QString ConstraintTeacherMaxSpanPerRealDay::getXmlDescription(Rules& r){
34040 	Q_UNUSED(r);
34041 
34042 	QString s="<ConstraintTeacherMaxSpanPerRealDay>\n";
34043 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
34044 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
34045 	s+="	<Max_Span>"+CustomFETString::number(this->maxSpanPerDay)+"</Max_Span>\n";
34046 	s+="	<Allow_One_Day_Exception_of_Plus_One>"+trueFalse(allowOneDayExceptionPlusOne)+"</Allow_One_Day_Exception_of_Plus_One>\n";
34047 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
34048 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
34049 	s+="</ConstraintTeacherMaxSpanPerRealDay>\n";
34050 	return s;
34051 }
34052 
getDescription(Rules & r)34053 QString ConstraintTeacherMaxSpanPerRealDay::getDescription(Rules& r){
34054 	Q_UNUSED(r);
34055 
34056 	QString begin=QString("");
34057 	if(!active)
34058 		begin="X - ";
34059 
34060 	QString end=QString("");
34061 	if(!comments.isEmpty())
34062 		end=", "+tr("C: %1", "Comments").arg(comments);
34063 
34064 	QString s;
34065 	s+=tr("Teacher max span per real day");s+=", ";
34066 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
34067 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
34068 	s+=tr("MS:%1", "Maximum span (in hours, per real day)").arg(this->maxSpanPerDay);s+=", ";
34069 	s+=tr("ODE:%1", "One day exception (in which the teacher can have span+1)").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));
34070 
34071 	return begin+s+end;
34072 }
34073 
getDetailedDescription(Rules & r)34074 QString ConstraintTeacherMaxSpanPerRealDay::getDetailedDescription(Rules& r){
34075 	Q_UNUSED(r);
34076 
34077 	QString s=tr("Time constraint");s+="\n";
34078 	s+=tr("A teacher must respect the maximum number of span (in hours) per real day");s+="\n";
34079 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
34080 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
34081 	s+=tr("Maximum span per day=%1").arg(this->maxSpanPerDay);s+="\n";
34082 	s+=tr("Allow one day exception of plus one=%1").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));s+="\n";
34083 
34084 	if(!active){
34085 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
34086 		s+="\n";
34087 	}
34088 	if(!comments.isEmpty()){
34089 		s+=tr("Comments=%1").arg(comments);
34090 		s+="\n";
34091 	}
34092 
34093 	return s;
34094 }
34095 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)34096 double ConstraintTeacherMaxSpanPerRealDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
34097 {
34098 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
34099 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
34100 		c.teachersMatrixReady=true;
34101 		c.subgroupsMatrixReady=true;
34102 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
34103 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
34104 
34105 		c.changedForMatrixCalculation=false;
34106 	}
34107 
34108 	Q_UNUSED(cl);
34109 	Q_UNUSED(dl);
34110 	Q_UNUSED(conflictsString);
34111 
34112 	assert(this->weightPercentage==100.0);
34113 
34114 	int nbroken=0;
34115 
34116 	bool except;
34117 	if(allowOneDayExceptionPlusOne)
34118 		except=true;
34119 	else
34120 		except=false;
34121 
34122 	for(int d=0; d<r.nDaysPerWeek/2; d++){
34123 		int begin=-1;
34124 		int end=-1;
34125 		for(int h=0; h<2*r.nHoursPerDay; h++){
34126 			int d3=d*2+(h<r.nHoursPerDay?0:1);
34127 			int h3=h%r.nHoursPerDay;
34128 			if(teachersMatrix[this->teacher_ID][d3][h3]>0){
34129 				begin=h;
34130 				break;
34131 			}
34132 		}
34133 		for(int h=2*r.nHoursPerDay-1; h>=0; h--){
34134 			int d3=d*2+(h<r.nHoursPerDay?0:1);
34135 			int h3=h%r.nHoursPerDay;
34136 			if(teachersMatrix[this->teacher_ID][d3][h3]>0){
34137 				end=h;
34138 				break;
34139 			}
34140 		}
34141 		if(end>=0 && begin>=0 && end>=begin){
34142 			int span=end-begin+1;
34143 			if(span>this->maxSpanPerDay){
34144 				if(except && span==maxSpanPerDay+1)
34145 					except=false;
34146 				else
34147 					nbroken++;
34148 			}
34149 		}
34150 	}
34151 
34152 	assert(nbroken==0);
34153 
34154 	return nbroken;
34155 }
34156 
isRelatedToActivity(Rules & r,Activity * a)34157 bool ConstraintTeacherMaxSpanPerRealDay::isRelatedToActivity(Rules& r, Activity* a)
34158 {
34159 	Q_UNUSED(r);
34160 	Q_UNUSED(a);
34161 
34162 	return false;
34163 }
34164 
isRelatedToTeacher(Teacher * t)34165 bool ConstraintTeacherMaxSpanPerRealDay::isRelatedToTeacher(Teacher* t)
34166 {
34167 	if(this->teacherName==t->name)
34168 		return true;
34169 	return false;
34170 }
34171 
isRelatedToSubject(Subject * s)34172 bool ConstraintTeacherMaxSpanPerRealDay::isRelatedToSubject(Subject* s)
34173 {
34174 	Q_UNUSED(s);
34175 
34176 	return false;
34177 }
34178 
isRelatedToActivityTag(ActivityTag * s)34179 bool ConstraintTeacherMaxSpanPerRealDay::isRelatedToActivityTag(ActivityTag* s)
34180 {
34181 	Q_UNUSED(s);
34182 
34183 	return false;
34184 }
34185 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)34186 bool ConstraintTeacherMaxSpanPerRealDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
34187 {
34188 	Q_UNUSED(r);
34189 	Q_UNUSED(s);
34190 
34191 	return false;
34192 }
34193 
hasWrongDayOrHour(Rules & r)34194 bool ConstraintTeacherMaxSpanPerRealDay::hasWrongDayOrHour(Rules& r)
34195 {
34196 	if(maxSpanPerDay>r.nHoursPerDay)
34197 		return true;
34198 
34199 	return false;
34200 }
34201 
canRepairWrongDayOrHour(Rules & r)34202 bool ConstraintTeacherMaxSpanPerRealDay::canRepairWrongDayOrHour(Rules& r)
34203 {
34204 	assert(hasWrongDayOrHour(r));
34205 
34206 	return true;
34207 }
34208 
repairWrongDayOrHour(Rules & r)34209 bool ConstraintTeacherMaxSpanPerRealDay::repairWrongDayOrHour(Rules& r)
34210 {
34211 	assert(hasWrongDayOrHour(r));
34212 
34213 	if(maxSpanPerDay>r.nHoursPerDay)
34214 		maxSpanPerDay=r.nHoursPerDay;
34215 
34216 	return true;
34217 }
34218 
34219 ///////////////////////////////////////////////////////////////////////////////////////////
34220 ///////////////////////////////////////////////////////////////////////////////////////////
34221 
ConstraintTeachersMaxSpanPerRealDay()34222 ConstraintTeachersMaxSpanPerRealDay::ConstraintTeachersMaxSpanPerRealDay()
34223 	: TimeConstraint()
34224 {
34225 	this->type=CONSTRAINT_TEACHERS_MAX_SPAN_PER_REAL_DAY;
34226 	this->maxSpanPerDay = -1;
34227 	allowOneDayExceptionPlusOne=false;
34228 }
34229 
ConstraintTeachersMaxSpanPerRealDay(double wp,int maxspan,bool except)34230 ConstraintTeachersMaxSpanPerRealDay::ConstraintTeachersMaxSpanPerRealDay(double wp, int maxspan, bool except)
34231  : TimeConstraint(wp)
34232  {
34233 	assert(maxspan>0);
34234 	this->maxSpanPerDay=maxspan;
34235 
34236 	allowOneDayExceptionPlusOne=except;
34237 
34238 	this->type=CONSTRAINT_TEACHERS_MAX_SPAN_PER_REAL_DAY;
34239 }
34240 
computeInternalStructure(QWidget * parent,Rules & r)34241 bool ConstraintTeachersMaxSpanPerRealDay::computeInternalStructure(QWidget* parent, Rules& r)
34242 {
34243 	Q_UNUSED(parent);
34244 	Q_UNUSED(r);
34245 
34246 	return true;
34247 }
34248 
hasInactiveActivities(Rules & r)34249 bool ConstraintTeachersMaxSpanPerRealDay::hasInactiveActivities(Rules& r)
34250 {
34251 	Q_UNUSED(r);
34252 	return false;
34253 }
34254 
getXmlDescription(Rules & r)34255 QString ConstraintTeachersMaxSpanPerRealDay::getXmlDescription(Rules& r){
34256 	Q_UNUSED(r);
34257 
34258 	QString s="<ConstraintTeachersMaxSpanPerRealDay>\n";
34259 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
34260 	s+="	<Max_Span>"+CustomFETString::number(this->maxSpanPerDay)+"</Max_Span>\n";
34261 	s+="	<Allow_One_Day_Exception_of_Plus_One>"+trueFalse(allowOneDayExceptionPlusOne)+"</Allow_One_Day_Exception_of_Plus_One>\n";
34262 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
34263 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
34264 	s+="</ConstraintTeachersMaxSpanPerRealDay>\n";
34265 	return s;
34266 }
34267 
getDescription(Rules & r)34268 QString ConstraintTeachersMaxSpanPerRealDay::getDescription(Rules& r){
34269 	Q_UNUSED(r);
34270 
34271 	QString begin=QString("");
34272 	if(!active)
34273 		begin="X - ";
34274 
34275 	QString end=QString("");
34276 	if(!comments.isEmpty())
34277 		end=", "+tr("C: %1", "Comments").arg(comments);
34278 
34279 	QString s;
34280 	s+=tr("Teachers max span per real day");s+=", ";
34281 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
34282 	s+=tr("MS:%1", "Maximum span (in hours, per real day)").arg(this->maxSpanPerDay);s+=", ";
34283 	s+=tr("ODE:%1", "One day exception (in which the teachers can have span+1)").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));
34284 
34285 	return begin+s+end;
34286 }
34287 
getDetailedDescription(Rules & r)34288 QString ConstraintTeachersMaxSpanPerRealDay::getDetailedDescription(Rules& r){
34289 	Q_UNUSED(r);
34290 
34291 	QString s=tr("Time constraint");s+="\n";
34292 	s+=tr("All teachers must respect the maximum number of span (in hours) per real day");s+="\n";
34293 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
34294 	s+=tr("Maximum span per day=%1").arg(this->maxSpanPerDay);s+="\n";
34295 	s+=tr("Allow one day exception of plus one=%1").arg(yesNoTranslated(this->allowOneDayExceptionPlusOne));s+="\n";
34296 
34297 	if(!active){
34298 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
34299 		s+="\n";
34300 	}
34301 	if(!comments.isEmpty()){
34302 		s+=tr("Comments=%1").arg(comments);
34303 		s+="\n";
34304 	}
34305 
34306 	return s;
34307 }
34308 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)34309 double ConstraintTeachersMaxSpanPerRealDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
34310 {
34311 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
34312 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
34313 		c.teachersMatrixReady=true;
34314 		c.subgroupsMatrixReady=true;
34315 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
34316 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
34317 
34318 		c.changedForMatrixCalculation=false;
34319 	}
34320 
34321 	Q_UNUSED(cl);
34322 	Q_UNUSED(dl);
34323 	Q_UNUSED(conflictsString);
34324 
34325 	assert(this->weightPercentage==100.0);
34326 
34327 	int nbroken=0;
34328 
34329 	for(int tch=0; tch<r.nInternalTeachers; tch++){
34330 		bool except;
34331 		if(allowOneDayExceptionPlusOne)
34332 			except=true;
34333 		else
34334 			except=false;
34335 
34336 		for(int d=0; d<r.nDaysPerWeek/2; d++){
34337 			int begin=-1;
34338 			int end=-1;
34339 			for(int h=0; h<2*r.nHoursPerDay; h++){
34340 				int d3=d*2+(h<r.nHoursPerDay?0:1);
34341 				int h3=h%r.nHoursPerDay;
34342 				if(teachersMatrix[tch][d3][h3]>0){
34343 					begin=h;
34344 					break;
34345 				}
34346 			}
34347 			for(int h=2*r.nHoursPerDay-1; h>=0; h--){
34348 				int d3=d*2+(h<r.nHoursPerDay?0:1);
34349 				int h3=h%r.nHoursPerDay;
34350 				if(teachersMatrix[tch][d3][h3]>0){
34351 					end=h;
34352 					break;
34353 				}
34354 			}
34355 			if(end>=0 && begin>=0 && end>=begin){
34356 				int span=end-begin+1;
34357 				if(span>this->maxSpanPerDay){
34358 					if(except && span==maxSpanPerDay+1)
34359 						except=false;
34360 					else
34361 						nbroken++;
34362 				}
34363 			}
34364 		}
34365 	}
34366 
34367 	assert(nbroken==0);
34368 
34369 	return nbroken;
34370 }
34371 
isRelatedToActivity(Rules & r,Activity * a)34372 bool ConstraintTeachersMaxSpanPerRealDay::isRelatedToActivity(Rules& r, Activity* a)
34373 {
34374 	Q_UNUSED(r);
34375 	Q_UNUSED(a);
34376 
34377 	return false;
34378 }
34379 
isRelatedToTeacher(Teacher * t)34380 bool ConstraintTeachersMaxSpanPerRealDay::isRelatedToTeacher(Teacher* t)
34381 {
34382 	Q_UNUSED(t);
34383 
34384 	return true;
34385 }
34386 
isRelatedToSubject(Subject * s)34387 bool ConstraintTeachersMaxSpanPerRealDay::isRelatedToSubject(Subject* s)
34388 {
34389 	Q_UNUSED(s);
34390 
34391 	return false;
34392 }
34393 
isRelatedToActivityTag(ActivityTag * s)34394 bool ConstraintTeachersMaxSpanPerRealDay::isRelatedToActivityTag(ActivityTag* s)
34395 {
34396 	Q_UNUSED(s);
34397 
34398 	return false;
34399 }
34400 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)34401 bool ConstraintTeachersMaxSpanPerRealDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
34402 {
34403 	Q_UNUSED(r);
34404 	Q_UNUSED(s);
34405 
34406 	return false;
34407 }
34408 
hasWrongDayOrHour(Rules & r)34409 bool ConstraintTeachersMaxSpanPerRealDay::hasWrongDayOrHour(Rules& r)
34410 {
34411 	if(maxSpanPerDay>r.nHoursPerDay)
34412 		return true;
34413 
34414 	return false;
34415 }
34416 
canRepairWrongDayOrHour(Rules & r)34417 bool ConstraintTeachersMaxSpanPerRealDay::canRepairWrongDayOrHour(Rules& r)
34418 {
34419 	assert(hasWrongDayOrHour(r));
34420 
34421 	return true;
34422 }
34423 
repairWrongDayOrHour(Rules & r)34424 bool ConstraintTeachersMaxSpanPerRealDay::repairWrongDayOrHour(Rules& r)
34425 {
34426 	assert(hasWrongDayOrHour(r));
34427 
34428 	if(maxSpanPerDay>r.nHoursPerDay)
34429 		maxSpanPerDay=r.nHoursPerDay;
34430 
34431 	return true;
34432 }
34433 
34434 ////////////////////////////////////////////////////////////////////////////////////////////
34435 ////////////////////////////////////////////////////////////////////////////////////////////
34436 
ConstraintStudentsSetMaxSpanPerRealDay()34437 ConstraintStudentsSetMaxSpanPerRealDay::ConstraintStudentsSetMaxSpanPerRealDay()
34438 	: TimeConstraint()
34439 {
34440 	this->type = CONSTRAINT_STUDENTS_SET_MAX_SPAN_PER_REAL_DAY;
34441 	this->maxSpanPerDay = -1;
34442 }
34443 
ConstraintStudentsSetMaxSpanPerRealDay(double wp,int maxspan,const QString & sn)34444 ConstraintStudentsSetMaxSpanPerRealDay::ConstraintStudentsSetMaxSpanPerRealDay(double wp, int maxspan, const QString& sn)
34445 	: TimeConstraint(wp)
34446 {
34447 	this->maxSpanPerDay = maxspan;
34448 	this->students = sn;
34449 	this->type = CONSTRAINT_STUDENTS_SET_MAX_SPAN_PER_REAL_DAY;
34450 }
34451 
hasInactiveActivities(Rules & r)34452 bool ConstraintStudentsSetMaxSpanPerRealDay::hasInactiveActivities(Rules& r)
34453 {
34454 	Q_UNUSED(r);
34455 	return false;
34456 }
34457 
getXmlDescription(Rules & r)34458 QString ConstraintStudentsSetMaxSpanPerRealDay::getXmlDescription(Rules& r)
34459 {
34460 	Q_UNUSED(r);
34461 
34462 	QString s="<ConstraintStudentsSetMaxSpanPerRealDay>\n";
34463 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
34464 	s+="	<Max_Span>"+CustomFETString::number(this->maxSpanPerDay)+"</Max_Span>\n";
34465 	s+="	<Students>"+protect(this->students)+"</Students>\n";
34466 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
34467 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
34468 	s+="</ConstraintStudentsSetMaxSpanPerRealDay>\n";
34469 	return s;
34470 }
34471 
getDescription(Rules & r)34472 QString ConstraintStudentsSetMaxSpanPerRealDay::getDescription(Rules& r)
34473 {
34474 	Q_UNUSED(r);
34475 
34476 	QString begin=QString("");
34477 	if(!active)
34478 		begin="X - ";
34479 
34480 	QString end=QString("");
34481 	if(!comments.isEmpty())
34482 		end=", "+tr("C: %1", "Comments").arg(comments);
34483 
34484 	QString s;
34485 	s+=tr("Students set max span per real day");s+=", ";
34486 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
34487 	s+=tr("St:%1", "Students (set)").arg(this->students); s+=", ";
34488 	s+=tr("MS:%1", "Max span (in hours, per real day)").arg(this->maxSpanPerDay);
34489 
34490 	return begin+s+end;
34491 }
34492 
getDetailedDescription(Rules & r)34493 QString ConstraintStudentsSetMaxSpanPerRealDay::getDetailedDescription(Rules& r)
34494 {
34495 	Q_UNUSED(r);
34496 
34497 	QString s=tr("Time constraint");s+="\n";
34498 	s+=tr("A students set must respect the maximum number of span (in hours) per real day");s+="\n";
34499 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
34500 	s+=tr("Students set=%1").arg(this->students);s+="\n";
34501 	s+=tr("Maximum span per day=%1").arg(this->maxSpanPerDay);s+="\n";
34502 
34503 	if(!active){
34504 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
34505 		s+="\n";
34506 	}
34507 	if(!comments.isEmpty()){
34508 		s+=tr("Comments=%1").arg(comments);
34509 		s+="\n";
34510 	}
34511 
34512 	return s;
34513 }
34514 
computeInternalStructure(QWidget * parent,Rules & r)34515 bool ConstraintStudentsSetMaxSpanPerRealDay::computeInternalStructure(QWidget* parent, Rules& r)
34516 {
34517 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
34518 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
34519 
34520 	if(ss==nullptr){
34521 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
34522 		 tr("Constraint students set max span per real day is wrong because it refers to inexistent students set."
34523 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
34524 
34525 		return false;
34526 	}
34527 
34528 	assert(ss!=nullptr);
34529 
34530 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
34531 	/*this->iSubgroupsList.clear();
34532 	if(ss->type==STUDENTS_SUBGROUP){
34533 		int tmp;
34534 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
34535 		assert(tmp>=0);
34536 		assert(tmp<r.nInternalSubgroups);
34537 		if(!this->iSubgroupsList.contains(tmp))
34538 			this->iSubgroupsList.append(tmp);
34539 	}
34540 	else if(ss->type==STUDENTS_GROUP){
34541 		StudentsGroup* stg=(StudentsGroup*)ss;
34542 		for(int i=0; i<stg->subgroupsList.size(); i++){
34543 			StudentsSubgroup* sts=stg->subgroupsList[i];
34544 			int tmp;
34545 			tmp=sts->indexInInternalSubgroupsList;
34546 			assert(tmp>=0);
34547 			assert(tmp<r.nInternalSubgroups);
34548 			if(!this->iSubgroupsList.contains(tmp))
34549 				this->iSubgroupsList.append(tmp);
34550 		}
34551 	}
34552 	else if(ss->type==STUDENTS_YEAR){
34553 		StudentsYear* sty=(StudentsYear*)ss;
34554 		for(int i=0; i<sty->groupsList.size(); i++){
34555 			StudentsGroup* stg=sty->groupsList[i];
34556 			for(int j=0; j<stg->subgroupsList.size(); j++){
34557 				StudentsSubgroup* sts=stg->subgroupsList[j];
34558 				int tmp;
34559 				tmp=sts->indexInInternalSubgroupsList;
34560 				assert(tmp>=0);
34561 				assert(tmp<r.nInternalSubgroups);
34562 				if(!this->iSubgroupsList.contains(tmp))
34563 					this->iSubgroupsList.append(tmp);
34564 			}
34565 		}
34566 	}
34567 	else
34568 		assert(0);*/
34569 
34570 	return true;
34571 }
34572 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)34573 double ConstraintStudentsSetMaxSpanPerRealDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
34574 {
34575 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
34576 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
34577 		c.teachersMatrixReady=true;
34578 		c.subgroupsMatrixReady=true;
34579 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
34580 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
34581 
34582 		c.changedForMatrixCalculation=false;
34583 	}
34584 
34585 	Q_UNUSED(cl);
34586 	Q_UNUSED(dl);
34587 	Q_UNUSED(conflictsString);
34588 
34589 	assert(this->weightPercentage==100.0);
34590 
34591 	int nbroken=0;
34592 
34593 	for(int sbg : qAsConst(this->iSubgroupsList)){
34594 		for(int d=0; d<r.nDaysPerWeek/2; d++){
34595 			int begin=-1;
34596 			int end=-1;
34597 			for(int h=0; h<2*r.nHoursPerDay; h++){
34598 				int d3=d*2+(h<r.nHoursPerDay?0:1);
34599 				int h3=h%r.nHoursPerDay;
34600 				if(subgroupsMatrix[sbg][d3][h3]>0){
34601 					begin=h;
34602 					break;
34603 				}
34604 			}
34605 			for(int h=2*r.nHoursPerDay-1; h>=0; h--){
34606 				int d3=d*2+(h<r.nHoursPerDay?0:1);
34607 				int h3=h%r.nHoursPerDay;
34608 				if(subgroupsMatrix[sbg][d3][h3]>0){
34609 					end=h;
34610 					break;
34611 				}
34612 			}
34613 			if(end>=0 && begin>=0 && end>=begin){
34614 				int span=end-begin+1;
34615 				if(span>this->maxSpanPerDay)
34616 					nbroken++;
34617 			}
34618 		}
34619 	}
34620 
34621 	assert(nbroken==0);
34622 
34623 	return nbroken;
34624 }
34625 
isRelatedToActivity(Rules & r,Activity * a)34626 bool ConstraintStudentsSetMaxSpanPerRealDay::isRelatedToActivity(Rules& r, Activity* a)
34627 {
34628 	Q_UNUSED(r);
34629 	Q_UNUSED(a);
34630 
34631 	return false;
34632 }
34633 
isRelatedToTeacher(Teacher * t)34634 bool ConstraintStudentsSetMaxSpanPerRealDay::isRelatedToTeacher(Teacher* t)
34635 {
34636 	Q_UNUSED(t);
34637 
34638 	return false;
34639 }
34640 
isRelatedToSubject(Subject * s)34641 bool ConstraintStudentsSetMaxSpanPerRealDay::isRelatedToSubject(Subject* s)
34642 {
34643 	Q_UNUSED(s);
34644 
34645 	return false;
34646 }
34647 
isRelatedToActivityTag(ActivityTag * s)34648 bool ConstraintStudentsSetMaxSpanPerRealDay::isRelatedToActivityTag(ActivityTag* s)
34649 {
34650 	Q_UNUSED(s);
34651 
34652 	return false;
34653 }
34654 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)34655 bool ConstraintStudentsSetMaxSpanPerRealDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
34656 {
34657 	return r.setsShareStudents(this->students, s->name);
34658 }
34659 
hasWrongDayOrHour(Rules & r)34660 bool ConstraintStudentsSetMaxSpanPerRealDay::hasWrongDayOrHour(Rules& r)
34661 {
34662 	if(maxSpanPerDay>r.nHoursPerDay)
34663 		return true;
34664 
34665 	return false;
34666 }
34667 
canRepairWrongDayOrHour(Rules & r)34668 bool ConstraintStudentsSetMaxSpanPerRealDay::canRepairWrongDayOrHour(Rules& r)
34669 {
34670 	assert(hasWrongDayOrHour(r));
34671 
34672 	return true;
34673 }
34674 
repairWrongDayOrHour(Rules & r)34675 bool ConstraintStudentsSetMaxSpanPerRealDay::repairWrongDayOrHour(Rules& r)
34676 {
34677 	assert(hasWrongDayOrHour(r));
34678 
34679 	if(maxSpanPerDay>r.nHoursPerDay)
34680 		maxSpanPerDay=r.nHoursPerDay;
34681 
34682 	return true;
34683 }
34684 
34685 ////////////////////////////////////////////////////////////////////////////////////////////
34686 ////////////////////////////////////////////////////////////////////////////////////////////
34687 
ConstraintStudentsMaxSpanPerRealDay()34688 ConstraintStudentsMaxSpanPerRealDay::ConstraintStudentsMaxSpanPerRealDay()
34689 	: TimeConstraint()
34690 {
34691 	this->type = CONSTRAINT_STUDENTS_MAX_SPAN_PER_REAL_DAY;
34692 	this->maxSpanPerDay = -1;
34693 }
34694 
ConstraintStudentsMaxSpanPerRealDay(double wp,int maxspan)34695 ConstraintStudentsMaxSpanPerRealDay::ConstraintStudentsMaxSpanPerRealDay(double wp, int maxspan)
34696 	: TimeConstraint(wp)
34697 {
34698 	this->maxSpanPerDay = maxspan;
34699 	this->type = CONSTRAINT_STUDENTS_MAX_SPAN_PER_REAL_DAY;
34700 }
34701 
hasInactiveActivities(Rules & r)34702 bool ConstraintStudentsMaxSpanPerRealDay::hasInactiveActivities(Rules& r)
34703 {
34704 	Q_UNUSED(r);
34705 	return false;
34706 }
34707 
getXmlDescription(Rules & r)34708 QString ConstraintStudentsMaxSpanPerRealDay::getXmlDescription(Rules& r)
34709 {
34710 	Q_UNUSED(r);
34711 
34712 	QString s="<ConstraintStudentsMaxSpanPerRealDay>\n";
34713 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
34714 	s+="	<Max_Span>"+CustomFETString::number(this->maxSpanPerDay)+"</Max_Span>\n";
34715 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
34716 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
34717 	s+="</ConstraintStudentsMaxSpanPerRealDay>\n";
34718 	return s;
34719 }
34720 
getDescription(Rules & r)34721 QString ConstraintStudentsMaxSpanPerRealDay::getDescription(Rules& r)
34722 {
34723 	Q_UNUSED(r);
34724 
34725 	QString begin=QString("");
34726 	if(!active)
34727 		begin="X - ";
34728 
34729 	QString end=QString("");
34730 	if(!comments.isEmpty())
34731 		end=", "+tr("C: %1", "Comments").arg(comments);
34732 
34733 	QString s;
34734 	s+=tr("Students max span per real day");s+=", ";
34735 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
34736 	s+=tr("MS:%1", "Max span (in hours, per day)").arg(this->maxSpanPerDay);
34737 
34738 	return begin+s+end;
34739 }
34740 
getDetailedDescription(Rules & r)34741 QString ConstraintStudentsMaxSpanPerRealDay::getDetailedDescription(Rules& r)
34742 {
34743 	Q_UNUSED(r);
34744 
34745 	QString s=tr("Time constraint");s+="\n";
34746 	s+=tr("All students must respect the maximum number of span (in hours) per real day");s+="\n";
34747 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
34748 	s+=tr("Maximum span per day=%1").arg(this->maxSpanPerDay);s+="\n";
34749 
34750 	if(!active){
34751 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
34752 		s+="\n";
34753 	}
34754 	if(!comments.isEmpty()){
34755 		s+=tr("Comments=%1").arg(comments);
34756 		s+="\n";
34757 	}
34758 
34759 	return s;
34760 }
34761 
computeInternalStructure(QWidget * parent,Rules & r)34762 bool ConstraintStudentsMaxSpanPerRealDay::computeInternalStructure(QWidget* parent, Rules& r)
34763 {
34764 	Q_UNUSED(parent);
34765 	Q_UNUSED(r);
34766 
34767 	return true;
34768 }
34769 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)34770 double ConstraintStudentsMaxSpanPerRealDay::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
34771 {
34772 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
34773 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
34774 		c.teachersMatrixReady=true;
34775 		c.subgroupsMatrixReady=true;
34776 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
34777 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
34778 
34779 		c.changedForMatrixCalculation=false;
34780 	}
34781 
34782 	Q_UNUSED(cl);
34783 	Q_UNUSED(dl);
34784 	Q_UNUSED(conflictsString);
34785 
34786 	assert(this->weightPercentage==100.0);
34787 
34788 	int nbroken=0;
34789 
34790 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
34791 		for(int d=0; d<r.nDaysPerWeek/2; d++){
34792 			int begin=-1;
34793 			int end=-1;
34794 			for(int h=0; h<2*r.nHoursPerDay; h++){
34795 				int d3=d*2+(h<r.nHoursPerDay?0:1);
34796 				int h3=h%r.nHoursPerDay;
34797 				if(subgroupsMatrix[sbg][d3][h3]>0){
34798 					begin=h;
34799 					break;
34800 				}
34801 			}
34802 			for(int h=2*r.nHoursPerDay-1; h>=0; h--){
34803 				int d3=d*2+(h<r.nHoursPerDay?0:1);
34804 				int h3=h%r.nHoursPerDay;
34805 				if(subgroupsMatrix[sbg][d3][h3]>0){
34806 					end=h;
34807 					break;
34808 				}
34809 			}
34810 			if(end>=0 && begin>=0 && end>=begin){
34811 				int span=end-begin+1;
34812 				if(span>this->maxSpanPerDay)
34813 					nbroken++;
34814 			}
34815 		}
34816 	}
34817 
34818 	assert(nbroken==0);
34819 
34820 	return nbroken;
34821 }
34822 
isRelatedToActivity(Rules & r,Activity * a)34823 bool ConstraintStudentsMaxSpanPerRealDay::isRelatedToActivity(Rules& r, Activity* a)
34824 {
34825 	Q_UNUSED(r);
34826 	Q_UNUSED(a);
34827 
34828 	return false;
34829 }
34830 
isRelatedToTeacher(Teacher * t)34831 bool ConstraintStudentsMaxSpanPerRealDay::isRelatedToTeacher(Teacher* t)
34832 {
34833 	Q_UNUSED(t);
34834 
34835 	return false;
34836 }
34837 
isRelatedToSubject(Subject * s)34838 bool ConstraintStudentsMaxSpanPerRealDay::isRelatedToSubject(Subject* s)
34839 {
34840 	Q_UNUSED(s);
34841 
34842 	return false;
34843 }
34844 
isRelatedToActivityTag(ActivityTag * s)34845 bool ConstraintStudentsMaxSpanPerRealDay::isRelatedToActivityTag(ActivityTag* s)
34846 {
34847 	Q_UNUSED(s);
34848 
34849 	return false;
34850 }
34851 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)34852 bool ConstraintStudentsMaxSpanPerRealDay::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
34853 {
34854 	Q_UNUSED(r);
34855 	Q_UNUSED(s);
34856 
34857 	return true;
34858 }
34859 
hasWrongDayOrHour(Rules & r)34860 bool ConstraintStudentsMaxSpanPerRealDay::hasWrongDayOrHour(Rules& r)
34861 {
34862 	if(maxSpanPerDay>r.nHoursPerDay)
34863 		return true;
34864 
34865 	return false;
34866 }
34867 
canRepairWrongDayOrHour(Rules & r)34868 bool ConstraintStudentsMaxSpanPerRealDay::canRepairWrongDayOrHour(Rules& r)
34869 {
34870 	assert(hasWrongDayOrHour(r));
34871 
34872 	return true;
34873 }
34874 
repairWrongDayOrHour(Rules & r)34875 bool ConstraintStudentsMaxSpanPerRealDay::repairWrongDayOrHour(Rules& r)
34876 {
34877 	assert(hasWrongDayOrHour(r));
34878 
34879 	if(maxSpanPerDay>r.nHoursPerDay)
34880 		maxSpanPerDay=r.nHoursPerDay;
34881 
34882 	return true;
34883 }
34884 
34885 ///////////////////////////////////////////////////////////////////////////////////////////
34886 ///////////////////////////////////////////////////////////////////////////////////////////
34887 
ConstraintTeacherMaxAfternoonsPerWeek()34888 ConstraintTeacherMaxAfternoonsPerWeek::ConstraintTeacherMaxAfternoonsPerWeek()
34889 	: TimeConstraint()
34890 {
34891 	this->type=CONSTRAINT_TEACHER_MAX_AFTERNOONS_PER_WEEK;
34892 }
34893 
ConstraintTeacherMaxAfternoonsPerWeek(double wp,int maxnd,QString tn)34894 ConstraintTeacherMaxAfternoonsPerWeek::ConstraintTeacherMaxAfternoonsPerWeek(double wp, int maxnd, QString tn)
34895 	 : TimeConstraint(wp)
34896 {
34897 	this->teacherName = tn;
34898 	this->maxAfternoonsPerWeek=maxnd;
34899 	this->type=CONSTRAINT_TEACHER_MAX_AFTERNOONS_PER_WEEK;
34900 }
34901 
computeInternalStructure(QWidget * parent,Rules & r)34902 bool ConstraintTeacherMaxAfternoonsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
34903 {
34904 	Q_UNUSED(parent);
34905 
34906 	//this->teacher_ID=r.searchTeacher(this->teacherName);
34907 	teacher_ID=r.teachersHash.value(teacherName, -1);
34908 	assert(this->teacher_ID>=0);
34909 	return true;
34910 }
34911 
hasInactiveActivities(Rules & r)34912 bool ConstraintTeacherMaxAfternoonsPerWeek::hasInactiveActivities(Rules& r)
34913 {
34914 	Q_UNUSED(r);
34915 	return false;
34916 }
34917 
getXmlDescription(Rules & r)34918 QString ConstraintTeacherMaxAfternoonsPerWeek::getXmlDescription(Rules& r)
34919 {
34920 	Q_UNUSED(r);
34921 
34922 	QString s="<ConstraintTeacherMaxAfternoonsPerWeek>\n";
34923 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
34924 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
34925 	s+="	<Max_Afternoons_Per_Week>"+CustomFETString::number(this->maxAfternoonsPerWeek)+"</Max_Afternoons_Per_Week>\n";
34926 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
34927 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
34928 	s+="</ConstraintTeacherMaxAfternoonsPerWeek>\n";
34929 	return s;
34930 }
34931 
getDescription(Rules & r)34932 QString ConstraintTeacherMaxAfternoonsPerWeek::getDescription(Rules& r){
34933 	Q_UNUSED(r);
34934 
34935 	QString begin=QString("");
34936 	if(!active)
34937 		begin="X - ";
34938 
34939 	QString end=QString("");
34940 	if(!comments.isEmpty())
34941 		end=", "+tr("C: %1", "Comments").arg(comments);
34942 
34943 	QString s=tr("Teacher max afternoons per week");s+=", ";
34944 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
34945 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
34946 	s+=tr("MA:%1", "Max afternoons (per week)").arg(this->maxAfternoonsPerWeek);
34947 
34948 	return begin+s+end;
34949 }
34950 
getDetailedDescription(Rules & r)34951 QString ConstraintTeacherMaxAfternoonsPerWeek::getDetailedDescription(Rules& r){
34952 	Q_UNUSED(r);
34953 
34954 	QString s=tr("Time constraint");s+="\n";
34955 	s+=tr("A teacher must respect the maximum number of afternoons per week");s+="\n";
34956 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
34957 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
34958 	s+=tr("Maximum afternoons per week=%1").arg(this->maxAfternoonsPerWeek);s+="\n";
34959 
34960 	if(!active){
34961 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
34962 		s+="\n";
34963 	}
34964 	if(!comments.isEmpty()){
34965 		s+=tr("Comments=%1").arg(comments);
34966 		s+="\n";
34967 	}
34968 
34969 	return s;
34970 }
34971 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)34972 double ConstraintTeacherMaxAfternoonsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
34973 {
34974 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
34975 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
34976 		c.teachersMatrixReady=true;
34977 		c.subgroupsMatrixReady=true;
34978 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
34979 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
34980 
34981 		c.changedForMatrixCalculation=false;
34982 	}
34983 
34984 	int nbroken;
34985 
34986 	//without logging
34987 	/*
34988 	if(conflictsString==nullptr){
34989 		nbroken=0;
34990 		//count sort
34991 		int t=this->teacher_ID;
34992 		int nd[MAX_HOURS_PER_DAY + 1];
34993 		for(int h=0; h<=r.nHoursPerDay; h++)
34994 			nd[h]=0;
34995 		for(int d=0; d<r.nDaysPerWeek; d++){
34996 			int nh=0;
34997 			for(int h=0; h<r.nHoursPerDay; h++)
34998 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
34999 			nd[nh]++;
35000 		}
35001 		//return the minimum occupied days which do not respect this constraint
35002 		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
35003 		for(int k=0; k<=r.nHoursPerDay; k++){
35004 			if(nd[k]>0){
35005 				if(i>nd[k]){
35006 					i-=nd[k];
35007 					nbroken+=nd[k]*k;
35008 				}
35009 				else{
35010 					nbroken+=i*k;
35011 					break;
35012 				}
35013 			}
35014 		}
35015 	}
35016 	//with logging
35017 	else{*/
35018 		nbroken=0;
35019 		//count sort
35020 		int t=this->teacher_ID;
35021 		//int nd[2*MAX_HOURS_PER_DAY + 1];
35022 		int nOD=0; //n occupied days
35023 		//for(int h=0; h<=2*r.nHoursPerDay; h++)
35024 		//nd[h]=0;
35025 		for(int d=1; d<r.nDaysPerWeek; d+=2){
35026 			int nh=0;
35027 			for(int h=0; h<r.nHoursPerDay; h++)
35028 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
35029 			if(nh>0)
35030 				nOD++;
35031 			//nd[nh]++;
35032 		}
35033 
35034 		//return the minimum occupied days which do not respect this constraint
35035 /*		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
35036 		for(int k=0; k<=2*r.nHoursPerDay; k++){
35037 			if(nd[k]>0){
35038 				if(i>nd[k]){
35039 					i-=nd[k];
35040 					nbroken+=nd[k]*k;
35041 				}
35042 				else{
35043 					nbroken+=i*k;
35044 					break;
35045 				}
35046 			}
35047 		}*/
35048 
35049 		if(nOD>this->maxAfternoonsPerWeek)
35050 			nbroken=1;
35051 
35052 		if(nbroken>0 && conflictsString!=nullptr){
35053 			QString s= tr("Time constraint teacher max afternoons per week broken for teacher: %1.")
35054 			 .arg(r.internalTeachersList[t]->name);
35055 			s += tr("This increases the conflicts total by %1")
35056 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
35057 
35058 			dl.append(s);
35059 			cl.append(nbroken*weightPercentage/100);
35060 
35061 			*conflictsString += s+"\n";
35062 		}
35063 	//}
35064 
35065 	if(weightPercentage==100)
35066 		assert(nbroken==0);
35067 	return weightPercentage/100 * nbroken;
35068 }
35069 
isRelatedToActivity(Rules & r,Activity * a)35070 bool ConstraintTeacherMaxAfternoonsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
35071 {
35072 	Q_UNUSED(r);
35073 	Q_UNUSED(a);
35074 
35075 	return false;
35076 }
35077 
isRelatedToTeacher(Teacher * t)35078 bool ConstraintTeacherMaxAfternoonsPerWeek::isRelatedToTeacher(Teacher* t)
35079 {
35080 	if(this->teacherName==t->name)
35081 		return true;
35082 	return false;
35083 }
35084 
isRelatedToSubject(Subject * s)35085 bool ConstraintTeacherMaxAfternoonsPerWeek::isRelatedToSubject(Subject* s)
35086 {
35087 	Q_UNUSED(s);
35088 
35089 	return false;
35090 }
35091 
isRelatedToActivityTag(ActivityTag * s)35092 bool ConstraintTeacherMaxAfternoonsPerWeek::isRelatedToActivityTag(ActivityTag* s)
35093 {
35094 	Q_UNUSED(s);
35095 
35096 	return false;
35097 }
35098 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)35099 bool ConstraintTeacherMaxAfternoonsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
35100 {
35101 	Q_UNUSED(r);
35102 	Q_UNUSED(s);
35103 
35104 	return false;
35105 }
35106 
hasWrongDayOrHour(Rules & r)35107 bool ConstraintTeacherMaxAfternoonsPerWeek::hasWrongDayOrHour(Rules& r)
35108 {
35109 	if(maxAfternoonsPerWeek>r.nDaysPerWeek/2)
35110 		return true;
35111 
35112 	return false;
35113 }
35114 
canRepairWrongDayOrHour(Rules & r)35115 bool ConstraintTeacherMaxAfternoonsPerWeek::canRepairWrongDayOrHour(Rules& r)
35116 {
35117 	assert(hasWrongDayOrHour(r));
35118 
35119 	return true;
35120 }
35121 
repairWrongDayOrHour(Rules & r)35122 bool ConstraintTeacherMaxAfternoonsPerWeek::repairWrongDayOrHour(Rules& r)
35123 {
35124 	assert(hasWrongDayOrHour(r));
35125 
35126 	if(maxAfternoonsPerWeek>r.nDaysPerWeek/2)
35127 		maxAfternoonsPerWeek=r.nDaysPerWeek/2;
35128 
35129 	return true;
35130 }
35131 
35132 ///////////////////////////////////////////////////////////////////////////////////////////
35133 ///////////////////////////////////////////////////////////////////////////////////////////
35134 
ConstraintTeachersMaxAfternoonsPerWeek()35135 ConstraintTeachersMaxAfternoonsPerWeek::ConstraintTeachersMaxAfternoonsPerWeek()
35136 	: TimeConstraint()
35137 {
35138 	this->type=CONSTRAINT_TEACHERS_MAX_AFTERNOONS_PER_WEEK;
35139 }
35140 
ConstraintTeachersMaxAfternoonsPerWeek(double wp,int maxnd)35141 ConstraintTeachersMaxAfternoonsPerWeek::ConstraintTeachersMaxAfternoonsPerWeek(double wp, int maxnd)
35142 	 : TimeConstraint(wp)
35143 {
35144 	this->maxAfternoonsPerWeek=maxnd;
35145 	this->type=CONSTRAINT_TEACHERS_MAX_AFTERNOONS_PER_WEEK;
35146 }
35147 
computeInternalStructure(QWidget * parent,Rules & r)35148 bool ConstraintTeachersMaxAfternoonsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
35149 {
35150 	Q_UNUSED(parent);
35151 	Q_UNUSED(r);
35152 
35153 	return true;
35154 }
35155 
hasInactiveActivities(Rules & r)35156 bool ConstraintTeachersMaxAfternoonsPerWeek::hasInactiveActivities(Rules& r)
35157 {
35158 	Q_UNUSED(r);
35159 	return false;
35160 }
35161 
getXmlDescription(Rules & r)35162 QString ConstraintTeachersMaxAfternoonsPerWeek::getXmlDescription(Rules& r)
35163 {
35164 	Q_UNUSED(r);
35165 
35166 	QString s="<ConstraintTeachersMaxAfternoonsPerWeek>\n";
35167 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
35168 	s+="	<Max_Afternoons_Per_Week>"+CustomFETString::number(this->maxAfternoonsPerWeek)+"</Max_Afternoons_Per_Week>\n";
35169 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
35170 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
35171 	s+="</ConstraintTeachersMaxAfternoonsPerWeek>\n";
35172 	return s;
35173 }
35174 
getDescription(Rules & r)35175 QString ConstraintTeachersMaxAfternoonsPerWeek::getDescription(Rules& r){
35176 	Q_UNUSED(r);
35177 
35178 	QString begin=QString("");
35179 	if(!active)
35180 		begin="X - ";
35181 
35182 	QString end=QString("");
35183 	if(!comments.isEmpty())
35184 		end=", "+tr("C: %1", "Comments").arg(comments);
35185 
35186 	QString s=tr("Teachers max afternoons per week");s+=", ";
35187 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
35188 	s+=tr("MA:%1", "Max afternoons (per week)").arg(this->maxAfternoonsPerWeek);
35189 
35190 	return begin+s+end;
35191 }
35192 
getDetailedDescription(Rules & r)35193 QString ConstraintTeachersMaxAfternoonsPerWeek::getDetailedDescription(Rules& r){
35194 	Q_UNUSED(r);
35195 
35196 	QString s=tr("Time constraint");s+="\n";
35197 	s+=tr("All teachers must respect the maximum number of afternoons per week");s+="\n";
35198 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
35199 	s+=tr("Maximum afternoons per week=%1").arg(this->maxAfternoonsPerWeek);s+="\n";
35200 
35201 	if(!active){
35202 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
35203 		s+="\n";
35204 	}
35205 	if(!comments.isEmpty()){
35206 		s+=tr("Comments=%1").arg(comments);
35207 		s+="\n";
35208 	}
35209 
35210 	return s;
35211 }
35212 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)35213 double ConstraintTeachersMaxAfternoonsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
35214 {
35215 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
35216 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
35217 		c.teachersMatrixReady=true;
35218 		c.subgroupsMatrixReady=true;
35219 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
35220 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
35221 
35222 		c.changedForMatrixCalculation=false;
35223 	}
35224 
35225 	int nbroken;
35226 
35227 	//without logging
35228 	/*
35229 	if(conflictsString==nullptr){
35230 		nbroken=0;
35231 		//count sort
35232 
35233 		for(int t=0; t<r.nInternalTeachers; t++){
35234 			int nd[MAX_HOURS_PER_DAY + 1];
35235 			for(int h=0; h<=r.nHoursPerDay; h++)
35236 				nd[h]=0;
35237 			for(int d=0; d<r.nDaysPerWeek; d++){
35238 				int nh=0;
35239 				for(int h=0; h<r.nHoursPerDay; h++)
35240 					nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
35241 				nd[nh]++;
35242 			}
35243 			//return the minimum occupied days which do not respect this constraint
35244 			int i = r.nDaysPerWeek - this->maxDaysPerWeek;
35245 			for(int k=0; k<=r.nHoursPerDay; k++){
35246 				if(nd[k]>0){
35247 					if(i>nd[k]){
35248 						i-=nd[k];
35249 						nbroken+=nd[k]*k;
35250 					}
35251 					else{
35252 						nbroken+=i*k;
35253 						break;
35254 					}
35255 				}
35256 			}
35257 
35258 		}
35259 	}
35260 	//with logging
35261 	else{*/
35262 		nbroken=0;
35263 
35264 		for(int t=0; t<r.nInternalTeachers; t++){
35265 			int nbr=0;
35266 
35267 			//count sort
35268 			//int t=this->teacher_ID;
35269 			//int nd[2*MAX_HOURS_PER_DAY + 1];
35270 			//for(int h=0; h<=2*r.nHoursPerDay; h++)
35271 			//	nd[h]=0;
35272 			int nOD=0;
35273 			for(int d=1; d<r.nDaysPerWeek; d+=2){
35274 				int nh=0;
35275 				for(int h=0; h<r.nHoursPerDay; h++)
35276 					nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
35277 				if(nh>0)
35278 					nOD++;
35279 				//nd[nh]++;
35280 			}
35281 			//return the minimum occupied days which do not respect this constraint
35282 /*			int i = r.nDaysPerWeek - this->maxDaysPerWeek;
35283 			for(int k=0; k<=2*r.nHoursPerDay; k++){
35284 				if(nd[k]>0){
35285 					if(i>nd[k]){
35286 						i-=nd[k];
35287 						nbroken+=nd[k]*k;
35288 						nbr+=nd[k]*k;
35289 					}
35290 					else{
35291 						nbroken+=i*k;
35292 						nbr+=i*k;
35293 						break;
35294 					}
35295 				}
35296 			}*/
35297 
35298 			if(nOD>this->maxAfternoonsPerWeek)
35299 				 nbr=1;
35300 
35301 			if(nbr>0 && conflictsString!=nullptr){
35302 				QString s= tr("Time constraint teachers max afternoons per week broken for teacher: %1.")
35303 				.arg(r.internalTeachersList[t]->name);
35304 				s += tr("This increases the conflicts total by %1")
35305 				.arg(CustomFETString::numberPlusTwoDigitsPrecision(nbr*weightPercentage/100));
35306 
35307 				dl.append(s);
35308 				cl.append(nbr*weightPercentage/100);
35309 
35310 				*conflictsString += s+"\n";
35311 			}
35312 
35313 		}
35314 //	}
35315 
35316 	if(weightPercentage==100)
35317 		assert(nbroken==0);
35318 	return weightPercentage/100 * nbroken;
35319 }
35320 
isRelatedToActivity(Rules & r,Activity * a)35321 bool ConstraintTeachersMaxAfternoonsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
35322 {
35323 	Q_UNUSED(r);
35324 	Q_UNUSED(a);
35325 
35326 	return false;
35327 }
35328 
isRelatedToTeacher(Teacher * t)35329 bool ConstraintTeachersMaxAfternoonsPerWeek::isRelatedToTeacher(Teacher* t)
35330 {
35331 	Q_UNUSED(t);
35332 
35333 	return true;
35334 }
35335 
isRelatedToSubject(Subject * s)35336 bool ConstraintTeachersMaxAfternoonsPerWeek::isRelatedToSubject(Subject* s)
35337 {
35338 	Q_UNUSED(s);
35339 
35340 	return false;
35341 }
35342 
isRelatedToActivityTag(ActivityTag * s)35343 bool ConstraintTeachersMaxAfternoonsPerWeek::isRelatedToActivityTag(ActivityTag* s)
35344 {
35345 	Q_UNUSED(s);
35346 
35347 	return false;
35348 }
35349 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)35350 bool ConstraintTeachersMaxAfternoonsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
35351 {
35352 	Q_UNUSED(r);
35353 	Q_UNUSED(s);
35354 
35355 	return false;
35356 }
35357 
hasWrongDayOrHour(Rules & r)35358 bool ConstraintTeachersMaxAfternoonsPerWeek::hasWrongDayOrHour(Rules& r)
35359 {
35360 	if(maxAfternoonsPerWeek>r.nDaysPerWeek/2)
35361 		return true;
35362 
35363 	return false;
35364 }
35365 
canRepairWrongDayOrHour(Rules & r)35366 bool ConstraintTeachersMaxAfternoonsPerWeek::canRepairWrongDayOrHour(Rules& r)
35367 {
35368 	assert(hasWrongDayOrHour(r));
35369 
35370 	return true;
35371 }
35372 
repairWrongDayOrHour(Rules & r)35373 bool ConstraintTeachersMaxAfternoonsPerWeek::repairWrongDayOrHour(Rules& r)
35374 {
35375 	assert(hasWrongDayOrHour(r));
35376 
35377 	if(maxAfternoonsPerWeek>r.nDaysPerWeek/2)
35378 		maxAfternoonsPerWeek=r.nDaysPerWeek/2;
35379 
35380 	return true;
35381 }
35382 
35383 ///////////////////////////////////////////////////////////////////////////////////////////
35384 ///////////////////////////////////////////////////////////////////////////////////////////
35385 
ConstraintTeacherMaxMorningsPerWeek()35386 ConstraintTeacherMaxMorningsPerWeek::ConstraintTeacherMaxMorningsPerWeek()
35387 	: TimeConstraint()
35388 {
35389 	this->type=CONSTRAINT_TEACHER_MAX_MORNINGS_PER_WEEK;
35390 }
35391 
ConstraintTeacherMaxMorningsPerWeek(double wp,int maxnd,QString tn)35392 ConstraintTeacherMaxMorningsPerWeek::ConstraintTeacherMaxMorningsPerWeek(double wp, int maxnd, QString tn)
35393 	 : TimeConstraint(wp)
35394 {
35395 	this->teacherName = tn;
35396 	this->maxMorningsPerWeek=maxnd;
35397 	this->type=CONSTRAINT_TEACHER_MAX_MORNINGS_PER_WEEK;
35398 }
35399 
computeInternalStructure(QWidget * parent,Rules & r)35400 bool ConstraintTeacherMaxMorningsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
35401 {
35402 	Q_UNUSED(parent);
35403 
35404 	//this->teacher_ID=r.searchTeacher(this->teacherName);
35405 	teacher_ID=r.teachersHash.value(teacherName, -1);
35406 	assert(this->teacher_ID>=0);
35407 	return true;
35408 }
35409 
hasInactiveActivities(Rules & r)35410 bool ConstraintTeacherMaxMorningsPerWeek::hasInactiveActivities(Rules& r)
35411 {
35412 	Q_UNUSED(r);
35413 	return false;
35414 }
35415 
getXmlDescription(Rules & r)35416 QString ConstraintTeacherMaxMorningsPerWeek::getXmlDescription(Rules& r)
35417 {
35418 	Q_UNUSED(r);
35419 
35420 	QString s="<ConstraintTeacherMaxMorningsPerWeek>\n";
35421 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
35422 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
35423 	s+="	<Max_Mornings_Per_Week>"+CustomFETString::number(this->maxMorningsPerWeek)+"</Max_Mornings_Per_Week>\n";
35424 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
35425 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
35426 	s+="</ConstraintTeacherMaxMorningsPerWeek>\n";
35427 	return s;
35428 }
35429 
getDescription(Rules & r)35430 QString ConstraintTeacherMaxMorningsPerWeek::getDescription(Rules& r){
35431 	Q_UNUSED(r);
35432 
35433 	QString begin=QString("");
35434 	if(!active)
35435 		begin="X - ";
35436 
35437 	QString end=QString("");
35438 	if(!comments.isEmpty())
35439 		end=", "+tr("C: %1", "Comments").arg(comments);
35440 
35441 	QString s=tr("Teacher max mornings per week");s+=", ";
35442 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
35443 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
35444 	s+=tr("MM:%1", "Max mornings (per week)").arg(this->maxMorningsPerWeek);
35445 
35446 	return begin+s+end;
35447 }
35448 
getDetailedDescription(Rules & r)35449 QString ConstraintTeacherMaxMorningsPerWeek::getDetailedDescription(Rules& r){
35450 	Q_UNUSED(r);
35451 
35452 	QString s=tr("Time constraint");s+="\n";
35453 	s+=tr("A teacher must respect the maximum number of mornings per week");s+="\n";
35454 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
35455 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
35456 	s+=tr("Maximum mornings per week=%1").arg(this->maxMorningsPerWeek);s+="\n";
35457 
35458 	if(!active){
35459 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
35460 		s+="\n";
35461 	}
35462 	if(!comments.isEmpty()){
35463 		s+=tr("Comments=%1").arg(comments);
35464 		s+="\n";
35465 	}
35466 
35467 	return s;
35468 }
35469 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)35470 double ConstraintTeacherMaxMorningsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
35471 {
35472 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
35473 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
35474 		c.teachersMatrixReady=true;
35475 		c.subgroupsMatrixReady=true;
35476 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
35477 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
35478 
35479 		c.changedForMatrixCalculation=false;
35480 	}
35481 
35482 	int nbroken;
35483 
35484 	//without logging
35485 	/*
35486 	if(conflictsString==nullptr){
35487 		nbroken=0;
35488 		//count sort
35489 		int t=this->teacher_ID;
35490 		int nd[MAX_HOURS_PER_DAY + 1];
35491 		for(int h=0; h<=r.nHoursPerDay; h++)
35492 			nd[h]=0;
35493 		for(int d=0; d<r.nDaysPerWeek; d++){
35494 			int nh=0;
35495 			for(int h=0; h<r.nHoursPerDay; h++)
35496 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
35497 			nd[nh]++;
35498 		}
35499 		//return the minimum occupied days which do not respect this constraint
35500 		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
35501 		for(int k=0; k<=r.nHoursPerDay; k++){
35502 			if(nd[k]>0){
35503 				if(i>nd[k]){
35504 					i-=nd[k];
35505 					nbroken+=nd[k]*k;
35506 				}
35507 				else{
35508 					nbroken+=i*k;
35509 					break;
35510 				}
35511 			}
35512 		}
35513 	}
35514 	//with logging
35515 	else{*/
35516 		nbroken=0;
35517 		//count sort
35518 		int t=this->teacher_ID;
35519 		//int nd[2*MAX_HOURS_PER_DAY + 1];
35520 		int nOD=0; //n occupied days
35521 		//for(int h=0; h<=2*r.nHoursPerDay; h++)
35522 		//nd[h]=0;
35523 		for(int d=0; d<r.nDaysPerWeek; d+=2){
35524 			int nh=0;
35525 			for(int h=0; h<r.nHoursPerDay; h++)
35526 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
35527 			if(nh>0)
35528 				nOD++;
35529 			//nd[nh]++;
35530 		}
35531 
35532 		//return the minimum occupied days which do not respect this constraint
35533 /*		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
35534 		for(int k=0; k<=2*r.nHoursPerDay; k++){
35535 			if(nd[k]>0){
35536 				if(i>nd[k]){
35537 					i-=nd[k];
35538 					nbroken+=nd[k]*k;
35539 				}
35540 				else{
35541 					nbroken+=i*k;
35542 					break;
35543 				}
35544 			}
35545 		}*/
35546 
35547 		if(nOD>this->maxMorningsPerWeek)
35548 			nbroken=1;
35549 
35550 		if(nbroken>0 && conflictsString!=nullptr){
35551 			QString s= tr("Time constraint teacher max mornings per week broken for teacher: %1.")
35552 			 .arg(r.internalTeachersList[t]->name);
35553 			s += tr("This increases the conflicts total by %1")
35554 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
35555 
35556 			dl.append(s);
35557 			cl.append(nbroken*weightPercentage/100);
35558 
35559 			*conflictsString += s+"\n";
35560 		}
35561 	//}
35562 
35563 	if(weightPercentage==100)
35564 		assert(nbroken==0);
35565 	return weightPercentage/100 * nbroken;
35566 }
35567 
isRelatedToActivity(Rules & r,Activity * a)35568 bool ConstraintTeacherMaxMorningsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
35569 {
35570 	Q_UNUSED(r);
35571 	Q_UNUSED(a);
35572 
35573 	return false;
35574 }
35575 
isRelatedToTeacher(Teacher * t)35576 bool ConstraintTeacherMaxMorningsPerWeek::isRelatedToTeacher(Teacher* t)
35577 {
35578 	if(this->teacherName==t->name)
35579 		return true;
35580 	return false;
35581 }
35582 
isRelatedToSubject(Subject * s)35583 bool ConstraintTeacherMaxMorningsPerWeek::isRelatedToSubject(Subject* s)
35584 {
35585 	Q_UNUSED(s);
35586 
35587 	return false;
35588 }
35589 
isRelatedToActivityTag(ActivityTag * s)35590 bool ConstraintTeacherMaxMorningsPerWeek::isRelatedToActivityTag(ActivityTag* s)
35591 {
35592 	Q_UNUSED(s);
35593 
35594 	return false;
35595 }
35596 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)35597 bool ConstraintTeacherMaxMorningsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
35598 {
35599 	Q_UNUSED(r);
35600 	Q_UNUSED(s);
35601 
35602 	return false;
35603 }
35604 
hasWrongDayOrHour(Rules & r)35605 bool ConstraintTeacherMaxMorningsPerWeek::hasWrongDayOrHour(Rules& r)
35606 {
35607 	if(maxMorningsPerWeek>r.nDaysPerWeek/2)
35608 		return true;
35609 
35610 	return false;
35611 }
35612 
canRepairWrongDayOrHour(Rules & r)35613 bool ConstraintTeacherMaxMorningsPerWeek::canRepairWrongDayOrHour(Rules& r)
35614 {
35615 	assert(hasWrongDayOrHour(r));
35616 
35617 	return true;
35618 }
35619 
repairWrongDayOrHour(Rules & r)35620 bool ConstraintTeacherMaxMorningsPerWeek::repairWrongDayOrHour(Rules& r)
35621 {
35622 	assert(hasWrongDayOrHour(r));
35623 
35624 	if(maxMorningsPerWeek>r.nDaysPerWeek/2)
35625 		maxMorningsPerWeek=r.nDaysPerWeek/2;
35626 
35627 	return true;
35628 }
35629 
35630 ///////////////////////////////////////////////////////////////////////////////////////////
35631 ///////////////////////////////////////////////////////////////////////////////////////////
35632 
ConstraintTeachersMaxMorningsPerWeek()35633 ConstraintTeachersMaxMorningsPerWeek::ConstraintTeachersMaxMorningsPerWeek()
35634 	: TimeConstraint()
35635 {
35636 	this->type=CONSTRAINT_TEACHERS_MAX_MORNINGS_PER_WEEK;
35637 }
35638 
ConstraintTeachersMaxMorningsPerWeek(double wp,int maxnd)35639 ConstraintTeachersMaxMorningsPerWeek::ConstraintTeachersMaxMorningsPerWeek(double wp, int maxnd)
35640 	 : TimeConstraint(wp)
35641 {
35642 	this->maxMorningsPerWeek=maxnd;
35643 	this->type=CONSTRAINT_TEACHERS_MAX_MORNINGS_PER_WEEK;
35644 }
35645 
computeInternalStructure(QWidget * parent,Rules & r)35646 bool ConstraintTeachersMaxMorningsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
35647 {
35648 	Q_UNUSED(parent);
35649 	Q_UNUSED(r);
35650 
35651 	return true;
35652 }
35653 
hasInactiveActivities(Rules & r)35654 bool ConstraintTeachersMaxMorningsPerWeek::hasInactiveActivities(Rules& r)
35655 {
35656 	Q_UNUSED(r);
35657 	return false;
35658 }
35659 
getXmlDescription(Rules & r)35660 QString ConstraintTeachersMaxMorningsPerWeek::getXmlDescription(Rules& r)
35661 {
35662 	Q_UNUSED(r);
35663 
35664 	QString s="<ConstraintTeachersMaxMorningsPerWeek>\n";
35665 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
35666 	s+="	<Max_Mornings_Per_Week>"+CustomFETString::number(this->maxMorningsPerWeek)+"</Max_Mornings_Per_Week>\n";
35667 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
35668 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
35669 	s+="</ConstraintTeachersMaxMorningsPerWeek>\n";
35670 	return s;
35671 }
35672 
getDescription(Rules & r)35673 QString ConstraintTeachersMaxMorningsPerWeek::getDescription(Rules& r){
35674 	Q_UNUSED(r);
35675 
35676 	QString begin=QString("");
35677 	if(!active)
35678 		begin="X - ";
35679 
35680 	QString end=QString("");
35681 	if(!comments.isEmpty())
35682 		end=", "+tr("C: %1", "Comments").arg(comments);
35683 
35684 	QString s=tr("Teachers max mornings per week");s+=", ";
35685 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
35686 	s+=tr("MM:%1", "Max mornings (per week)").arg(this->maxMorningsPerWeek);
35687 
35688 	return begin+s+end;
35689 }
35690 
getDetailedDescription(Rules & r)35691 QString ConstraintTeachersMaxMorningsPerWeek::getDetailedDescription(Rules& r){
35692 	Q_UNUSED(r);
35693 
35694 	QString s=tr("Time constraint");s+="\n";
35695 	s+=tr("All teachers must respect the maximum number of mornings per week");s+="\n";
35696 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
35697 	s+=tr("Maximum mornings per week=%1").arg(this->maxMorningsPerWeek);s+="\n";
35698 
35699 	if(!active){
35700 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
35701 		s+="\n";
35702 	}
35703 	if(!comments.isEmpty()){
35704 		s+=tr("Comments=%1").arg(comments);
35705 		s+="\n";
35706 	}
35707 
35708 	return s;
35709 }
35710 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)35711 double ConstraintTeachersMaxMorningsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
35712 {
35713 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
35714 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
35715 		c.teachersMatrixReady=true;
35716 		c.subgroupsMatrixReady=true;
35717 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
35718 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
35719 
35720 		c.changedForMatrixCalculation=false;
35721 	}
35722 
35723 	int nbroken;
35724 
35725 	//without logging
35726 	/*
35727 	if(conflictsString==nullptr){
35728 		nbroken=0;
35729 		//count sort
35730 
35731 		for(int t=0; t<r.nInternalTeachers; t++){
35732 			int nd[MAX_HOURS_PER_DAY + 1];
35733 			for(int h=0; h<=r.nHoursPerDay; h++)
35734 				nd[h]=0;
35735 			for(int d=0; d<r.nDaysPerWeek; d++){
35736 				int nh=0;
35737 				for(int h=0; h<r.nHoursPerDay; h++)
35738 					nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
35739 				nd[nh]++;
35740 			}
35741 			//return the minimum occupied days which do not respect this constraint
35742 			int i = r.nDaysPerWeek - this->maxDaysPerWeek;
35743 			for(int k=0; k<=r.nHoursPerDay; k++){
35744 				if(nd[k]>0){
35745 					if(i>nd[k]){
35746 						i-=nd[k];
35747 						nbroken+=nd[k]*k;
35748 					}
35749 					else{
35750 						nbroken+=i*k;
35751 						break;
35752 					}
35753 				}
35754 			}
35755 
35756 		}
35757 	}
35758 	//with logging
35759 	else{*/
35760 		nbroken=0;
35761 
35762 		for(int t=0; t<r.nInternalTeachers; t++){
35763 			int nbr=0;
35764 
35765 			//count sort
35766 			//int t=this->teacher_ID;
35767 			//int nd[2*MAX_HOURS_PER_DAY + 1];
35768 			//for(int h=0; h<=2*r.nHoursPerDay; h++)
35769 			//	nd[h]=0;
35770 			int nOD=0;
35771 			for(int d=0; d<r.nDaysPerWeek; d+=2){
35772 				int nh=0;
35773 				for(int h=0; h<r.nHoursPerDay; h++)
35774 					nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
35775 				if(nh>0)
35776 					nOD++;
35777 				//nd[nh]++;
35778 			}
35779 			//return the minimum occupied days which do not respect this constraint
35780 /*			int i = r.nDaysPerWeek - this->maxDaysPerWeek;
35781 			for(int k=0; k<=2*r.nHoursPerDay; k++){
35782 				if(nd[k]>0){
35783 					if(i>nd[k]){
35784 						i-=nd[k];
35785 						nbroken+=nd[k]*k;
35786 						nbr+=nd[k]*k;
35787 					}
35788 					else{
35789 						nbroken+=i*k;
35790 						nbr+=i*k;
35791 						break;
35792 					}
35793 				}
35794 			}*/
35795 
35796 			if(nOD>this->maxMorningsPerWeek)
35797 				 nbr=1;
35798 
35799 			if(nbr>0 && conflictsString!=nullptr){
35800 				QString s= tr("Time constraint teachers max mornings per week broken for teacher: %1.")
35801 				.arg(r.internalTeachersList[t]->name);
35802 				s += tr("This increases the conflicts total by %1")
35803 				.arg(CustomFETString::numberPlusTwoDigitsPrecision(nbr*weightPercentage/100));
35804 
35805 				dl.append(s);
35806 				cl.append(nbr*weightPercentage/100);
35807 
35808 				*conflictsString += s+"\n";
35809 			}
35810 
35811 		}
35812 //	}
35813 
35814 	if(weightPercentage==100)
35815 		assert(nbroken==0);
35816 	return weightPercentage/100 * nbroken;
35817 }
35818 
isRelatedToActivity(Rules & r,Activity * a)35819 bool ConstraintTeachersMaxMorningsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
35820 {
35821 	Q_UNUSED(r);
35822 	Q_UNUSED(a);
35823 
35824 	return false;
35825 }
35826 
isRelatedToTeacher(Teacher * t)35827 bool ConstraintTeachersMaxMorningsPerWeek::isRelatedToTeacher(Teacher* t)
35828 {
35829 	Q_UNUSED(t);
35830 
35831 	return true;
35832 }
35833 
isRelatedToSubject(Subject * s)35834 bool ConstraintTeachersMaxMorningsPerWeek::isRelatedToSubject(Subject* s)
35835 {
35836 	Q_UNUSED(s);
35837 
35838 	return false;
35839 }
35840 
isRelatedToActivityTag(ActivityTag * s)35841 bool ConstraintTeachersMaxMorningsPerWeek::isRelatedToActivityTag(ActivityTag* s)
35842 {
35843 	Q_UNUSED(s);
35844 
35845 	return false;
35846 }
35847 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)35848 bool ConstraintTeachersMaxMorningsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
35849 {
35850 	Q_UNUSED(r);
35851 	Q_UNUSED(s);
35852 
35853 	return false;
35854 }
35855 
hasWrongDayOrHour(Rules & r)35856 bool ConstraintTeachersMaxMorningsPerWeek::hasWrongDayOrHour(Rules& r)
35857 {
35858 	if(maxMorningsPerWeek>r.nDaysPerWeek/2)
35859 		return true;
35860 
35861 	return false;
35862 }
35863 
canRepairWrongDayOrHour(Rules & r)35864 bool ConstraintTeachersMaxMorningsPerWeek::canRepairWrongDayOrHour(Rules& r)
35865 {
35866 	assert(hasWrongDayOrHour(r));
35867 
35868 	return true;
35869 }
35870 
repairWrongDayOrHour(Rules & r)35871 bool ConstraintTeachersMaxMorningsPerWeek::repairWrongDayOrHour(Rules& r)
35872 {
35873 	assert(hasWrongDayOrHour(r));
35874 
35875 	if(maxMorningsPerWeek>r.nDaysPerWeek/2)
35876 		maxMorningsPerWeek=r.nDaysPerWeek/2;
35877 
35878 	return true;
35879 }
35880 
35881 ///////////////////////////////////////////////////////////////////////////////////////////
35882 ///////////////////////////////////////////////////////////////////////////////////////////
35883 
ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3()35884 ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3()
35885 	: TimeConstraint()
35886 {
35887 	this->type=CONSTRAINT_TEACHER_MAX_TWO_ACTIVITY_TAGS_PER_DAY_FROM_N1N2N3;
35888 }
35889 
ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3(double wp,QString tn)35890 ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3(double wp, QString tn)
35891 	 : TimeConstraint(wp)
35892 {
35893 	this->teacherName = tn;
35894 	this->type=CONSTRAINT_TEACHER_MAX_TWO_ACTIVITY_TAGS_PER_DAY_FROM_N1N2N3;
35895 }
35896 
computeInternalStructure(QWidget * parent,Rules & r)35897 bool ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::computeInternalStructure(QWidget* parent, Rules& r)
35898 {
35899 	Q_UNUSED(parent);
35900 
35901 	//this->teacher_ID=r.searchTeacher(this->teacherName);
35902 	teacher_ID=r.teachersHash.value(teacherName, -1);
35903 	assert(this->teacher_ID>=0);
35904 	return true;
35905 }
35906 
hasInactiveActivities(Rules & r)35907 bool ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::hasInactiveActivities(Rules& r)
35908 {
35909 	Q_UNUSED(r);
35910 	return false;
35911 }
35912 
getXmlDescription(Rules & r)35913 QString ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::getXmlDescription(Rules& r)
35914 {
35915 	Q_UNUSED(r);
35916 
35917 	QString s="<ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3>\n";
35918 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
35919 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
35920 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
35921 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
35922 	s+="</ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3>\n";
35923 	return s;
35924 }
35925 
getDescription(Rules & r)35926 QString ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::getDescription(Rules& r){
35927 	Q_UNUSED(r);
35928 
35929 	QString begin=QString("");
35930 	if(!active)
35931 		begin="X - ";
35932 
35933 	QString end=QString("");
35934 	if(!comments.isEmpty())
35935 		end=", "+tr("C: %1", "Comments").arg(comments);
35936 
35937 	QString s=tr("Teacher max two activity tags per day from N1, N2, N3");s+=", ";
35938 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
35939 	s+=tr("T:%1", "Teacher").arg(this->teacherName);
35940 
35941 	return begin+s+end;
35942 }
35943 
getDetailedDescription(Rules & r)35944 QString ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::getDetailedDescription(Rules& r){
35945 	Q_UNUSED(r);
35946 
35947 	QString s=tr("Time constraint");s+="\n";
35948 	s+=tr("A teacher must respect a maximum of two activity tags per day from N1, N2, N3");s+="\n";
35949 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
35950 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
35951 
35952 	if(!active){
35953 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
35954 		s+="\n";
35955 	}
35956 	if(!comments.isEmpty()){
35957 		s+=tr("Comments=%1").arg(comments);
35958 		s+="\n";
35959 	}
35960 
35961 	return s;
35962 }
35963 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)35964 double ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
35965 {
35966 	Q_UNUSED(cl);
35967 	Q_UNUSED(dl);
35968 	Q_UNUSED(conflictsString);
35969 
35970 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
35971 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
35972 		c.teachersMatrixReady=true;
35973 		c.subgroupsMatrixReady=true;
35974 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
35975 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
35976 
35977 		c.changedForMatrixCalculation=false;
35978 	}
35979 
35980 	int nbroken=0;
35981 
35982 	Teacher* tch=r.internalTeachersList[teacher_ID];
35983 	Matrix2D<int> crtTeacherTimetableActivityTag;
35984 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
35985 	for(int d=0; d<r.nDaysPerWeek; d++)
35986 		for(int h=0; h<r.nHoursPerDay; h++)
35987 			crtTeacherTimetableActivityTag[d][h]=-1;
35988 
35989 	for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
35990 		int d=c.times[ai]%r.nDaysPerWeek;
35991 		int h=c.times[ai]/r.nDaysPerWeek;
35992 		for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
35993 			assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
35994 			crtTeacherTimetableActivityTag[d][h+dur]=activityTagN1N2N3[ai];
35995 		}
35996 	}
35997 
35998 	for(int d=0; d<r.nDaysPerWeek; d++){
35999 		int cnt[4];
36000 		cnt[0]=cnt[1]=cnt[2]=cnt[3]=0; //cnt[3] means none.
36001 
36002 		for(int h=0; h<r.nHoursPerDay; h++){
36003 			if(crtTeacherTimetableActivityTag[d][h]>=0){
36004 				assert(crtTeacherTimetableActivityTag[d][h]<4);
36005 				cnt[crtTeacherTimetableActivityTag[d][h]]++;
36006 			}
36007 		}
36008 
36009 		if(cnt[0]>0 && cnt[1]>0 && cnt[2]>0)
36010 			nbroken++;
36011 	}
36012 
36013 	assert(weightPercentage==100);
36014 
36015 	if(weightPercentage==100)
36016 		assert(nbroken==0);
36017 	return weightPercentage/100 * nbroken;
36018 }
36019 
isRelatedToActivity(Rules & r,Activity * a)36020 bool ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::isRelatedToActivity(Rules& r, Activity* a)
36021 {
36022 	Q_UNUSED(r);
36023 	Q_UNUSED(a);
36024 
36025 	return false;
36026 }
36027 
isRelatedToTeacher(Teacher * t)36028 bool ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::isRelatedToTeacher(Teacher* t)
36029 {
36030 	if(this->teacherName==t->name)
36031 		return true;
36032 	return false;
36033 }
36034 
isRelatedToSubject(Subject * s)36035 bool ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::isRelatedToSubject(Subject* s)
36036 {
36037 	Q_UNUSED(s);
36038 
36039 	return false;
36040 }
36041 
isRelatedToActivityTag(ActivityTag * s)36042 bool ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::isRelatedToActivityTag(ActivityTag* s)
36043 {
36044 	Q_UNUSED(s);
36045 
36046 	return false;
36047 }
36048 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)36049 bool ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
36050 {
36051 	Q_UNUSED(r);
36052 	Q_UNUSED(s);
36053 
36054 	return false;
36055 }
36056 
hasWrongDayOrHour(Rules & r)36057 bool ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::hasWrongDayOrHour(Rules& r)
36058 {
36059 	Q_UNUSED(r);
36060 
36061 	return false;
36062 }
36063 
canRepairWrongDayOrHour(Rules & r)36064 bool ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::canRepairWrongDayOrHour(Rules& r)
36065 {
36066 	assert(hasWrongDayOrHour(r));
36067 
36068 	return true;
36069 }
36070 
repairWrongDayOrHour(Rules & r)36071 bool ConstraintTeacherMaxTwoActivityTagsPerDayFromN1N2N3::repairWrongDayOrHour(Rules& r)
36072 {
36073 	assert(hasWrongDayOrHour(r));
36074 
36075 	return true;
36076 }
36077 
36078 ///////////////////////////////////////////////////////////////////////////////////////////
36079 ///////////////////////////////////////////////////////////////////////////////////////////
36080 
ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3()36081 ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3()
36082 	: TimeConstraint()
36083 {
36084 	this->type=CONSTRAINT_TEACHERS_MAX_TWO_ACTIVITY_TAGS_PER_DAY_FROM_N1N2N3;
36085 }
36086 
ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3(double wp)36087 ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3(double wp)
36088 	 : TimeConstraint(wp)
36089 {
36090 	this->type=CONSTRAINT_TEACHERS_MAX_TWO_ACTIVITY_TAGS_PER_DAY_FROM_N1N2N3;
36091 }
36092 
computeInternalStructure(QWidget * parent,Rules & r)36093 bool ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::computeInternalStructure(QWidget* parent, Rules& r)
36094 {
36095 	Q_UNUSED(parent);
36096 	Q_UNUSED(r);
36097 
36098 	//this->teacher_ID=r.searchTeacher(this->teacherName);
36099 	//teacher_ID=r.teachersHash.value(teacherName, -1);
36100 	//assert(this->teacher_ID>=0);
36101 	return true;
36102 }
36103 
hasInactiveActivities(Rules & r)36104 bool ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::hasInactiveActivities(Rules& r)
36105 {
36106 	Q_UNUSED(r);
36107 	return false;
36108 }
36109 
getXmlDescription(Rules & r)36110 QString ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::getXmlDescription(Rules& r)
36111 {
36112 	Q_UNUSED(r);
36113 
36114 	QString s="<ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3>\n";
36115 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
36116 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
36117 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
36118 	s+="</ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3>\n";
36119 	return s;
36120 }
36121 
getDescription(Rules & r)36122 QString ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::getDescription(Rules& r){
36123 	Q_UNUSED(r);
36124 
36125 	QString begin=QString("");
36126 	if(!active)
36127 		begin="X - ";
36128 
36129 	QString end=QString("");
36130 	if(!comments.isEmpty())
36131 		end=", "+tr("C: %1", "Comments").arg(comments);
36132 
36133 	QString s=tr("Teachers max two activity tags per day from N1, N2, N3");s+=", ";
36134 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
36135 
36136 	return begin+s+end;
36137 }
36138 
getDetailedDescription(Rules & r)36139 QString ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::getDetailedDescription(Rules& r){
36140 	Q_UNUSED(r);
36141 
36142 	QString s=tr("Time constraint");s+="\n";
36143 	s+=tr("All teachers must respect a maximum of two activity tags per day from N1, N2, N3");s+="\n";
36144 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
36145 
36146 	if(!active){
36147 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
36148 		s+="\n";
36149 	}
36150 	if(!comments.isEmpty()){
36151 		s+=tr("Comments=%1").arg(comments);
36152 		s+="\n";
36153 	}
36154 
36155 	return s;
36156 }
36157 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)36158 double ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
36159 {
36160 	Q_UNUSED(cl);
36161 	Q_UNUSED(dl);
36162 	Q_UNUSED(conflictsString);
36163 
36164 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
36165 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
36166 		c.teachersMatrixReady=true;
36167 		c.subgroupsMatrixReady=true;
36168 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
36169 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
36170 
36171 		c.changedForMatrixCalculation=false;
36172 	}
36173 
36174 	int nbroken=0;
36175 
36176 	Matrix2D<int> crtTeacherTimetableActivityTag;
36177 	crtTeacherTimetableActivityTag.resize(r.nDaysPerWeek, r.nHoursPerDay);
36178 	for(int teacher_ID=0; teacher_ID<r.nInternalTeachers; teacher_ID++){
36179 		Teacher* tch=r.internalTeachersList[teacher_ID];
36180 		for(int d=0; d<r.nDaysPerWeek; d++)
36181 			for(int h=0; h<r.nHoursPerDay; h++)
36182 				crtTeacherTimetableActivityTag[d][h]=-1;
36183 
36184 		for(int ai : qAsConst(tch->activitiesForTeacher)) if(c.times[ai]!=UNALLOCATED_TIME){
36185 			int d=c.times[ai]%r.nDaysPerWeek;
36186 			int h=c.times[ai]/r.nDaysPerWeek;
36187 			for(int dur=0; dur<r.internalActivitiesList[ai].duration; dur++){
36188 				assert(crtTeacherTimetableActivityTag[d][h+dur]==-1);
36189 				crtTeacherTimetableActivityTag[d][h+dur]=activityTagN1N2N3[ai];
36190 			}
36191 		}
36192 
36193 		for(int d=0; d<r.nDaysPerWeek; d++){
36194 			int cnt[4];
36195 			cnt[0]=cnt[1]=cnt[2]=cnt[3]=0; //cnt[3] means none.
36196 
36197 			for(int h=0; h<r.nHoursPerDay; h++){
36198 				if(crtTeacherTimetableActivityTag[d][h]>=0){
36199 					assert(crtTeacherTimetableActivityTag[d][h]<4);
36200 					cnt[crtTeacherTimetableActivityTag[d][h]]++;
36201 				}
36202 			}
36203 
36204 			if(cnt[0]>0 && cnt[1]>0 && cnt[2]>0)
36205 				nbroken++;
36206 		}
36207 	}
36208 
36209 	assert(weightPercentage==100);
36210 
36211 	if(weightPercentage==100)
36212 		assert(nbroken==0);
36213 	return weightPercentage/100 * nbroken;
36214 }
36215 
isRelatedToActivity(Rules & r,Activity * a)36216 bool ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::isRelatedToActivity(Rules& r, Activity* a)
36217 {
36218 	Q_UNUSED(r);
36219 	Q_UNUSED(a);
36220 
36221 	return false;
36222 }
36223 
isRelatedToTeacher(Teacher * t)36224 bool ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::isRelatedToTeacher(Teacher* t)
36225 {
36226 	Q_UNUSED(t);
36227 
36228 	return false;
36229 }
36230 
isRelatedToSubject(Subject * s)36231 bool ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::isRelatedToSubject(Subject* s)
36232 {
36233 	Q_UNUSED(s);
36234 
36235 	return false;
36236 }
36237 
isRelatedToActivityTag(ActivityTag * s)36238 bool ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::isRelatedToActivityTag(ActivityTag* s)
36239 {
36240 	Q_UNUSED(s);
36241 
36242 	return false;
36243 }
36244 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)36245 bool ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
36246 {
36247 	Q_UNUSED(r);
36248 	Q_UNUSED(s);
36249 
36250 	return false;
36251 }
36252 
hasWrongDayOrHour(Rules & r)36253 bool ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::hasWrongDayOrHour(Rules& r)
36254 {
36255 	Q_UNUSED(r);
36256 
36257 	return false;
36258 }
36259 
canRepairWrongDayOrHour(Rules & r)36260 bool ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::canRepairWrongDayOrHour(Rules& r)
36261 {
36262 	assert(hasWrongDayOrHour(r));
36263 
36264 	return true;
36265 }
36266 
repairWrongDayOrHour(Rules & r)36267 bool ConstraintTeachersMaxTwoActivityTagsPerDayFromN1N2N3::repairWrongDayOrHour(Rules& r)
36268 {
36269 	assert(hasWrongDayOrHour(r));
36270 
36271 	return true;
36272 }
36273 
36274 ///////////////////////////////////////////////////////////////////////////////////////////
36275 ///////////////////////////////////////////////////////////////////////////////////////////
36276 
ConstraintTeacherMinMorningsPerWeek()36277 ConstraintTeacherMinMorningsPerWeek::ConstraintTeacherMinMorningsPerWeek()
36278 	: TimeConstraint()
36279 {
36280 	this->type=CONSTRAINT_TEACHER_MIN_MORNINGS_PER_WEEK;
36281 }
36282 
ConstraintTeacherMinMorningsPerWeek(double wp,int minmornings,const QString & teacher)36283 ConstraintTeacherMinMorningsPerWeek::ConstraintTeacherMinMorningsPerWeek(double wp, int minmornings, const QString& teacher)
36284  : TimeConstraint(wp)
36285  {
36286 	assert(minmornings>0);
36287 	this->minMorningsPerWeek=minmornings;
36288 	this->teacherName=teacher;
36289 
36290 	this->type=CONSTRAINT_TEACHER_MIN_MORNINGS_PER_WEEK;
36291 }
36292 
computeInternalStructure(QWidget * parent,Rules & r)36293 bool ConstraintTeacherMinMorningsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
36294 {
36295 	Q_UNUSED(parent);
36296 
36297 	//this->teacher_ID=r.searchTeacher(this->teacherName);
36298 	teacher_ID=r.teachersHash.value(teacherName, -1);
36299 	assert(this->teacher_ID>=0);
36300 	return true;
36301 }
36302 
hasInactiveActivities(Rules & r)36303 bool ConstraintTeacherMinMorningsPerWeek::hasInactiveActivities(Rules& r)
36304 {
36305 	Q_UNUSED(r);
36306 	return false;
36307 }
36308 
getXmlDescription(Rules & r)36309 QString ConstraintTeacherMinMorningsPerWeek::getXmlDescription(Rules& r){
36310 	Q_UNUSED(r);
36311 
36312 	QString s="<ConstraintTeacherMinMorningsPerWeek>\n";
36313 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
36314 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
36315 	s+="	<Minimum_Mornings_Per_Week>"+CustomFETString::number(this->minMorningsPerWeek)+"</Minimum_Mornings_Per_Week>\n";
36316 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
36317 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
36318 	s+="</ConstraintTeacherMinMorningsPerWeek>\n";
36319 	return s;
36320 }
36321 
getDescription(Rules & r)36322 QString ConstraintTeacherMinMorningsPerWeek::getDescription(Rules& r){
36323 	Q_UNUSED(r);
36324 
36325 	QString begin=QString("");
36326 	if(!active)
36327 		begin="X - ";
36328 
36329 	QString end=QString("");
36330 	if(!comments.isEmpty())
36331 		end=", "+tr("C: %1", "Comments").arg(comments);
36332 
36333 	QString s;
36334 	s+=tr("Teacher min mornings per week");s+=", ";
36335 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
36336 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
36337 	s+=tr("mM:%1", "Minimum mornings per week").arg(this->minMorningsPerWeek);
36338 
36339 	return begin+s+end;
36340 }
36341 
getDetailedDescription(Rules & r)36342 QString ConstraintTeacherMinMorningsPerWeek::getDetailedDescription(Rules& r){
36343 	Q_UNUSED(r);
36344 
36345 	QString s=tr("Time constraint");s+="\n";
36346 	s+=tr("A teacher must respect the minimum number of mornings per week");s+="\n";
36347 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
36348 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
36349 	s+=tr("Minimum mornings per week=%1").arg(this->minMorningsPerWeek);s+="\n";
36350 
36351 	if(!active){
36352 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
36353 		s+="\n";
36354 	}
36355 	if(!comments.isEmpty()){
36356 		s+=tr("Comments=%1").arg(comments);
36357 		s+="\n";
36358 	}
36359 
36360 	return s;
36361 }
36362 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)36363 double ConstraintTeacherMinMorningsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
36364 {
36365 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
36366 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
36367 		c.teachersMatrixReady=true;
36368 		c.subgroupsMatrixReady=true;
36369 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
36370 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
36371 
36372 		c.changedForMatrixCalculation=false;
36373 	}
36374 
36375 	int nbroken;
36376 
36377 	nbroken=0;
36378 	int i=this->teacher_ID;
36379 	int nd=0;
36380 	for(int d=0; d<r.nDaysPerWeek; d++)if((d%2)==0){
36381 		for(int h=0; h<r.nHoursPerDay; h++){
36382 			if(teachersMatrix[i][d][h]>0){
36383 				nd++;
36384 				break;
36385 			}
36386 		}
36387 	}
36388 
36389 	if(nd<this->minMorningsPerWeek){
36390 		nbroken+=this->minMorningsPerWeek-nd;
36391 
36392 		if(conflictsString!=nullptr){
36393 			QString s=(tr(
36394 			 "Time constraint teacher min %1 mornings per week broken for teacher %2.")
36395 			 .arg(CustomFETString::number(this->minMorningsPerWeek))
36396 			 .arg(r.internalTeachersList[i]->name)
36397 			 )
36398 			 +" "
36399 			 +
36400 			 tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(double(nbroken)*weightPercentage/100));
36401 
36402 			dl.append(s);
36403 			cl.append(double(nbroken)*weightPercentage/100);
36404 
36405 			*conflictsString+= s+"\n";
36406 		}
36407 	}
36408 
36409 	if(c.nPlacedActivities==r.nInternalActivities)
36410 		if(weightPercentage==100)
36411 			assert(nbroken==0);
36412 
36413 	return weightPercentage/100 * nbroken;
36414 }
36415 
isRelatedToActivity(Rules & r,Activity * a)36416 bool ConstraintTeacherMinMorningsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
36417 {
36418 	Q_UNUSED(r);
36419 	Q_UNUSED(a);
36420 
36421 	return false;
36422 }
36423 
isRelatedToTeacher(Teacher * t)36424 bool ConstraintTeacherMinMorningsPerWeek::isRelatedToTeacher(Teacher* t)
36425 {
36426 	if(this->teacherName==t->name)
36427 		return true;
36428 	return false;
36429 }
36430 
isRelatedToSubject(Subject * s)36431 bool ConstraintTeacherMinMorningsPerWeek::isRelatedToSubject(Subject* s)
36432 {
36433 	Q_UNUSED(s);
36434 
36435 	return false;
36436 }
36437 
isRelatedToActivityTag(ActivityTag * s)36438 bool ConstraintTeacherMinMorningsPerWeek::isRelatedToActivityTag(ActivityTag* s)
36439 {
36440 	Q_UNUSED(s);
36441 
36442 	return false;
36443 }
36444 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)36445 bool ConstraintTeacherMinMorningsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
36446 {
36447 	Q_UNUSED(r);
36448 	Q_UNUSED(s);
36449 
36450 	return false;
36451 }
36452 
hasWrongDayOrHour(Rules & r)36453 bool ConstraintTeacherMinMorningsPerWeek::hasWrongDayOrHour(Rules& r)
36454 {
36455 	if(minMorningsPerWeek>r.nDaysPerWeek/2)
36456 		return true;
36457 
36458 	return false;
36459 }
36460 
canRepairWrongDayOrHour(Rules & r)36461 bool ConstraintTeacherMinMorningsPerWeek::canRepairWrongDayOrHour(Rules& r)
36462 {
36463 	assert(hasWrongDayOrHour(r));
36464 
36465 	return true;
36466 }
36467 
repairWrongDayOrHour(Rules & r)36468 bool ConstraintTeacherMinMorningsPerWeek::repairWrongDayOrHour(Rules& r)
36469 {
36470 	assert(hasWrongDayOrHour(r));
36471 
36472 	if(minMorningsPerWeek>r.nDaysPerWeek/2)
36473 		minMorningsPerWeek=r.nDaysPerWeek/2;
36474 
36475 	return true;
36476 }
36477 
36478 ///////////////////////////////////////////////////////////////////////////////////////////
36479 ///////////////////////////////////////////////////////////////////////////////////////////
36480 
ConstraintTeachersMinMorningsPerWeek()36481 ConstraintTeachersMinMorningsPerWeek::ConstraintTeachersMinMorningsPerWeek()
36482 	: TimeConstraint()
36483 {
36484 	this->type=CONSTRAINT_TEACHERS_MIN_MORNINGS_PER_WEEK;
36485 }
36486 
ConstraintTeachersMinMorningsPerWeek(double wp,int minmornings)36487 ConstraintTeachersMinMorningsPerWeek::ConstraintTeachersMinMorningsPerWeek(double wp, int minmornings)
36488  : TimeConstraint(wp)
36489  {
36490 	assert(minmornings>0);
36491 	this->minMorningsPerWeek=minmornings;
36492 
36493 	this->type=CONSTRAINT_TEACHERS_MIN_MORNINGS_PER_WEEK;
36494 }
36495 
computeInternalStructure(QWidget * parent,Rules & r)36496 bool ConstraintTeachersMinMorningsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
36497 {
36498 	Q_UNUSED(parent);
36499 	Q_UNUSED(r);
36500 
36501 	return true;
36502 }
36503 
hasInactiveActivities(Rules & r)36504 bool ConstraintTeachersMinMorningsPerWeek::hasInactiveActivities(Rules& r)
36505 {
36506 	Q_UNUSED(r);
36507 	return false;
36508 }
36509 
getXmlDescription(Rules & r)36510 QString ConstraintTeachersMinMorningsPerWeek::getXmlDescription(Rules& r){
36511 	Q_UNUSED(r);
36512 
36513 	QString s="<ConstraintTeachersMinMorningsPerWeek>\n";
36514 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
36515 	s+="	<Minimum_Mornings_Per_Week>"+CustomFETString::number(this->minMorningsPerWeek)+"</Minimum_Mornings_Per_Week>\n";
36516 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
36517 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
36518 	s+="</ConstraintTeachersMinMorningsPerWeek>\n";
36519 	return s;
36520 }
36521 
getDescription(Rules & r)36522 QString ConstraintTeachersMinMorningsPerWeek::getDescription(Rules& r){
36523 	Q_UNUSED(r);
36524 
36525 	QString begin=QString("");
36526 	if(!active)
36527 		begin="X - ";
36528 
36529 	QString end=QString("");
36530 	if(!comments.isEmpty())
36531 		end=", "+tr("C: %1", "Comments").arg(comments);
36532 
36533 	QString s;
36534 	s+=tr("Teachers min mornings per week");s+=", ";
36535 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
36536 	s+=tr("mM:%1", "Minimum morning per week").arg(this->minMorningsPerWeek);
36537 
36538 	return begin+s+end;
36539 }
36540 
getDetailedDescription(Rules & r)36541 QString ConstraintTeachersMinMorningsPerWeek::getDetailedDescription(Rules& r){
36542 	Q_UNUSED(r);
36543 
36544 	QString s=tr("Time constraint");s+="\n";
36545 	s+=tr("All teachers must respect the minimum number of mornings per week");s+="\n";
36546 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
36547 	s+=tr("Minimum mornings per week=%1").arg(this->minMorningsPerWeek);s+="\n";
36548 
36549 	if(!active){
36550 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
36551 		s+="\n";
36552 	}
36553 	if(!comments.isEmpty()){
36554 		s+=tr("Comments=%1").arg(comments);
36555 		s+="\n";
36556 	}
36557 
36558 	return s;
36559 }
36560 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)36561 double ConstraintTeachersMinMorningsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
36562 {
36563 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
36564 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
36565 		c.teachersMatrixReady=true;
36566 		c.subgroupsMatrixReady=true;
36567 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
36568 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
36569 
36570 		c.changedForMatrixCalculation=false;
36571 	}
36572 
36573 	int nbrokentotal=0;
36574 	for(int i=0; i<r.nInternalTeachers; i++){
36575 		int nbroken;
36576 
36577 		nbroken=0;
36578 		//int i=this->teacher_ID;
36579 		int nd=0;
36580 		for(int d=0; d<r.nDaysPerWeek; d++)if((d%2)==0){
36581 			for(int h=0; h<r.nHoursPerDay; h++){
36582 				if(teachersMatrix[i][d][h]>0){
36583 					nd++;
36584 					break;
36585 				}
36586 			}
36587 		}
36588 
36589 		if(nd<this->minMorningsPerWeek){
36590 			nbroken+=this->minMorningsPerWeek-nd;
36591 			nbrokentotal+=nbroken;
36592 
36593 			if(conflictsString!=nullptr){
36594 				QString s=(tr(
36595 				 "Time constraint teachers min %1 mornings per week broken for teacher %2.")
36596 				 .arg(CustomFETString::number(this->minMorningsPerWeek))
36597 				 .arg(r.internalTeachersList[i]->name)
36598 				 )
36599 				 +" "
36600 				 +
36601 				 tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(double(nbroken)*weightPercentage/100));
36602 
36603 				dl.append(s);
36604 				cl.append(double(nbroken)*weightPercentage/100);
36605 
36606 				*conflictsString+= s+"\n";
36607 			}
36608 		}
36609 	}
36610 
36611 	if(c.nPlacedActivities==r.nInternalActivities)
36612 		if(weightPercentage==100)
36613 			assert(nbrokentotal==0);
36614 
36615 	return weightPercentage/100 * nbrokentotal;
36616 }
36617 
isRelatedToActivity(Rules & r,Activity * a)36618 bool ConstraintTeachersMinMorningsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
36619 {
36620 	Q_UNUSED(r);
36621 	Q_UNUSED(a);
36622 
36623 	return false;
36624 }
36625 
isRelatedToTeacher(Teacher * t)36626 bool ConstraintTeachersMinMorningsPerWeek::isRelatedToTeacher(Teacher* t)
36627 {
36628 	Q_UNUSED(t);
36629 	return true;
36630 }
36631 
isRelatedToSubject(Subject * s)36632 bool ConstraintTeachersMinMorningsPerWeek::isRelatedToSubject(Subject* s)
36633 {
36634 	Q_UNUSED(s);
36635 
36636 	return false;
36637 }
36638 
isRelatedToActivityTag(ActivityTag * s)36639 bool ConstraintTeachersMinMorningsPerWeek::isRelatedToActivityTag(ActivityTag* s)
36640 {
36641 	Q_UNUSED(s);
36642 
36643 	return false;
36644 }
36645 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)36646 bool ConstraintTeachersMinMorningsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
36647 {
36648 	Q_UNUSED(r);
36649 	Q_UNUSED(s);
36650 
36651 	return false;
36652 }
36653 
hasWrongDayOrHour(Rules & r)36654 bool ConstraintTeachersMinMorningsPerWeek::hasWrongDayOrHour(Rules& r)
36655 {
36656 	if(minMorningsPerWeek>r.nDaysPerWeek/2)
36657 		return true;
36658 
36659 	return false;
36660 }
36661 
canRepairWrongDayOrHour(Rules & r)36662 bool ConstraintTeachersMinMorningsPerWeek::canRepairWrongDayOrHour(Rules& r)
36663 {
36664 	assert(hasWrongDayOrHour(r));
36665 
36666 	return true;
36667 }
36668 
repairWrongDayOrHour(Rules & r)36669 bool ConstraintTeachersMinMorningsPerWeek::repairWrongDayOrHour(Rules& r)
36670 {
36671 	assert(hasWrongDayOrHour(r));
36672 
36673 	if(minMorningsPerWeek>r.nDaysPerWeek/2)
36674 		minMorningsPerWeek=r.nDaysPerWeek/2;
36675 
36676 	return true;
36677 }
36678 
36679 ///////////////////////////////////////////////////////////////////////////////////////////
36680 ///////////////////////////////////////////////////////////////////////////////////////////
36681 
ConstraintTeacherMinAfternoonsPerWeek()36682 ConstraintTeacherMinAfternoonsPerWeek::ConstraintTeacherMinAfternoonsPerWeek()
36683 	: TimeConstraint()
36684 {
36685 	this->type=CONSTRAINT_TEACHER_MIN_AFTERNOONS_PER_WEEK;
36686 }
36687 
ConstraintTeacherMinAfternoonsPerWeek(double wp,int minafternoons,const QString & teacher)36688 ConstraintTeacherMinAfternoonsPerWeek::ConstraintTeacherMinAfternoonsPerWeek(double wp, int minafternoons, const QString& teacher)
36689  : TimeConstraint(wp)
36690  {
36691 	assert(minafternoons>0);
36692 	this->minAfternoonsPerWeek=minafternoons;
36693 	this->teacherName=teacher;
36694 
36695 	this->type=CONSTRAINT_TEACHER_MIN_AFTERNOONS_PER_WEEK;
36696 }
36697 
computeInternalStructure(QWidget * parent,Rules & r)36698 bool ConstraintTeacherMinAfternoonsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
36699 {
36700 	Q_UNUSED(parent);
36701 
36702 	//this->teacher_ID=r.searchTeacher(this->teacherName);
36703 	teacher_ID=r.teachersHash.value(teacherName, -1);
36704 	assert(this->teacher_ID>=0);
36705 	return true;
36706 }
36707 
hasInactiveActivities(Rules & r)36708 bool ConstraintTeacherMinAfternoonsPerWeek::hasInactiveActivities(Rules& r)
36709 {
36710 	Q_UNUSED(r);
36711 	return false;
36712 }
36713 
getXmlDescription(Rules & r)36714 QString ConstraintTeacherMinAfternoonsPerWeek::getXmlDescription(Rules& r){
36715 	Q_UNUSED(r);
36716 
36717 	QString s="<ConstraintTeacherMinAfternoonsPerWeek>\n";
36718 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
36719 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
36720 	s+="	<Minimum_Afternoons_Per_Week>"+CustomFETString::number(this->minAfternoonsPerWeek)+"</Minimum_Afternoons_Per_Week>\n";
36721 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
36722 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
36723 	s+="</ConstraintTeacherMinAfternoonsPerWeek>\n";
36724 	return s;
36725 }
36726 
getDescription(Rules & r)36727 QString ConstraintTeacherMinAfternoonsPerWeek::getDescription(Rules& r){
36728 	Q_UNUSED(r);
36729 
36730 	QString begin=QString("");
36731 	if(!active)
36732 		begin="X - ";
36733 
36734 	QString end=QString("");
36735 	if(!comments.isEmpty())
36736 		end=", "+tr("C: %1", "Comments").arg(comments);
36737 
36738 	QString s;
36739 	s+=tr("Teacher min afternoons per week");s+=", ";
36740 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
36741 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
36742 	s+=tr("mA:%1", "Minimum afternoons per week").arg(this->minAfternoonsPerWeek);
36743 
36744 	return begin+s+end;
36745 }
36746 
getDetailedDescription(Rules & r)36747 QString ConstraintTeacherMinAfternoonsPerWeek::getDetailedDescription(Rules& r){
36748 	Q_UNUSED(r);
36749 
36750 	QString s=tr("Time constraint");s+="\n";
36751 	s+=tr("A teacher must respect the minimum number of afternoons per week");s+="\n";
36752 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
36753 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
36754 	s+=tr("Minimum afternoons per week=%1").arg(this->minAfternoonsPerWeek);s+="\n";
36755 
36756 	if(!active){
36757 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
36758 		s+="\n";
36759 	}
36760 	if(!comments.isEmpty()){
36761 		s+=tr("Comments=%1").arg(comments);
36762 		s+="\n";
36763 	}
36764 
36765 	return s;
36766 }
36767 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)36768 double ConstraintTeacherMinAfternoonsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
36769 {
36770 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
36771 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
36772 		c.teachersMatrixReady=true;
36773 		c.subgroupsMatrixReady=true;
36774 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
36775 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
36776 
36777 		c.changedForMatrixCalculation=false;
36778 	}
36779 
36780 	int nbroken;
36781 
36782 	nbroken=0;
36783 	int i=this->teacher_ID;
36784 	int nd=0;
36785 	for(int d=0; d<r.nDaysPerWeek; d++)if((d%2)==1){
36786 		for(int h=0; h<r.nHoursPerDay; h++){
36787 			if(teachersMatrix[i][d][h]>0){
36788 				nd++;
36789 				break;
36790 			}
36791 		}
36792 	}
36793 
36794 	if(nd<this->minAfternoonsPerWeek){
36795 		nbroken+=this->minAfternoonsPerWeek-nd;
36796 
36797 		if(conflictsString!=nullptr){
36798 			QString s=(tr(
36799 			 "Time constraint teacher min %1 afternoons per week broken for teacher %2.")
36800 			 .arg(CustomFETString::number(this->minAfternoonsPerWeek))
36801 			 .arg(r.internalTeachersList[i]->name)
36802 			 )
36803 			 +" "
36804 			 +
36805 			 tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(double(nbroken)*weightPercentage/100));
36806 
36807 			dl.append(s);
36808 			cl.append(double(nbroken)*weightPercentage/100);
36809 
36810 			*conflictsString+= s+"\n";
36811 		}
36812 	}
36813 
36814 	if(c.nPlacedActivities==r.nInternalActivities)
36815 		if(weightPercentage==100)
36816 			assert(nbroken==0);
36817 
36818 	return weightPercentage/100 * nbroken;
36819 }
36820 
isRelatedToActivity(Rules & r,Activity * a)36821 bool ConstraintTeacherMinAfternoonsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
36822 {
36823 	Q_UNUSED(r);
36824 	Q_UNUSED(a);
36825 
36826 	return false;
36827 }
36828 
isRelatedToTeacher(Teacher * t)36829 bool ConstraintTeacherMinAfternoonsPerWeek::isRelatedToTeacher(Teacher* t)
36830 {
36831 	if(this->teacherName==t->name)
36832 		return true;
36833 	return false;
36834 }
36835 
isRelatedToSubject(Subject * s)36836 bool ConstraintTeacherMinAfternoonsPerWeek::isRelatedToSubject(Subject* s)
36837 {
36838 	Q_UNUSED(s);
36839 
36840 	return false;
36841 }
36842 
isRelatedToActivityTag(ActivityTag * s)36843 bool ConstraintTeacherMinAfternoonsPerWeek::isRelatedToActivityTag(ActivityTag* s)
36844 {
36845 	Q_UNUSED(s);
36846 
36847 	return false;
36848 }
36849 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)36850 bool ConstraintTeacherMinAfternoonsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
36851 {
36852 	Q_UNUSED(r);
36853 	Q_UNUSED(s);
36854 
36855 	return false;
36856 }
36857 
hasWrongDayOrHour(Rules & r)36858 bool ConstraintTeacherMinAfternoonsPerWeek::hasWrongDayOrHour(Rules& r)
36859 {
36860 	if(minAfternoonsPerWeek>r.nDaysPerWeek/2)
36861 		return true;
36862 
36863 	return false;
36864 }
36865 
canRepairWrongDayOrHour(Rules & r)36866 bool ConstraintTeacherMinAfternoonsPerWeek::canRepairWrongDayOrHour(Rules& r)
36867 {
36868 	assert(hasWrongDayOrHour(r));
36869 
36870 	return true;
36871 }
36872 
repairWrongDayOrHour(Rules & r)36873 bool ConstraintTeacherMinAfternoonsPerWeek::repairWrongDayOrHour(Rules& r)
36874 {
36875 	assert(hasWrongDayOrHour(r));
36876 
36877 	if(minAfternoonsPerWeek>r.nDaysPerWeek/2)
36878 		minAfternoonsPerWeek=r.nDaysPerWeek/2;
36879 
36880 	return true;
36881 }
36882 
36883 ///////////////////////////////////////////////////////////////////////////////////////////
36884 ///////////////////////////////////////////////////////////////////////////////////////////
36885 
ConstraintTeachersMinAfternoonsPerWeek()36886 ConstraintTeachersMinAfternoonsPerWeek::ConstraintTeachersMinAfternoonsPerWeek()
36887 	: TimeConstraint()
36888 {
36889 	this->type=CONSTRAINT_TEACHERS_MIN_AFTERNOONS_PER_WEEK;
36890 }
36891 
ConstraintTeachersMinAfternoonsPerWeek(double wp,int minafternoons)36892 ConstraintTeachersMinAfternoonsPerWeek::ConstraintTeachersMinAfternoonsPerWeek(double wp, int minafternoons)
36893  : TimeConstraint(wp)
36894  {
36895 	assert(minafternoons>0);
36896 	this->minAfternoonsPerWeek=minafternoons;
36897 
36898 	this->type=CONSTRAINT_TEACHERS_MIN_AFTERNOONS_PER_WEEK;
36899 }
36900 
computeInternalStructure(QWidget * parent,Rules & r)36901 bool ConstraintTeachersMinAfternoonsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
36902 {
36903 	Q_UNUSED(parent);
36904 	Q_UNUSED(r);
36905 
36906 	return true;
36907 }
36908 
hasInactiveActivities(Rules & r)36909 bool ConstraintTeachersMinAfternoonsPerWeek::hasInactiveActivities(Rules& r)
36910 {
36911 	Q_UNUSED(r);
36912 	return false;
36913 }
36914 
getXmlDescription(Rules & r)36915 QString ConstraintTeachersMinAfternoonsPerWeek::getXmlDescription(Rules& r){
36916 	Q_UNUSED(r);
36917 
36918 	QString s="<ConstraintTeachersMinAfternoonsPerWeek>\n";
36919 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
36920 	s+="	<Minimum_Afternoons_Per_Week>"+CustomFETString::number(this->minAfternoonsPerWeek)+"</Minimum_Afternoons_Per_Week>\n";
36921 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
36922 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
36923 	s+="</ConstraintTeachersMinAfternoonsPerWeek>\n";
36924 	return s;
36925 }
36926 
getDescription(Rules & r)36927 QString ConstraintTeachersMinAfternoonsPerWeek::getDescription(Rules& r){
36928 	Q_UNUSED(r);
36929 
36930 	QString begin=QString("");
36931 	if(!active)
36932 		begin="X - ";
36933 
36934 	QString end=QString("");
36935 	if(!comments.isEmpty())
36936 		end=", "+tr("C: %1", "Comments").arg(comments);
36937 
36938 	QString s;
36939 	s+=tr("Teachers min afternoons per week");s+=", ";
36940 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
36941 	s+=tr("mA:%1", "Minimum afternoons per week").arg(this->minAfternoonsPerWeek);
36942 
36943 	return begin+s+end;
36944 }
36945 
getDetailedDescription(Rules & r)36946 QString ConstraintTeachersMinAfternoonsPerWeek::getDetailedDescription(Rules& r){
36947 	Q_UNUSED(r);
36948 
36949 	QString s=tr("Time constraint");s+="\n";
36950 	s+=tr("All teachers must respect the minimum number of afternoons per week");s+="\n";
36951 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
36952 	s+=tr("Minimum afternoons per week=%1").arg(this->minAfternoonsPerWeek);s+="\n";
36953 
36954 	if(!active){
36955 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
36956 		s+="\n";
36957 	}
36958 	if(!comments.isEmpty()){
36959 		s+=tr("Comments=%1").arg(comments);
36960 		s+="\n";
36961 	}
36962 
36963 	return s;
36964 }
36965 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)36966 double ConstraintTeachersMinAfternoonsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
36967 {
36968 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
36969 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
36970 		c.teachersMatrixReady=true;
36971 		c.subgroupsMatrixReady=true;
36972 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
36973 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
36974 
36975 		c.changedForMatrixCalculation=false;
36976 	}
36977 
36978 	int nbrokentotal=0;
36979 	for(int i=0; i<r.nInternalTeachers; i++){
36980 		int nbroken;
36981 
36982 		nbroken=0;
36983 		//int i=this->teacher_ID;
36984 		int nd=0;
36985 		for(int d=0; d<r.nDaysPerWeek; d++)if((d%2)==1){
36986 			for(int h=0; h<r.nHoursPerDay; h++){
36987 				if(teachersMatrix[i][d][h]>0){
36988 					nd++;
36989 					break;
36990 				}
36991 			}
36992 		}
36993 
36994 		if(nd<this->minAfternoonsPerWeek){
36995 			nbroken+=this->minAfternoonsPerWeek-nd;
36996 			nbrokentotal+=nbroken;
36997 
36998 			if(conflictsString!=nullptr){
36999 				QString s=(tr(
37000 				 "Time constraint teachers min %1 afternoons per week broken for teacher %2.")
37001 				 .arg(CustomFETString::number(this->minAfternoonsPerWeek))
37002 				 .arg(r.internalTeachersList[i]->name)
37003 				 )
37004 				 +" "
37005 				 +
37006 				 tr("This increases the conflicts total by %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(double(nbroken)*weightPercentage/100));
37007 
37008 				dl.append(s);
37009 				cl.append(double(nbroken)*weightPercentage/100);
37010 
37011 				*conflictsString+= s+"\n";
37012 			}
37013 		}
37014 	}
37015 
37016 	if(c.nPlacedActivities==r.nInternalActivities)
37017 		if(weightPercentage==100)
37018 			assert(nbrokentotal==0);
37019 
37020 	return weightPercentage/100 * nbrokentotal;
37021 }
37022 
isRelatedToActivity(Rules & r,Activity * a)37023 bool ConstraintTeachersMinAfternoonsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
37024 {
37025 	Q_UNUSED(r);
37026 	Q_UNUSED(a);
37027 
37028 	return false;
37029 }
37030 
isRelatedToTeacher(Teacher * t)37031 bool ConstraintTeachersMinAfternoonsPerWeek::isRelatedToTeacher(Teacher* t)
37032 {
37033 	Q_UNUSED(t);
37034 	return true;
37035 }
37036 
isRelatedToSubject(Subject * s)37037 bool ConstraintTeachersMinAfternoonsPerWeek::isRelatedToSubject(Subject* s)
37038 {
37039 	Q_UNUSED(s);
37040 
37041 	return false;
37042 }
37043 
isRelatedToActivityTag(ActivityTag * s)37044 bool ConstraintTeachersMinAfternoonsPerWeek::isRelatedToActivityTag(ActivityTag* s)
37045 {
37046 	Q_UNUSED(s);
37047 
37048 	return false;
37049 }
37050 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)37051 bool ConstraintTeachersMinAfternoonsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
37052 {
37053 	Q_UNUSED(r);
37054 	Q_UNUSED(s);
37055 
37056 	return false;
37057 }
37058 
hasWrongDayOrHour(Rules & r)37059 bool ConstraintTeachersMinAfternoonsPerWeek::hasWrongDayOrHour(Rules& r)
37060 {
37061 	if(minAfternoonsPerWeek>r.nDaysPerWeek/2)
37062 		return true;
37063 
37064 	return false;
37065 }
37066 
canRepairWrongDayOrHour(Rules & r)37067 bool ConstraintTeachersMinAfternoonsPerWeek::canRepairWrongDayOrHour(Rules& r)
37068 {
37069 	assert(hasWrongDayOrHour(r));
37070 
37071 	return true;
37072 }
37073 
repairWrongDayOrHour(Rules & r)37074 bool ConstraintTeachersMinAfternoonsPerWeek::repairWrongDayOrHour(Rules& r)
37075 {
37076 	assert(hasWrongDayOrHour(r));
37077 
37078 	if(minAfternoonsPerWeek>r.nDaysPerWeek/2)
37079 		minAfternoonsPerWeek=r.nDaysPerWeek/2;
37080 
37081 	return true;
37082 }
37083 
37084 ///////////////////////////////////////////////////////////////////////////////////////////
37085 ///////////////////////////////////////////////////////////////////////////////////////////
37086 
ConstraintTeacherMaxTwoConsecutiveMornings()37087 ConstraintTeacherMaxTwoConsecutiveMornings::ConstraintTeacherMaxTwoConsecutiveMornings()
37088 	: TimeConstraint()
37089 {
37090 	this->type=CONSTRAINT_TEACHER_MAX_TWO_CONSECUTIVE_MORNINGS;
37091 }
37092 
ConstraintTeacherMaxTwoConsecutiveMornings(double wp,QString tn)37093 ConstraintTeacherMaxTwoConsecutiveMornings::ConstraintTeacherMaxTwoConsecutiveMornings(double wp, QString tn)
37094 	 : TimeConstraint(wp)
37095 {
37096 	this->teacherName = tn;
37097 	this->type=CONSTRAINT_TEACHER_MAX_TWO_CONSECUTIVE_MORNINGS;
37098 }
37099 
computeInternalStructure(QWidget * parent,Rules & r)37100 bool ConstraintTeacherMaxTwoConsecutiveMornings::computeInternalStructure(QWidget* parent, Rules& r)
37101 {
37102 	Q_UNUSED(parent);
37103 
37104 	//this->teacher_ID=r.searchTeacher(this->teacherName);
37105 	teacher_ID=r.teachersHash.value(teacherName, -1);
37106 	assert(this->teacher_ID>=0);
37107 	return true;
37108 }
37109 
hasInactiveActivities(Rules & r)37110 bool ConstraintTeacherMaxTwoConsecutiveMornings::hasInactiveActivities(Rules& r)
37111 {
37112 	Q_UNUSED(r);
37113 	return false;
37114 }
37115 
getXmlDescription(Rules & r)37116 QString ConstraintTeacherMaxTwoConsecutiveMornings::getXmlDescription(Rules& r)
37117 {
37118 	Q_UNUSED(r);
37119 
37120 	QString s="<ConstraintTeacherMaxTwoConsecutiveMornings>\n";
37121 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
37122 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
37123 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
37124 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
37125 	s+="</ConstraintTeacherMaxTwoConsecutiveMornings>\n";
37126 	return s;
37127 }
37128 
getDescription(Rules & r)37129 QString ConstraintTeacherMaxTwoConsecutiveMornings::getDescription(Rules& r){
37130 	Q_UNUSED(r);
37131 
37132 	QString begin=QString("");
37133 	if(!active)
37134 		begin="X - ";
37135 
37136 	QString end=QString("");
37137 	if(!comments.isEmpty())
37138 		end=", "+tr("C: %1", "Comments").arg(comments);
37139 
37140 	QString s=tr("Teacher max two consecutive mornings");s+=", ";
37141 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
37142 	s+=tr("T:%1", "Teacher").arg(this->teacherName);
37143 
37144 	return begin+s+end;
37145 }
37146 
getDetailedDescription(Rules & r)37147 QString ConstraintTeacherMaxTwoConsecutiveMornings::getDetailedDescription(Rules& r){
37148 	Q_UNUSED(r);
37149 
37150 	QString s=tr("Time constraint");s+="\n";
37151 	s+=tr("A teacher must respect maximum two consecutive mornings");s+="\n";
37152 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
37153 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
37154 
37155 	if(!active){
37156 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
37157 		s+="\n";
37158 	}
37159 	if(!comments.isEmpty()){
37160 		s+=tr("Comments=%1").arg(comments);
37161 		s+="\n";
37162 	}
37163 
37164 	return s;
37165 }
37166 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)37167 double ConstraintTeacherMaxTwoConsecutiveMornings::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
37168 {
37169 	Q_UNUSED(cl);
37170 	Q_UNUSED(dl);
37171 	Q_UNUSED(conflictsString);
37172 
37173 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
37174 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
37175 		c.teachersMatrixReady=true;
37176 		c.subgroupsMatrixReady=true;
37177 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
37178 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
37179 
37180 		c.changedForMatrixCalculation=false;
37181 	}
37182 
37183 	//int nbroken;
37184 
37185 	//without logging
37186 	/*
37187 	if(conflictsString==nullptr){
37188 		nbroken=0;
37189 		//count sort
37190 		int t=this->teacher_ID;
37191 		int nd[MAX_HOURS_PER_DAY + 1];
37192 		for(int h=0; h<=r.nHoursPerDay; h++)
37193 			nd[h]=0;
37194 		for(int d=0; d<r.nDaysPerWeek; d++){
37195 			int nh=0;
37196 			for(int h=0; h<r.nHoursPerDay; h++)
37197 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
37198 			nd[nh]++;
37199 		}
37200 		//return the minimum occupied days which do not respect this constraint
37201 		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
37202 		for(int k=0; k<=r.nHoursPerDay; k++){
37203 			if(nd[k]>0){
37204 				if(i>nd[k]){
37205 					i-=nd[k];
37206 					nbroken+=nd[k]*k;
37207 				}
37208 				else{
37209 					nbroken+=i*k;
37210 					break;
37211 				}
37212 			}
37213 		}
37214 	}
37215 	//with logging
37216 	else{*/
37217 	//	nbroken=0;
37218 		//count sort
37219 /*		int t=this->teacher_ID;
37220 		//int nd[2*MAX_HOURS_PER_DAY + 1];
37221 		int nOD=0; //n occupied days
37222 		//for(int h=0; h<=2*r.nHoursPerDay; h++)
37223 		//nd[h]=0;
37224 		for(int d=0; d<r.nDaysPerWeek; d+=2){
37225 			int nh=0;
37226 			for(int h=0; h<r.nHoursPerDay; h++)
37227 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
37228 			if(nh>0)
37229 				nOD++;
37230 			//nd[nh]++;
37231 		}*/
37232 
37233 		//return the minimum occupied days which do not respect this constraint
37234 /*		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
37235 		for(int k=0; k<=2*r.nHoursPerDay; k++){
37236 			if(nd[k]>0){
37237 				if(i>nd[k]){
37238 					i-=nd[k];
37239 					nbroken+=nd[k]*k;
37240 				}
37241 				else{
37242 					nbroken+=i*k;
37243 					break;
37244 				}
37245 			}
37246 		}*/
37247 
37248 /*		if(nOD>this->maxMorningsPerWeek)
37249 			nbroken=1;
37250 
37251 		if(nbroken>0 && conflictsString!=nullptr){
37252 			QString s= tr("Time constraint teacher max mornings per week broken for teacher: %1.")
37253 			 .arg(r.internalTeachersList[t]->name);
37254 			s += tr("This increases the conflicts total by %1")
37255 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
37256 
37257 			dl.append(s);
37258 			cl.append(nbroken*weightPercentage/100);
37259 
37260 			*conflictsString += s+"\n";
37261 		}*/
37262 	//}
37263 
37264 /*	if(weightPercentage==100)
37265 		assert(nbroken==0);
37266 	return weightPercentage/100 * nbroken;*/
37267 
37268 	int tch=this->teacher_ID;
37269 	Matrix1D<bool> occDay;
37270 	occDay.resize(r.nDaysPerWeek);
37271 //	for(int tch=0; tch<r.nInternalTeachers; tch++){
37272 		for(int d=0; d<r.nDaysPerWeek; d+=2){
37273 			occDay[d]=false;
37274 			for(int h=0; h<r.nHoursPerDay; h++){
37275 				if(teachersMatrix[tch][d][h]>0){
37276 					occDay[d]=true;
37277 					break;
37278 				}
37279 			}
37280 		}
37281 		for(int d=0; d+4<r.nDaysPerWeek; d+=2)
37282 			if(occDay[d] && occDay[d+2] && occDay[d+4])
37283 				assert(0);
37284 //	}
37285 
37286 	return 0.0;
37287 }
37288 
isRelatedToActivity(Rules & r,Activity * a)37289 bool ConstraintTeacherMaxTwoConsecutiveMornings::isRelatedToActivity(Rules& r, Activity* a)
37290 {
37291 	Q_UNUSED(r);
37292 	Q_UNUSED(a);
37293 
37294 	return false;
37295 }
37296 
isRelatedToTeacher(Teacher * t)37297 bool ConstraintTeacherMaxTwoConsecutiveMornings::isRelatedToTeacher(Teacher* t)
37298 {
37299 	if(this->teacherName==t->name)
37300 		return true;
37301 	return false;
37302 }
37303 
isRelatedToSubject(Subject * s)37304 bool ConstraintTeacherMaxTwoConsecutiveMornings::isRelatedToSubject(Subject* s)
37305 {
37306 	Q_UNUSED(s);
37307 
37308 	return false;
37309 }
37310 
isRelatedToActivityTag(ActivityTag * s)37311 bool ConstraintTeacherMaxTwoConsecutiveMornings::isRelatedToActivityTag(ActivityTag* s)
37312 {
37313 	Q_UNUSED(s);
37314 
37315 	return false;
37316 }
37317 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)37318 bool ConstraintTeacherMaxTwoConsecutiveMornings::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
37319 {
37320 	Q_UNUSED(r);
37321 	Q_UNUSED(s);
37322 
37323 	return false;
37324 }
37325 
hasWrongDayOrHour(Rules & r)37326 bool ConstraintTeacherMaxTwoConsecutiveMornings::hasWrongDayOrHour(Rules& r)
37327 {
37328 	Q_UNUSED(r);
37329 
37330 	return false;
37331 }
37332 
canRepairWrongDayOrHour(Rules & r)37333 bool ConstraintTeacherMaxTwoConsecutiveMornings::canRepairWrongDayOrHour(Rules& r)
37334 {
37335 	assert(hasWrongDayOrHour(r));
37336 
37337 	return true;
37338 }
37339 
repairWrongDayOrHour(Rules & r)37340 bool ConstraintTeacherMaxTwoConsecutiveMornings::repairWrongDayOrHour(Rules& r)
37341 {
37342 	assert(hasWrongDayOrHour(r));
37343 
37344 	return true;
37345 }
37346 
37347 ///////////////////////////////////////////////////////////////////////////////////////////
37348 ///////////////////////////////////////////////////////////////////////////////////////////
37349 
ConstraintTeachersMaxTwoConsecutiveMornings()37350 ConstraintTeachersMaxTwoConsecutiveMornings::ConstraintTeachersMaxTwoConsecutiveMornings()
37351 	: TimeConstraint()
37352 {
37353 	this->type=CONSTRAINT_TEACHERS_MAX_TWO_CONSECUTIVE_MORNINGS;
37354 }
37355 
ConstraintTeachersMaxTwoConsecutiveMornings(double wp)37356 ConstraintTeachersMaxTwoConsecutiveMornings::ConstraintTeachersMaxTwoConsecutiveMornings(double wp)
37357 	 : TimeConstraint(wp)
37358 {
37359 	this->type=CONSTRAINT_TEACHERS_MAX_TWO_CONSECUTIVE_MORNINGS;
37360 }
37361 
computeInternalStructure(QWidget * parent,Rules & r)37362 bool ConstraintTeachersMaxTwoConsecutiveMornings::computeInternalStructure(QWidget* parent, Rules& r)
37363 {
37364 	Q_UNUSED(parent);
37365 	Q_UNUSED(r);
37366 
37367 	//this->teacher_ID=r.searchTeacher(this->teacherName);
37368 	//teacher_ID=r.teachersHash.value(teacherName, -1);
37369 	//assert(this->teacher_ID>=0);
37370 	return true;
37371 }
37372 
hasInactiveActivities(Rules & r)37373 bool ConstraintTeachersMaxTwoConsecutiveMornings::hasInactiveActivities(Rules& r)
37374 {
37375 	Q_UNUSED(r);
37376 	return false;
37377 }
37378 
getXmlDescription(Rules & r)37379 QString ConstraintTeachersMaxTwoConsecutiveMornings::getXmlDescription(Rules& r)
37380 {
37381 	Q_UNUSED(r);
37382 
37383 	QString s="<ConstraintTeachersMaxTwoConsecutiveMornings>\n";
37384 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
37385 //	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
37386 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
37387 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
37388 	s+="</ConstraintTeachersMaxTwoConsecutiveMornings>\n";
37389 	return s;
37390 }
37391 
getDescription(Rules & r)37392 QString ConstraintTeachersMaxTwoConsecutiveMornings::getDescription(Rules& r){
37393 	Q_UNUSED(r);
37394 
37395 	QString begin=QString("");
37396 	if(!active)
37397 		begin="X - ";
37398 
37399 	QString end=QString("");
37400 	if(!comments.isEmpty())
37401 		end=", "+tr("C: %1", "Comments").arg(comments);
37402 
37403 	QString s=tr("Teachers max two consecutive mornings");s+=", ";
37404 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
37405 //	s+=tr("T:%1", "Teacher").arg(this->teacherName);
37406 
37407 	return begin+s+end;
37408 }
37409 
getDetailedDescription(Rules & r)37410 QString ConstraintTeachersMaxTwoConsecutiveMornings::getDetailedDescription(Rules& r){
37411 	Q_UNUSED(r);
37412 
37413 	QString s=tr("Time constraint");s+="\n";
37414 	s+=tr("All teachers must respect maximum two consecutive mornings");s+="\n";
37415 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
37416 //	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
37417 
37418 	if(!active){
37419 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
37420 		s+="\n";
37421 	}
37422 	if(!comments.isEmpty()){
37423 		s+=tr("Comments=%1").arg(comments);
37424 		s+="\n";
37425 	}
37426 
37427 	return s;
37428 }
37429 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)37430 double ConstraintTeachersMaxTwoConsecutiveMornings::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
37431 {
37432 	Q_UNUSED(cl);
37433 	Q_UNUSED(dl);
37434 	Q_UNUSED(conflictsString);
37435 
37436 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
37437 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
37438 		c.teachersMatrixReady=true;
37439 		c.subgroupsMatrixReady=true;
37440 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
37441 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
37442 
37443 		c.changedForMatrixCalculation=false;
37444 	}
37445 
37446 	//int nbroken;
37447 
37448 	//without logging
37449 	/*
37450 	if(conflictsString==nullptr){
37451 		nbroken=0;
37452 		//count sort
37453 		int t=this->teacher_ID;
37454 		int nd[MAX_HOURS_PER_DAY + 1];
37455 		for(int h=0; h<=r.nHoursPerDay; h++)
37456 			nd[h]=0;
37457 		for(int d=0; d<r.nDaysPerWeek; d++){
37458 			int nh=0;
37459 			for(int h=0; h<r.nHoursPerDay; h++)
37460 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
37461 			nd[nh]++;
37462 		}
37463 		//return the minimum occupied days which do not respect this constraint
37464 		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
37465 		for(int k=0; k<=r.nHoursPerDay; k++){
37466 			if(nd[k]>0){
37467 				if(i>nd[k]){
37468 					i-=nd[k];
37469 					nbroken+=nd[k]*k;
37470 				}
37471 				else{
37472 					nbroken+=i*k;
37473 					break;
37474 				}
37475 			}
37476 		}
37477 	}
37478 	//with logging
37479 	else{*/
37480 //		nbroken=0;
37481 		//count sort
37482 /*		int t=this->teacher_ID;
37483 		//int nd[2*MAX_HOURS_PER_DAY + 1];
37484 		int nOD=0; //n occupied days
37485 		//for(int h=0; h<=2*r.nHoursPerDay; h++)
37486 		//nd[h]=0;
37487 		for(int d=0; d<r.nDaysPerWeek; d+=2){
37488 			int nh=0;
37489 			for(int h=0; h<r.nHoursPerDay; h++)
37490 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
37491 			if(nh>0)
37492 				nOD++;
37493 			//nd[nh]++;
37494 		}*/
37495 
37496 		//return the minimum occupied days which do not respect this constraint
37497 /*		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
37498 		for(int k=0; k<=2*r.nHoursPerDay; k++){
37499 			if(nd[k]>0){
37500 				if(i>nd[k]){
37501 					i-=nd[k];
37502 					nbroken+=nd[k]*k;
37503 				}
37504 				else{
37505 					nbroken+=i*k;
37506 					break;
37507 				}
37508 			}
37509 		}*/
37510 
37511 /*		if(nOD>this->maxMorningsPerWeek)
37512 			nbroken=1;
37513 
37514 		if(nbroken>0 && conflictsString!=nullptr){
37515 			QString s= tr("Time constraint teacher max mornings per week broken for teacher: %1.")
37516 			 .arg(r.internalTeachersList[t]->name);
37517 			s += tr("This increases the conflicts total by %1")
37518 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
37519 
37520 			dl.append(s);
37521 			cl.append(nbroken*weightPercentage/100);
37522 
37523 			*conflictsString += s+"\n";
37524 		}*/
37525 	//}
37526 
37527 /*	if(weightPercentage==100)
37528 		assert(nbroken==0);
37529 	return weightPercentage/100 * nbroken;*/
37530 
37531 	Matrix1D<bool> occDay;
37532 	occDay.resize(r.nDaysPerWeek);
37533 	for(int tch=0; tch<r.nInternalTeachers; tch++){
37534 		for(int d=0; d<r.nDaysPerWeek; d+=2){
37535 			occDay[d]=false;
37536 			for(int h=0; h<r.nHoursPerDay; h++){
37537 				if(teachersMatrix[tch][d][h]>0){
37538 					occDay[d]=true;
37539 					break;
37540 				}
37541 			}
37542 		}
37543 		for(int d=0; d+4<r.nDaysPerWeek; d+=2)
37544 			if(occDay[d] && occDay[d+2] && occDay[d+4])
37545 				assert(0);
37546 	}
37547 
37548 	return 0.0;
37549 }
37550 
isRelatedToActivity(Rules & r,Activity * a)37551 bool ConstraintTeachersMaxTwoConsecutiveMornings::isRelatedToActivity(Rules& r, Activity* a)
37552 {
37553 	Q_UNUSED(r);
37554 	Q_UNUSED(a);
37555 
37556 	return false;
37557 }
37558 
isRelatedToTeacher(Teacher * t)37559 bool ConstraintTeachersMaxTwoConsecutiveMornings::isRelatedToTeacher(Teacher* t)
37560 {
37561 	Q_UNUSED(t);
37562 	//if(this->teacherName==t->name)
37563 	//	return true;
37564 	//return false;
37565 	return true;
37566 }
37567 
isRelatedToSubject(Subject * s)37568 bool ConstraintTeachersMaxTwoConsecutiveMornings::isRelatedToSubject(Subject* s)
37569 {
37570 	Q_UNUSED(s);
37571 
37572 	return false;
37573 }
37574 
isRelatedToActivityTag(ActivityTag * s)37575 bool ConstraintTeachersMaxTwoConsecutiveMornings::isRelatedToActivityTag(ActivityTag* s)
37576 {
37577 	Q_UNUSED(s);
37578 
37579 	return false;
37580 }
37581 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)37582 bool ConstraintTeachersMaxTwoConsecutiveMornings::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
37583 {
37584 	Q_UNUSED(r);
37585 	Q_UNUSED(s);
37586 
37587 	return false;
37588 }
37589 
hasWrongDayOrHour(Rules & r)37590 bool ConstraintTeachersMaxTwoConsecutiveMornings::hasWrongDayOrHour(Rules& r)
37591 {
37592 	Q_UNUSED(r);
37593 
37594 	return false;
37595 }
37596 
canRepairWrongDayOrHour(Rules & r)37597 bool ConstraintTeachersMaxTwoConsecutiveMornings::canRepairWrongDayOrHour(Rules& r)
37598 {
37599 	assert(hasWrongDayOrHour(r));
37600 
37601 	return true;
37602 }
37603 
repairWrongDayOrHour(Rules & r)37604 bool ConstraintTeachersMaxTwoConsecutiveMornings::repairWrongDayOrHour(Rules& r)
37605 {
37606 	assert(hasWrongDayOrHour(r));
37607 
37608 	return true;
37609 }
37610 
37611 ///////////////////////////////////////////////////////////////////////////////////////////
37612 ///////////////////////////////////////////////////////////////////////////////////////////
37613 
ConstraintTeacherMaxTwoConsecutiveAfternoons()37614 ConstraintTeacherMaxTwoConsecutiveAfternoons::ConstraintTeacherMaxTwoConsecutiveAfternoons()
37615 	: TimeConstraint()
37616 {
37617 	this->type=CONSTRAINT_TEACHER_MAX_TWO_CONSECUTIVE_AFTERNOONS;
37618 }
37619 
ConstraintTeacherMaxTwoConsecutiveAfternoons(double wp,QString tn)37620 ConstraintTeacherMaxTwoConsecutiveAfternoons::ConstraintTeacherMaxTwoConsecutiveAfternoons(double wp, QString tn)
37621 	 : TimeConstraint(wp)
37622 {
37623 	this->teacherName = tn;
37624 	this->type=CONSTRAINT_TEACHER_MAX_TWO_CONSECUTIVE_AFTERNOONS;
37625 }
37626 
computeInternalStructure(QWidget * parent,Rules & r)37627 bool ConstraintTeacherMaxTwoConsecutiveAfternoons::computeInternalStructure(QWidget* parent, Rules& r)
37628 {
37629 	Q_UNUSED(parent);
37630 
37631 	//this->teacher_ID=r.searchTeacher(this->teacherName);
37632 	teacher_ID=r.teachersHash.value(teacherName, -1);
37633 	assert(this->teacher_ID>=0);
37634 	return true;
37635 }
37636 
hasInactiveActivities(Rules & r)37637 bool ConstraintTeacherMaxTwoConsecutiveAfternoons::hasInactiveActivities(Rules& r)
37638 {
37639 	Q_UNUSED(r);
37640 	return false;
37641 }
37642 
getXmlDescription(Rules & r)37643 QString ConstraintTeacherMaxTwoConsecutiveAfternoons::getXmlDescription(Rules& r)
37644 {
37645 	Q_UNUSED(r);
37646 
37647 	QString s="<ConstraintTeacherMaxTwoConsecutiveAfternoons>\n";
37648 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
37649 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
37650 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
37651 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
37652 	s+="</ConstraintTeacherMaxTwoConsecutiveAfternoons>\n";
37653 	return s;
37654 }
37655 
getDescription(Rules & r)37656 QString ConstraintTeacherMaxTwoConsecutiveAfternoons::getDescription(Rules& r){
37657 	Q_UNUSED(r);
37658 
37659 	QString begin=QString("");
37660 	if(!active)
37661 		begin="X - ";
37662 
37663 	QString end=QString("");
37664 	if(!comments.isEmpty())
37665 		end=", "+tr("C: %1", "Comments").arg(comments);
37666 
37667 	QString s=tr("Teacher max two consecutive afternoons");s+=", ";
37668 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
37669 	s+=tr("T:%1", "Teacher").arg(this->teacherName);
37670 
37671 	return begin+s+end;
37672 }
37673 
getDetailedDescription(Rules & r)37674 QString ConstraintTeacherMaxTwoConsecutiveAfternoons::getDetailedDescription(Rules& r){
37675 	Q_UNUSED(r);
37676 
37677 	QString s=tr("Time constraint");s+="\n";
37678 	s+=tr("A teacher must respect maximum two consecutive afternoons");s+="\n";
37679 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
37680 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
37681 
37682 	if(!active){
37683 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
37684 		s+="\n";
37685 	}
37686 	if(!comments.isEmpty()){
37687 		s+=tr("Comments=%1").arg(comments);
37688 		s+="\n";
37689 	}
37690 
37691 	return s;
37692 }
37693 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)37694 double ConstraintTeacherMaxTwoConsecutiveAfternoons::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
37695 {
37696 	Q_UNUSED(cl);
37697 	Q_UNUSED(dl);
37698 	Q_UNUSED(conflictsString);
37699 
37700 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
37701 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
37702 		c.teachersMatrixReady=true;
37703 		c.subgroupsMatrixReady=true;
37704 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
37705 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
37706 
37707 		c.changedForMatrixCalculation=false;
37708 	}
37709 
37710 	//int nbroken;
37711 
37712 	//without logging
37713 	/*
37714 	if(conflictsString==nullptr){
37715 		nbroken=0;
37716 		//count sort
37717 		int t=this->teacher_ID;
37718 		int nd[MAX_HOURS_PER_DAY + 1];
37719 		for(int h=0; h<=r.nHoursPerDay; h++)
37720 			nd[h]=0;
37721 		for(int d=0; d<r.nDaysPerWeek; d++){
37722 			int nh=0;
37723 			for(int h=0; h<r.nHoursPerDay; h++)
37724 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
37725 			nd[nh]++;
37726 		}
37727 		//return the minimum occupied days which do not respect this constraint
37728 		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
37729 		for(int k=0; k<=r.nHoursPerDay; k++){
37730 			if(nd[k]>0){
37731 				if(i>nd[k]){
37732 					i-=nd[k];
37733 					nbroken+=nd[k]*k;
37734 				}
37735 				else{
37736 					nbroken+=i*k;
37737 					break;
37738 				}
37739 			}
37740 		}
37741 	}
37742 	//with logging
37743 	else{*/
37744 //		nbroken=0;
37745 		//count sort
37746 /*		int t=this->teacher_ID;
37747 		//int nd[2*MAX_HOURS_PER_DAY + 1];
37748 		int nOD=0; //n occupied days
37749 		//for(int h=0; h<=2*r.nHoursPerDay; h++)
37750 		//nd[h]=0;
37751 		for(int d=0; d<r.nDaysPerWeek; d+=2){
37752 			int nh=0;
37753 			for(int h=0; h<r.nHoursPerDay; h++)
37754 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
37755 			if(nh>0)
37756 				nOD++;
37757 			//nd[nh]++;
37758 		}*/
37759 
37760 		//return the minimum occupied days which do not respect this constraint
37761 /*		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
37762 		for(int k=0; k<=2*r.nHoursPerDay; k++){
37763 			if(nd[k]>0){
37764 				if(i>nd[k]){
37765 					i-=nd[k];
37766 					nbroken+=nd[k]*k;
37767 				}
37768 				else{
37769 					nbroken+=i*k;
37770 					break;
37771 				}
37772 			}
37773 		}*/
37774 
37775 /*		if(nOD>this->maxAfternoonsPerWeek)
37776 			nbroken=1;
37777 
37778 		if(nbroken>0 && conflictsString!=nullptr){
37779 			QString s= tr("Time constraint teacher max afternoons per week broken for teacher: %1.")
37780 			 .arg(r.internalTeachersList[t]->name);
37781 			s += tr("This increases the conflicts total by %1")
37782 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
37783 
37784 			dl.append(s);
37785 			cl.append(nbroken*weightPercentage/100);
37786 
37787 			*conflictsString += s+"\n";
37788 		}*/
37789 	//}
37790 
37791 /*	if(weightPercentage==100)
37792 		assert(nbroken==0);
37793 	return weightPercentage/100 * nbroken;*/
37794 
37795 	Matrix1D<bool> occDay;
37796 	occDay.resize(r.nDaysPerWeek);
37797 	int tch=this->teacher_ID;
37798 //	for(int tch=0; tch<r.nInternalTeachers; tch++){
37799 		for(int d=1; d<r.nDaysPerWeek; d+=2){
37800 			occDay[d]=false;
37801 			for(int h=0; h<r.nHoursPerDay; h++){
37802 				if(teachersMatrix[tch][d][h]>0){
37803 					occDay[d]=true;
37804 					break;
37805 				}
37806 			}
37807 		}
37808 		for(int d=1; d+4<r.nDaysPerWeek; d+=2)
37809 			if(occDay[d] && occDay[d+2] && occDay[d+4])
37810 				assert(0);
37811 //	}
37812 
37813 	return 0.0;
37814 }
37815 
isRelatedToActivity(Rules & r,Activity * a)37816 bool ConstraintTeacherMaxTwoConsecutiveAfternoons::isRelatedToActivity(Rules& r, Activity* a)
37817 {
37818 	Q_UNUSED(r);
37819 	Q_UNUSED(a);
37820 
37821 	return false;
37822 }
37823 
isRelatedToTeacher(Teacher * t)37824 bool ConstraintTeacherMaxTwoConsecutiveAfternoons::isRelatedToTeacher(Teacher* t)
37825 {
37826 	if(this->teacherName==t->name)
37827 		return true;
37828 	return false;
37829 }
37830 
isRelatedToSubject(Subject * s)37831 bool ConstraintTeacherMaxTwoConsecutiveAfternoons::isRelatedToSubject(Subject* s)
37832 {
37833 	Q_UNUSED(s);
37834 
37835 	return false;
37836 }
37837 
isRelatedToActivityTag(ActivityTag * s)37838 bool ConstraintTeacherMaxTwoConsecutiveAfternoons::isRelatedToActivityTag(ActivityTag* s)
37839 {
37840 	Q_UNUSED(s);
37841 
37842 	return false;
37843 }
37844 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)37845 bool ConstraintTeacherMaxTwoConsecutiveAfternoons::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
37846 {
37847 	Q_UNUSED(r);
37848 	Q_UNUSED(s);
37849 
37850 	return false;
37851 }
37852 
hasWrongDayOrHour(Rules & r)37853 bool ConstraintTeacherMaxTwoConsecutiveAfternoons::hasWrongDayOrHour(Rules& r)
37854 {
37855 	Q_UNUSED(r);
37856 
37857 	return false;
37858 }
37859 
canRepairWrongDayOrHour(Rules & r)37860 bool ConstraintTeacherMaxTwoConsecutiveAfternoons::canRepairWrongDayOrHour(Rules& r)
37861 {
37862 	assert(hasWrongDayOrHour(r));
37863 
37864 	return true;
37865 }
37866 
repairWrongDayOrHour(Rules & r)37867 bool ConstraintTeacherMaxTwoConsecutiveAfternoons::repairWrongDayOrHour(Rules& r)
37868 {
37869 	assert(hasWrongDayOrHour(r));
37870 
37871 	return true;
37872 }
37873 
37874 ///////////////////////////////////////////////////////////////////////////////////////////
37875 ///////////////////////////////////////////////////////////////////////////////////////////
37876 
ConstraintTeachersMaxTwoConsecutiveAfternoons()37877 ConstraintTeachersMaxTwoConsecutiveAfternoons::ConstraintTeachersMaxTwoConsecutiveAfternoons()
37878 	: TimeConstraint()
37879 {
37880 	this->type=CONSTRAINT_TEACHERS_MAX_TWO_CONSECUTIVE_AFTERNOONS;
37881 }
37882 
ConstraintTeachersMaxTwoConsecutiveAfternoons(double wp)37883 ConstraintTeachersMaxTwoConsecutiveAfternoons::ConstraintTeachersMaxTwoConsecutiveAfternoons(double wp)
37884 	 : TimeConstraint(wp)
37885 {
37886 	this->type=CONSTRAINT_TEACHERS_MAX_TWO_CONSECUTIVE_AFTERNOONS;
37887 }
37888 
computeInternalStructure(QWidget * parent,Rules & r)37889 bool ConstraintTeachersMaxTwoConsecutiveAfternoons::computeInternalStructure(QWidget* parent, Rules& r)
37890 {
37891 	Q_UNUSED(parent);
37892 	Q_UNUSED(r);
37893 
37894 	//this->teacher_ID=r.searchTeacher(this->teacherName);
37895 	//teacher_ID=r.teachersHash.value(teacherName, -1);
37896 	//assert(this->teacher_ID>=0);
37897 	return true;
37898 }
37899 
hasInactiveActivities(Rules & r)37900 bool ConstraintTeachersMaxTwoConsecutiveAfternoons::hasInactiveActivities(Rules& r)
37901 {
37902 	Q_UNUSED(r);
37903 	return false;
37904 }
37905 
getXmlDescription(Rules & r)37906 QString ConstraintTeachersMaxTwoConsecutiveAfternoons::getXmlDescription(Rules& r)
37907 {
37908 	Q_UNUSED(r);
37909 
37910 	QString s="<ConstraintTeachersMaxTwoConsecutiveAfternoons>\n";
37911 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
37912 //	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
37913 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
37914 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
37915 	s+="</ConstraintTeachersMaxTwoConsecutiveAfternoons>\n";
37916 	return s;
37917 }
37918 
getDescription(Rules & r)37919 QString ConstraintTeachersMaxTwoConsecutiveAfternoons::getDescription(Rules& r){
37920 	Q_UNUSED(r);
37921 
37922 	QString begin=QString("");
37923 	if(!active)
37924 		begin="X - ";
37925 
37926 	QString end=QString("");
37927 	if(!comments.isEmpty())
37928 		end=", "+tr("C: %1", "Comments").arg(comments);
37929 
37930 	QString s=tr("Teachers max two consecutive afternoons");s+=", ";
37931 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
37932 //	s+=tr("T:%1", "Teacher").arg(this->teacherName);
37933 
37934 	return begin+s+end;
37935 }
37936 
getDetailedDescription(Rules & r)37937 QString ConstraintTeachersMaxTwoConsecutiveAfternoons::getDetailedDescription(Rules& r){
37938 	Q_UNUSED(r);
37939 
37940 	QString s=tr("Time constraint");s+="\n";
37941 	s+=tr("All teachers must respect maximum two consecutive afternoons");s+="\n";
37942 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
37943 //	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
37944 
37945 	if(!active){
37946 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
37947 		s+="\n";
37948 	}
37949 	if(!comments.isEmpty()){
37950 		s+=tr("Comments=%1").arg(comments);
37951 		s+="\n";
37952 	}
37953 
37954 	return s;
37955 }
37956 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)37957 double ConstraintTeachersMaxTwoConsecutiveAfternoons::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
37958 {
37959 	Q_UNUSED(cl);
37960 	Q_UNUSED(dl);
37961 	Q_UNUSED(conflictsString);
37962 
37963 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
37964 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
37965 		c.teachersMatrixReady=true;
37966 		c.subgroupsMatrixReady=true;
37967 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
37968 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
37969 
37970 		c.changedForMatrixCalculation=false;
37971 	}
37972 
37973 //	int nbroken;
37974 
37975 	//without logging
37976 	/*
37977 	if(conflictsString==nullptr){
37978 		nbroken=0;
37979 		//count sort
37980 		int t=this->teacher_ID;
37981 		int nd[MAX_HOURS_PER_DAY + 1];
37982 		for(int h=0; h<=r.nHoursPerDay; h++)
37983 			nd[h]=0;
37984 		for(int d=0; d<r.nDaysPerWeek; d++){
37985 			int nh=0;
37986 			for(int h=0; h<r.nHoursPerDay; h++)
37987 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
37988 			nd[nh]++;
37989 		}
37990 		//return the minimum occupied days which do not respect this constraint
37991 		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
37992 		for(int k=0; k<=r.nHoursPerDay; k++){
37993 			if(nd[k]>0){
37994 				if(i>nd[k]){
37995 					i-=nd[k];
37996 					nbroken+=nd[k]*k;
37997 				}
37998 				else{
37999 					nbroken+=i*k;
38000 					break;
38001 				}
38002 			}
38003 		}
38004 	}
38005 	//with logging
38006 	else{*/
38007 //		nbroken=0;
38008 		//count sort
38009 /*		int t=this->teacher_ID;
38010 		//int nd[2*MAX_HOURS_PER_DAY + 1];
38011 		int nOD=0; //n occupied days
38012 		//for(int h=0; h<=2*r.nHoursPerDay; h++)
38013 		//nd[h]=0;
38014 		for(int d=0; d<r.nDaysPerWeek; d+=2){
38015 			int nh=0;
38016 			for(int h=0; h<r.nHoursPerDay; h++)
38017 				nh += teachersMatrix[t][d][h]>=1 ? 1 : 0;
38018 			if(nh>0)
38019 				nOD++;
38020 			//nd[nh]++;
38021 		}*/
38022 
38023 		//return the minimum occupied days which do not respect this constraint
38024 /*		int i = r.nDaysPerWeek - this->maxDaysPerWeek;
38025 		for(int k=0; k<=2*r.nHoursPerDay; k++){
38026 			if(nd[k]>0){
38027 				if(i>nd[k]){
38028 					i-=nd[k];
38029 					nbroken+=nd[k]*k;
38030 				}
38031 				else{
38032 					nbroken+=i*k;
38033 					break;
38034 				}
38035 			}
38036 		}*/
38037 
38038 /*		if(nOD>this->maxAfternoonsPerWeek)
38039 			nbroken=1;
38040 
38041 		if(nbroken>0 && conflictsString!=nullptr){
38042 			QString s= tr("Time constraint teacher max afternoons per week broken for teacher: %1.")
38043 			 .arg(r.internalTeachersList[t]->name);
38044 			s += tr("This increases the conflicts total by %1")
38045 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
38046 
38047 			dl.append(s);
38048 			cl.append(nbroken*weightPercentage/100);
38049 
38050 			*conflictsString += s+"\n";
38051 		}*/
38052 	//}
38053 
38054 /*	if(weightPercentage==100)
38055 		assert(nbroken==0);
38056 	return weightPercentage/100 * nbroken;*/
38057 
38058 	Matrix1D<bool> occDay;
38059 	occDay.resize(r.nDaysPerWeek);
38060 	for(int tch=0; tch<r.nInternalTeachers; tch++){
38061 		for(int d=1; d<r.nDaysPerWeek; d+=2){
38062 			occDay[d]=false;
38063 			for(int h=0; h<r.nHoursPerDay; h++){
38064 				if(teachersMatrix[tch][d][h]>0){
38065 					occDay[d]=true;
38066 					break;
38067 				}
38068 			}
38069 		}
38070 		for(int d=1; d+4<r.nDaysPerWeek; d+=2)
38071 			if(occDay[d] && occDay[d+2] && occDay[d+4])
38072 				assert(0);
38073 	}
38074 
38075 	return 0.0;
38076 }
38077 
isRelatedToActivity(Rules & r,Activity * a)38078 bool ConstraintTeachersMaxTwoConsecutiveAfternoons::isRelatedToActivity(Rules& r, Activity* a)
38079 {
38080 	Q_UNUSED(r);
38081 	Q_UNUSED(a);
38082 
38083 	return false;
38084 }
38085 
isRelatedToTeacher(Teacher * t)38086 bool ConstraintTeachersMaxTwoConsecutiveAfternoons::isRelatedToTeacher(Teacher* t)
38087 {
38088 	Q_UNUSED(t);
38089 
38090 	//if(this->teacherName==t->name)
38091 	//	return true;
38092 	//return false;
38093 	return true;
38094 }
38095 
isRelatedToSubject(Subject * s)38096 bool ConstraintTeachersMaxTwoConsecutiveAfternoons::isRelatedToSubject(Subject* s)
38097 {
38098 	Q_UNUSED(s);
38099 
38100 	return false;
38101 }
38102 
isRelatedToActivityTag(ActivityTag * s)38103 bool ConstraintTeachersMaxTwoConsecutiveAfternoons::isRelatedToActivityTag(ActivityTag* s)
38104 {
38105 	Q_UNUSED(s);
38106 
38107 	return false;
38108 }
38109 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)38110 bool ConstraintTeachersMaxTwoConsecutiveAfternoons::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
38111 {
38112 	Q_UNUSED(r);
38113 	Q_UNUSED(s);
38114 
38115 	return false;
38116 }
38117 
hasWrongDayOrHour(Rules & r)38118 bool ConstraintTeachersMaxTwoConsecutiveAfternoons::hasWrongDayOrHour(Rules& r)
38119 {
38120 	Q_UNUSED(r);
38121 
38122 	return false;
38123 }
38124 
canRepairWrongDayOrHour(Rules & r)38125 bool ConstraintTeachersMaxTwoConsecutiveAfternoons::canRepairWrongDayOrHour(Rules& r)
38126 {
38127 	assert(hasWrongDayOrHour(r));
38128 
38129 	return true;
38130 }
38131 
repairWrongDayOrHour(Rules & r)38132 bool ConstraintTeachersMaxTwoConsecutiveAfternoons::repairWrongDayOrHour(Rules& r)
38133 {
38134 	assert(hasWrongDayOrHour(r));
38135 
38136 	return true;
38137 }
38138 
38139 ////////////////////////////////////////////////////////////////////////////////////////////
38140 ////////////////////////////////////////////////////////////////////////////////////////////
38141 
ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour()38142 ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour()
38143 	: TimeConstraint()
38144 {
38145 	this->type = CONSTRAINT_TEACHERS_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
38146 }
38147 
ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour(double wp,int mBSH)38148 ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour(double wp, int mBSH)
38149 	: TimeConstraint(wp)
38150 {
38151 	this->type = CONSTRAINT_TEACHERS_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
38152 	this->maxBeginningsAtSecondHour=mBSH;
38153 }
38154 
computeInternalStructure(QWidget * parent,Rules & r)38155 bool ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
38156 {
38157 	Q_UNUSED(parent);
38158 	Q_UNUSED(r);
38159 
38160 	return true;
38161 }
38162 
hasInactiveActivities(Rules & r)38163 bool ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
38164 {
38165 	Q_UNUSED(r);
38166 	return false;
38167 }
38168 
getXmlDescription(Rules & r)38169 QString ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
38170 {
38171 	Q_UNUSED(r);
38172 
38173 	QString s="<ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour>\n";
38174 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
38175 	s+="	<Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
38176 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
38177 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
38178 	s+="</ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour>\n";
38179 	return s;
38180 }
38181 
getDescription(Rules & r)38182 QString ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
38183 {
38184 	Q_UNUSED(r);
38185 
38186 	QString begin=QString("");
38187 	if(!active)
38188 		begin="X - ";
38189 
38190 	QString end=QString("");
38191 	if(!comments.isEmpty())
38192 		end=", "+tr("C: %1", "Comments").arg(comments);
38193 
38194 	QString s;
38195 	s+=tr("Teachers must begin afternoons early, respecting maximum %1 arrivals at second hour")
38196 	 .arg(this->maxBeginningsAtSecondHour);
38197 	s+=", ";
38198 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
38199 
38200 	return begin+s+end;
38201 }
38202 
getDetailedDescription(Rules & r)38203 QString ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
38204 {
38205 	Q_UNUSED(r);
38206 
38207 	QString s=tr("Time constraint");s+="\n";
38208 	s+=tr("All teachers must begin the afternoons early, respecting maximum %1 later arrivals, at second hour")
38209 	 .arg(this->maxBeginningsAtSecondHour);s+="\n";
38210 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
38211 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
38212 
38213 	if(!active){
38214 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
38215 		s+="\n";
38216 	}
38217 	if(!comments.isEmpty()){
38218 		s+=tr("Comments=%1").arg(comments);
38219 		s+="\n";
38220 	}
38221 
38222 	return s;
38223 }
38224 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)38225 double ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
38226 {
38227 	//considers the condition that the hours of teachers begin as early as possible the afternoons
38228 
38229 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
38230 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
38231 		c.teachersMatrixReady=true;
38232 		c.subgroupsMatrixReady=true;
38233 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
38234 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
38235 
38236 		c.changedForMatrixCalculation=false;
38237 	}
38238 
38239 	int conflTotal=0;
38240 
38241 	for(int i=0; i<r.nInternalTeachers; i++){
38242 		int nGapsFirstHour=0;
38243 		for(int j=0; j<r.nDaysPerWeek; j++){
38244 			if(j%2==0)
38245 				continue;
38246 
38247 			int k;
38248 			for(k=0; k<r.nHoursPerDay; k++)
38249 				if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k])
38250 					break;
38251 
38252 			bool firstHourOccupied=false;
38253 			if(k<r.nHoursPerDay)
38254 				if(teachersMatrix[i][j][k]>0)
38255 					firstHourOccupied=true;
38256 
38257 			bool dayOccupied=firstHourOccupied;
38258 
38259 			bool illegalGap=false;
38260 
38261 			if(!dayOccupied){
38262 				for(k++; k<r.nHoursPerDay; k++){
38263 					if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
38264 						if(teachersMatrix[i][j][k]>0){
38265 							dayOccupied=true;
38266 							break;
38267 						}
38268 						else{
38269 							illegalGap=true;
38270 						}
38271 					}
38272 				}
38273 			}
38274 
38275 			if(dayOccupied && illegalGap){
38276 				if(conflictsString!=nullptr){
38277 					QString s=tr("Constraint teachers afternoons early max %1 beginnings at second hour broken for teacher %2, on day %3,"
38278 					 " because the teacher has an illegal gap, increases conflicts total by %4")
38279 					 .arg(this->maxBeginningsAtSecondHour)
38280 					 .arg(r.internalTeachersList[i]->name)
38281 					 .arg(r.daysOfTheWeek[j])
38282 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
38283 
38284 					dl.append(s);
38285 					cl.append(1*weightPercentage/100);
38286 
38287 					*conflictsString+= s+"\n";
38288 
38289 					conflTotal+=1;
38290 				}
38291 
38292 				if(c.nPlacedActivities==r.nInternalActivities){
38293 					assert(0);
38294 				}
38295 			}
38296 
38297 			if(dayOccupied && !firstHourOccupied)
38298 				nGapsFirstHour++;
38299 		}
38300 
38301 		if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
38302 			if(conflictsString!=nullptr){
38303 				QString s=tr("Constraint teachers afternoons early max %1 beginnings at second hour broken for teacher %2,"
38304 				 " because the teacher has too many arrivals at second hour, increases conflicts total by %3")
38305 				 .arg(this->maxBeginningsAtSecondHour)
38306 				 .arg(r.internalTeachersList[i]->name)
38307 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
38308 
38309 				dl.append(s);
38310 				cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
38311 
38312 				*conflictsString+= s+"\n";
38313 
38314 				conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
38315 			}
38316 
38317 			if(c.nPlacedActivities==r.nInternalActivities){
38318 				assert(0);
38319 			}
38320 		}
38321 	}
38322 
38323 	if(c.nPlacedActivities==r.nInternalActivities)
38324 		if(weightPercentage==100)    //might be broken for partial solutions
38325 			assert(conflTotal==0);
38326 	return weightPercentage/100 * conflTotal;
38327 }
38328 
isRelatedToActivity(Rules & r,Activity * a)38329 bool ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
38330 {
38331 	Q_UNUSED(r);
38332 	Q_UNUSED(a);
38333 
38334 	return false;
38335 }
38336 
isRelatedToTeacher(Teacher * t)38337 bool ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
38338 {
38339 	Q_UNUSED(t);
38340 
38341 	return true;
38342 }
38343 
isRelatedToSubject(Subject * s)38344 bool ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
38345 {
38346 	Q_UNUSED(s);
38347 
38348 	return false;
38349 }
38350 
isRelatedToActivityTag(ActivityTag * s)38351 bool ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
38352 {
38353 	Q_UNUSED(s);
38354 
38355 	return false;
38356 }
38357 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)38358 bool ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
38359 {
38360 	Q_UNUSED(r);
38361 	Q_UNUSED(s);
38362 
38363 	return false;
38364 }
38365 
hasWrongDayOrHour(Rules & r)38366 bool ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
38367 {
38368 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
38369 		return true;
38370 
38371 	return false;
38372 }
38373 
canRepairWrongDayOrHour(Rules & r)38374 bool ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
38375 {
38376 	assert(hasWrongDayOrHour(r));
38377 
38378 	return true;
38379 }
38380 
repairWrongDayOrHour(Rules & r)38381 bool ConstraintTeachersAfternoonsEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
38382 {
38383 	assert(hasWrongDayOrHour(r));
38384 
38385 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
38386 		maxBeginningsAtSecondHour=r.nDaysPerWeek/2;
38387 
38388 	return true;
38389 }
38390 
38391 ////////////////////////////////////////////////////////////////////////////////////////////
38392 ////////////////////////////////////////////////////////////////////////////////////////////
38393 
ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour()38394 ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour()
38395 	: TimeConstraint()
38396 {
38397 	this->type = CONSTRAINT_TEACHER_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
38398 }
38399 
ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour(double wp,int mBSH,const QString & teacher)38400 ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour(double wp, int mBSH, const QString& teacher)
38401 	: TimeConstraint(wp)
38402 {
38403 	this->type = CONSTRAINT_TEACHER_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
38404 	this->teacherName=teacher;
38405 	this->maxBeginningsAtSecondHour=mBSH;
38406 }
38407 
computeInternalStructure(QWidget * parent,Rules & r)38408 bool ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
38409 {
38410 	Q_UNUSED(parent);
38411 
38412 	teacherIndex=r.teachersHash.value(teacherName, -1);
38413 	assert(this->teacherIndex>=0);
38414 
38415 	return true;
38416 }
38417 
hasInactiveActivities(Rules & r)38418 bool ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
38419 {
38420 	Q_UNUSED(r);
38421 	return false;
38422 }
38423 
getXmlDescription(Rules & r)38424 QString ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
38425 {
38426 	Q_UNUSED(r);
38427 
38428 	QString s="<ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour>\n";
38429 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
38430 	s+="	<Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
38431 	s+="	<Teacher>"+protect(this->teacherName)+"</Teacher>\n";
38432 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
38433 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
38434 	s+="</ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour>\n";
38435 	return s;
38436 }
38437 
getDescription(Rules & r)38438 QString ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
38439 {
38440 	Q_UNUSED(r);
38441 
38442 	QString begin=QString("");
38443 	if(!active)
38444 		begin="X - ";
38445 
38446 	QString end=QString("");
38447 	if(!comments.isEmpty())
38448 		end=", "+tr("C: %1", "Comments").arg(comments);
38449 
38450 	QString s;
38451 
38452 	s+=tr("Teacher must begin afternoons early, respecting maximum %1 arrivals at second hour")
38453 	 .arg(this->maxBeginningsAtSecondHour); s+=", ";
38454 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
38455 	s+=tr("T:%1", "Teacher").arg(this->teacherName);
38456 
38457 	return begin+s+end;
38458 }
38459 
getDetailedDescription(Rules & r)38460 QString ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
38461 {
38462 	Q_UNUSED(r);
38463 
38464 	QString s=tr("Time constraint");s+="\n";
38465 
38466 	s+=tr("A teacher must begin his afternoons early, respecting a maximum number of later arrivals, at second hour"); s+="\n";
38467 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
38468 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
38469 	s+=tr("Teacher=%1").arg(this->teacherName); s+="\n";
38470 	s+=tr("Maximum number of arrivals at the second hour=%1").arg(this->maxBeginningsAtSecondHour);s+="\n";
38471 
38472 	if(!active){
38473 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
38474 		s+="\n";
38475 	}
38476 	if(!comments.isEmpty()){
38477 		s+=tr("Comments=%1").arg(comments);
38478 		s+="\n";
38479 	}
38480 
38481 	return s;
38482 }
38483 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)38484 double ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
38485 {
38486 	//considers the condition that the hours of subgroups begin as early as possible
38487 
38488 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
38489 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
38490 		c.teachersMatrixReady=true;
38491 		c.subgroupsMatrixReady=true;
38492 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
38493 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
38494 
38495 		c.changedForMatrixCalculation=false;
38496 	}
38497 
38498 	int conflTotal=0;
38499 
38500 	if(1){
38501 		int i=teacherIndex;
38502 
38503 		int nGapsFirstHour=0;
38504 		for(int j=0; j<r.nDaysPerWeek; j++){
38505 			if(j%2==0)
38506 				continue;
38507 
38508 			int k;
38509 			for(k=0; k<r.nHoursPerDay; k++)
38510 				if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k])
38511 					break;
38512 
38513 			bool firstHourOccupied=false;
38514 			if(k<r.nHoursPerDay)
38515 				if(teachersMatrix[i][j][k]>0)
38516 					firstHourOccupied=true;
38517 
38518 			bool dayOccupied=firstHourOccupied;
38519 
38520 			bool illegalGap=false;
38521 
38522 			if(!dayOccupied){
38523 				for(k++; k<r.nHoursPerDay; k++){
38524 					if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
38525 						if(teachersMatrix[i][j][k]>0){
38526 							dayOccupied=true;
38527 							break;
38528 						}
38529 						else{
38530 							illegalGap=true;
38531 						}
38532 					}
38533 				}
38534 			}
38535 
38536 			if(dayOccupied && illegalGap){
38537 				if(conflictsString!=nullptr){
38538 					QString s=tr("Constraint teacher afternoons early max %1 beginnings at second hour broken for teacher %2, on day %3,"
38539 					 " because the teacher has an illegal gap, increases conflicts total by %4")
38540 					 .arg(this->maxBeginningsAtSecondHour)
38541 					 .arg(r.internalTeachersList[i]->name)
38542 					 .arg(r.daysOfTheWeek[j])
38543 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
38544 
38545 					dl.append(s);
38546 					cl.append(1*weightPercentage/100);
38547 
38548 					*conflictsString+= s+"\n";
38549 
38550 					conflTotal+=1;
38551 				}
38552 
38553 				if(c.nPlacedActivities==r.nInternalActivities)
38554 					assert(0);
38555 			}
38556 
38557 			if(dayOccupied && !firstHourOccupied)
38558 				nGapsFirstHour++;
38559 		}
38560 
38561 		if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
38562 			if(conflictsString!=nullptr){
38563 				QString s=tr("Constraint teacher afternoons early max %1 beginnings at second hour broken for teacher %2,"
38564 				 " because the teacher has too many arrivals at second hour, increases conflicts total by %3")
38565 				 .arg(this->maxBeginningsAtSecondHour)
38566 				 .arg(r.internalTeachersList[i]->name)
38567 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
38568 
38569 				dl.append(s);
38570 				cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
38571 
38572 				*conflictsString+= s+"\n";
38573 
38574 				conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
38575 			}
38576 
38577 			if(c.nPlacedActivities==r.nInternalActivities)
38578 				assert(0);
38579 		}
38580 	}
38581 
38582 	if(c.nPlacedActivities==r.nInternalActivities)
38583 		if(weightPercentage==100)    //might be broken for partial solutions
38584 			assert(conflTotal==0);
38585 	return weightPercentage/100 * conflTotal;
38586 }
38587 
isRelatedToActivity(Rules & r,Activity * a)38588 bool ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
38589 {
38590 	Q_UNUSED(r);
38591 	Q_UNUSED(a);
38592 
38593 	return false;
38594 }
38595 
isRelatedToTeacher(Teacher * t)38596 bool ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
38597 {
38598 	return this->teacherName==t->name;
38599 }
38600 
isRelatedToSubject(Subject * s)38601 bool ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
38602 {
38603 	Q_UNUSED(s);
38604 
38605 	return false;
38606 }
38607 
isRelatedToActivityTag(ActivityTag * s)38608 bool ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
38609 {
38610 	Q_UNUSED(s);
38611 
38612 	return false;
38613 }
38614 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)38615 bool ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
38616 {
38617 	Q_UNUSED(r);
38618 	Q_UNUSED(s);
38619 
38620 	return false;
38621 }
38622 
hasWrongDayOrHour(Rules & r)38623 bool ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
38624 {
38625 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
38626 		return true;
38627 
38628 	return false;
38629 }
38630 
canRepairWrongDayOrHour(Rules & r)38631 bool ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
38632 {
38633 	assert(hasWrongDayOrHour(r));
38634 
38635 	return true;
38636 }
38637 
repairWrongDayOrHour(Rules & r)38638 bool ConstraintTeacherAfternoonsEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
38639 {
38640 	assert(hasWrongDayOrHour(r));
38641 
38642 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
38643 		maxBeginningsAtSecondHour=r.nDaysPerWeek/2;
38644 
38645 	return true;
38646 }
38647 
38648 //2020-06-14
38649 ////////////////////////////////////////////////////////////////////////////////////////////
38650 ////////////////////////////////////////////////////////////////////////////////////////////
38651 
ConstraintStudentsMinHoursPerMorning()38652 ConstraintStudentsMinHoursPerMorning::ConstraintStudentsMinHoursPerMorning()
38653 	: TimeConstraint()
38654 {
38655 	this->type = CONSTRAINT_STUDENTS_MIN_HOURS_PER_MORNING;
38656 	this->minHoursPerMorning = -1;
38657 
38658 	this->allowEmptyMornings=false;
38659 }
38660 
ConstraintStudentsMinHoursPerMorning(double wp,int minnh,bool _allowEmptyMornings)38661 ConstraintStudentsMinHoursPerMorning::ConstraintStudentsMinHoursPerMorning(double wp, int minnh, bool _allowEmptyMornings)
38662 	: TimeConstraint(wp)
38663 {
38664 	this->minHoursPerMorning = minnh;
38665 	this->type = CONSTRAINT_STUDENTS_MIN_HOURS_PER_MORNING;
38666 
38667 	this->allowEmptyMornings=_allowEmptyMornings;
38668 }
38669 
computeInternalStructure(QWidget * parent,Rules & r)38670 bool ConstraintStudentsMinHoursPerMorning::computeInternalStructure(QWidget* parent, Rules& r)
38671 {
38672 	Q_UNUSED(parent);
38673 	Q_UNUSED(r);
38674 
38675 	return true;
38676 }
38677 
hasInactiveActivities(Rules & r)38678 bool ConstraintStudentsMinHoursPerMorning::hasInactiveActivities(Rules& r)
38679 {
38680 	Q_UNUSED(r);
38681 	return false;
38682 }
38683 
getXmlDescription(Rules & r)38684 QString ConstraintStudentsMinHoursPerMorning::getXmlDescription(Rules& r)
38685 {
38686 	Q_UNUSED(r);
38687 
38688 	QString s="<ConstraintStudentsMinHoursPerMorning>\n";
38689 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
38690 	if(this->minHoursPerMorning>=0)
38691 		s+="	<Minimum_Hours_Per_Morning>"+CustomFETString::number(this->minHoursPerMorning)+"</Minimum_Hours_Per_Morning>\n";
38692 	else
38693 		assert(0);
38694 	if(this->allowEmptyMornings)
38695 		s+="	<Allow_Empty_Mornings>true</Allow_Empty_Mornings>\n";
38696 	else
38697 		s+="	<Allow_Empty_Mornings>false</Allow_Empty_Mornings>\n";
38698 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
38699 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
38700 	s+="</ConstraintStudentsMinHoursPerMorning>\n";
38701 	return s;
38702 }
38703 
getDescription(Rules & r)38704 QString ConstraintStudentsMinHoursPerMorning::getDescription(Rules& r)
38705 {
38706 	Q_UNUSED(r);
38707 
38708 	QString begin=QString("");
38709 	if(!active)
38710 		begin="X - ";
38711 
38712 	QString end=QString("");
38713 	if(!comments.isEmpty())
38714 		end=", "+tr("C: %1", "Comments").arg(comments);
38715 
38716 	QString s;
38717 
38718 	if(this->allowEmptyMornings)
38719 		s+="! ";
38720 	s+=tr("Students min hours per morning");s+=", ";
38721 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
38722 	s+=tr("mH:%1", "Min hours (per morning)").arg(this->minHoursPerMorning);s+=", ";
38723 	s+=tr("AEM:%1", "Allow empty mornings").arg(yesNoTranslated(this->allowEmptyMornings));
38724 
38725 	return begin+s+end;
38726 }
38727 
getDetailedDescription(Rules & r)38728 QString ConstraintStudentsMinHoursPerMorning::getDetailedDescription(Rules& r)
38729 {
38730 	Q_UNUSED(r);
38731 
38732 	QString s=tr("Time constraint");s+="\n";
38733 	if(this->allowEmptyMornings==true){
38734 		s+=tr("(nonstandard, students may have empty mornings)");
38735 		s+="\n";
38736 	}
38737 	s+=tr("All students must respect the minimum number of hours per morning");s+="\n";
38738 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
38739 	s+=tr("Minimum hours per morning=%1").arg(this->minHoursPerMorning);s+="\n";
38740 	s+=tr("Allow empty mornings=%1").arg(yesNoTranslated(this->allowEmptyMornings));s+="\n";
38741 
38742 	if(!active){
38743 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
38744 		s+="\n";
38745 	}
38746 	if(!comments.isEmpty()){
38747 		s+=tr("Comments=%1").arg(comments);
38748 		s+="\n";
38749 	}
38750 
38751 	return s;
38752 }
38753 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)38754 double ConstraintStudentsMinHoursPerMorning::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
38755 {
38756 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
38757 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
38758 		c.teachersMatrixReady=true;
38759 		c.subgroupsMatrixReady=true;
38760 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
38761 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
38762 
38763 		c.changedForMatrixCalculation=false;
38764 	}
38765 
38766 	int tmp1/*, tmp2*/;
38767 	int too_little;
38768 
38769 	assert(this->minHoursPerMorning>=0);
38770 
38771 	too_little=0;
38772 	for(int i=0; i<r.nInternalSubgroups; i++)
38773 		for(int j=0; j<r.nDaysPerWeek/2; j++){
38774 			tmp1=0;
38775 			for(int k=0; k<r.nHoursPerDay; k++){
38776 				if(subgroupsMatrix[i][2*j][k]>=1)
38777 					tmp1++;
38778 			}
38779 
38780 			if(tmp1>0 && tmp1<this->minHoursPerMorning){
38781 				too_little += - tmp1 + this->minHoursPerMorning;
38782 
38783 				if(conflictsString!=nullptr){
38784 					QString s=tr("Time constraint students min hours per morning broken for subgroup: %1, day: %2, length=%3, conflict increase=%4")
38785 					 .arg(r.internalSubgroupsList[i]->name)
38786 					 .arg(r.daysOfTheWeek[2*j])
38787 					 .arg(CustomFETString::number(tmp1))
38788 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(-tmp1+this->minHoursPerMorning)));
38789 
38790 					dl.append(s);
38791 					cl.append(weightPercentage/100*(-tmp1+this->minHoursPerMorning));
38792 
38793 					*conflictsString+= s+"\n";
38794 				}
38795 			}
38796 
38797 			/*tmp2=0;
38798 			for(int k=0; k<r.nHoursPerDay; k++){
38799 				if(subgroupsMatrix[i][2*j+1][k]>=1)
38800 					tmp2++;
38801 			}
38802 
38803 			if(tmp2>0 && tmp2<this->minHoursDaily){
38804 				too_little += - tmp2 + this->minHoursDaily;
38805 
38806 				if(conflictsString!=nullptr){
38807 					QString s=tr("Time constraint students min hours daily broken for subgroup: %1, day: %2, length=%3, conflict increase=%4")
38808 					 .arg(r.internalSubgroupsList[i]->name)
38809 					 .arg(r.daysOfTheWeek[2*j+1])
38810 					 .arg(CustomFETString::number(tmp2))
38811 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(-tmp2+this->minHoursDaily)));
38812 
38813 					dl.append(s);
38814 					cl.append(weightPercentage/100*(-tmp2+this->minHoursDaily));
38815 
38816 					*conflictsString+= s+"\n";
38817 				}
38818 			}*/
38819 
38820 			if(!this->allowEmptyMornings==true)
38821 				if(tmp1/*+tmp2*/==0){
38822 					too_little++;
38823 
38824 					if(conflictsString!=nullptr){
38825 						QString s=tr("Time constraint students min hours per morning broken for subgroup: %1, day: %2, empty morning, but"
38826 						 " the constraint does not allow empty mornings, conflict increase=%3")
38827 						 .arg(r.internalSubgroupsList[i]->name)
38828 						 .arg(r.daysOfTheWeek[2*j])
38829 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(1)));
38830 
38831 						dl.append(s);
38832 						cl.append(weightPercentage/100*1);
38833 
38834 						*conflictsString+= s+"\n";
38835 					}
38836 				}
38837 		}
38838 
38839 	assert(too_little>=0);
38840 
38841 	if(c.nPlacedActivities==r.nInternalActivities)
38842 		if(weightPercentage==100) //does not work for partial solutions
38843 			assert(too_little==0);
38844 
38845 	return too_little * weightPercentage/100;
38846 }
38847 
isRelatedToActivity(Rules & r,Activity * a)38848 bool ConstraintStudentsMinHoursPerMorning::isRelatedToActivity(Rules& r, Activity* a)
38849 {
38850 	Q_UNUSED(r);
38851 	Q_UNUSED(a);
38852 
38853 	return false;
38854 }
38855 
isRelatedToTeacher(Teacher * t)38856 bool ConstraintStudentsMinHoursPerMorning::isRelatedToTeacher(Teacher* t)
38857 {
38858 	Q_UNUSED(t);
38859 
38860 	return false;
38861 }
38862 
isRelatedToSubject(Subject * s)38863 bool ConstraintStudentsMinHoursPerMorning::isRelatedToSubject(Subject* s)
38864 {
38865 	Q_UNUSED(s);
38866 
38867 	return false;
38868 }
38869 
isRelatedToActivityTag(ActivityTag * s)38870 bool ConstraintStudentsMinHoursPerMorning::isRelatedToActivityTag(ActivityTag* s)
38871 {
38872 	Q_UNUSED(s);
38873 
38874 	return false;
38875 }
38876 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)38877 bool ConstraintStudentsMinHoursPerMorning::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
38878 {
38879 	Q_UNUSED(r);
38880 	Q_UNUSED(s);
38881 
38882 	return true;
38883 }
38884 
hasWrongDayOrHour(Rules & r)38885 bool ConstraintStudentsMinHoursPerMorning::hasWrongDayOrHour(Rules& r)
38886 {
38887 	if(minHoursPerMorning>r.nHoursPerDay)
38888 		return true;
38889 
38890 	return false;
38891 }
38892 
canRepairWrongDayOrHour(Rules & r)38893 bool ConstraintStudentsMinHoursPerMorning::canRepairWrongDayOrHour(Rules& r)
38894 {
38895 	assert(hasWrongDayOrHour(r));
38896 
38897 	return true;
38898 }
38899 
repairWrongDayOrHour(Rules & r)38900 bool ConstraintStudentsMinHoursPerMorning::repairWrongDayOrHour(Rules& r)
38901 {
38902 	assert(hasWrongDayOrHour(r));
38903 
38904 	if(minHoursPerMorning>r.nHoursPerDay)
38905 		minHoursPerMorning=r.nHoursPerDay;
38906 
38907 	return true;
38908 }
38909 
38910 ////////////////////////////////////////////////////////////////////////////////////////////
38911 ////////////////////////////////////////////////////////////////////////////////////////////
38912 
ConstraintStudentsSetMinHoursPerMorning()38913 ConstraintStudentsSetMinHoursPerMorning::ConstraintStudentsSetMinHoursPerMorning()
38914 	: TimeConstraint()
38915 {
38916 	this->type = CONSTRAINT_STUDENTS_SET_MIN_HOURS_PER_MORNING;
38917 	this->minHoursPerMorning = -1;
38918 
38919 	this->allowEmptyMornings=false;
38920 }
38921 
ConstraintStudentsSetMinHoursPerMorning(double wp,int minnh,QString s,bool _allowEmptyMornings)38922 ConstraintStudentsSetMinHoursPerMorning::ConstraintStudentsSetMinHoursPerMorning(double wp, int minnh, QString s, bool _allowEmptyMornings)
38923 	: TimeConstraint(wp)
38924 {
38925 	this->minHoursPerMorning = minnh;
38926 	this->students = s;
38927 	this->type = CONSTRAINT_STUDENTS_SET_MIN_HOURS_PER_MORNING;
38928 
38929 	this->allowEmptyMornings=_allowEmptyMornings;
38930 }
38931 
hasInactiveActivities(Rules & r)38932 bool ConstraintStudentsSetMinHoursPerMorning::hasInactiveActivities(Rules& r)
38933 {
38934 	Q_UNUSED(r);
38935 	return false;
38936 }
38937 
getXmlDescription(Rules & r)38938 QString ConstraintStudentsSetMinHoursPerMorning::getXmlDescription(Rules& r)
38939 {
38940 	Q_UNUSED(r);
38941 
38942 	QString s="<ConstraintStudentsSetMinHoursPerMorning>\n";
38943 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
38944 	s+="	<Minimum_Hours_Per_Morning>"+CustomFETString::number(this->minHoursPerMorning)+"</Minimum_Hours_Per_Morning>\n";
38945 	s+="	<Students>"+protect(this->students)+"</Students>\n";
38946 	if(this->allowEmptyMornings)
38947 		s+="	<Allow_Empty_Mornings>true</Allow_Empty_Mornings>\n";
38948 	else
38949 		s+="	<Allow_Empty_Mornings>false</Allow_Empty_Mornings>\n";
38950 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
38951 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
38952 	s+="</ConstraintStudentsSetMinHoursPerMorning>\n";
38953 	return s;
38954 }
38955 
getDescription(Rules & r)38956 QString ConstraintStudentsSetMinHoursPerMorning::getDescription(Rules& r)
38957 {
38958 	Q_UNUSED(r);
38959 
38960 	QString begin=QString("");
38961 	if(!active)
38962 		begin="X - ";
38963 
38964 	QString end=QString("");
38965 	if(!comments.isEmpty())
38966 		end=", "+tr("C: %1", "Comments").arg(comments);
38967 
38968 	QString s;
38969 
38970 	if(this->allowEmptyMornings)
38971 		s+="! ";
38972 	s+=tr("Students set min hours per morning");s+=", ";
38973 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
38974 	s+=tr("St:%1", "Students (set)").arg(this->students);s+=", ";
38975 	s+=tr("mH:%1", "Min hours (per morning)").arg(this->minHoursPerMorning);s+=", ";
38976 	s+=tr("AEM:%1", "Allow empty mornings").arg(yesNoTranslated(this->allowEmptyMornings));
38977 
38978 	return begin+s+end;
38979 }
38980 
getDetailedDescription(Rules & r)38981 QString ConstraintStudentsSetMinHoursPerMorning::getDetailedDescription(Rules& r)
38982 {
38983 	Q_UNUSED(r);
38984 
38985 	QString s=tr("Time constraint");s+="\n";
38986 	if(this->allowEmptyMornings==true){
38987 		s+=tr("(nonstandard, students may have empty mornings)");
38988 		s+="\n";
38989 	}
38990 	s+=tr("A students set must respect the minimum number of hours per morning");s+="\n";
38991 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
38992 	s+=tr("Students set=%1").arg(this->students);s+="\n";
38993 	s+=tr("Minimum hours per morning=%1").arg(this->minHoursPerMorning);s+="\n";
38994 	s+=tr("Allow empty mornings=%1").arg(yesNoTranslated(this->allowEmptyMornings));s+="\n";
38995 
38996 	if(!active){
38997 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
38998 		s+="\n";
38999 	}
39000 	if(!comments.isEmpty()){
39001 		s+=tr("Comments=%1").arg(comments);
39002 		s+="\n";
39003 	}
39004 
39005 	return s;
39006 }
39007 
computeInternalStructure(QWidget * parent,Rules & r)39008 bool ConstraintStudentsSetMinHoursPerMorning::computeInternalStructure(QWidget* parent, Rules& r)
39009 {
39010 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
39011 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
39012 
39013 	if(ss==nullptr){
39014 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
39015 		 tr("Constraint students set min hours per morning is wrong because it refers to inexistent students set."
39016 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
39017 
39018 		return false;
39019 	}
39020 
39021 	assert(ss!=nullptr);
39022 
39023 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
39024 	/*this->iSubgroupsList.clear();
39025 	if(ss->type==STUDENTS_SUBGROUP){
39026 		int tmp;
39027 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
39028 		assert(tmp>=0);
39029 		assert(tmp<r.nInternalSubgroups);
39030 		if(!this->iSubgroupsList.contains(tmp))
39031 			this->iSubgroupsList.append(tmp);
39032 	}
39033 	else if(ss->type==STUDENTS_GROUP){
39034 		StudentsGroup* stg=(StudentsGroup*)ss;
39035 		for(int i=0; i<stg->subgroupsList.size(); i++){
39036 			StudentsSubgroup* sts=stg->subgroupsList[i];
39037 			int tmp;
39038 			tmp=sts->indexInInternalSubgroupsList;
39039 			assert(tmp>=0);
39040 			assert(tmp<r.nInternalSubgroups);
39041 			if(!this->iSubgroupsList.contains(tmp))
39042 				this->iSubgroupsList.append(tmp);
39043 		}
39044 	}
39045 	else if(ss->type==STUDENTS_YEAR){
39046 		StudentsYear* sty=(StudentsYear*)ss;
39047 		for(int i=0; i<sty->groupsList.size(); i++){
39048 			StudentsGroup* stg=sty->groupsList[i];
39049 			for(int j=0; j<stg->subgroupsList.size(); j++){
39050 				StudentsSubgroup* sts=stg->subgroupsList[j];
39051 				int tmp;
39052 				tmp=sts->indexInInternalSubgroupsList;
39053 				assert(tmp>=0);
39054 				assert(tmp<r.nInternalSubgroups);
39055 				if(!this->iSubgroupsList.contains(tmp))
39056 					this->iSubgroupsList.append(tmp);
39057 			}
39058 		}
39059 	}
39060 	else
39061 		assert(0);*/
39062 
39063 	return true;
39064 }
39065 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)39066 double ConstraintStudentsSetMinHoursPerMorning::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
39067 {
39068 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
39069 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
39070 		c.teachersMatrixReady=true;
39071 		c.subgroupsMatrixReady=true;
39072 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
39073 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
39074 
39075 		c.changedForMatrixCalculation=false;
39076 	}
39077 
39078 	int tmp1/*, tmp2*/;
39079 	int too_little;
39080 
39081 	assert(this->minHoursPerMorning>=0);
39082 
39083 	too_little=0;
39084 	for(int sg=0; sg<this->iSubgroupsList.count(); sg++){
39085 		int i=iSubgroupsList.at(sg);
39086 		for(int j=0; j<r.nDaysPerWeek/2; j++){
39087 			tmp1=0;
39088 			for(int k=0; k<r.nHoursPerDay; k++){
39089 				if(subgroupsMatrix[i][2*j][k]>=1)
39090 					tmp1++;
39091 			}
39092 
39093 			if(tmp1>0 && tmp1<this->minHoursPerMorning){
39094 				too_little += - tmp1 + this->minHoursPerMorning;
39095 
39096 				if(conflictsString!=nullptr){
39097 					QString s=tr("Time constraint students set min hours per morning broken for subgroup: %1, day: %2, length=%3, conflict increase=%4")
39098 					 .arg(r.internalSubgroupsList[i]->name)
39099 					 .arg(r.daysOfTheWeek[2*j])
39100 					 .arg(CustomFETString::number(tmp1))
39101 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(-tmp1+this->minHoursPerMorning)));
39102 
39103 					dl.append(s);
39104 					cl.append(weightPercentage/100*(-tmp1+this->minHoursPerMorning));
39105 
39106 					*conflictsString+= s+"\n";
39107 				}
39108 			}
39109 
39110 			/*tmp2=0;
39111 			for(int k=0; k<r.nHoursPerDay; k++){
39112 				if(subgroupsMatrix[i][2*j+1][k]>=1)
39113 					tmp2++;
39114 			}
39115 
39116 			if(tmp2>0 && tmp2<this->minHoursDaily){
39117 				too_little += - tmp2 + this->minHoursDaily;
39118 
39119 				if(conflictsString!=nullptr){
39120 					QString s=tr("Time constraint students set min hours daily broken for subgroup: %1, day: %2, length=%3, conflict increase=%4")
39121 					 .arg(r.internalSubgroupsList[i]->name)
39122 					 .arg(r.daysOfTheWeek[2*j+1])
39123 					 .arg(CustomFETString::number(tmp2))
39124 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(-tmp2+this->minHoursDaily)));
39125 
39126 					dl.append(s);
39127 					cl.append(weightPercentage/100*(-tmp2+this->minHoursDaily));
39128 
39129 					*conflictsString+= s+"\n";
39130 				}
39131 			}*/
39132 
39133 			if(!this->allowEmptyMornings==true)
39134 				if(tmp1/*+tmp2*/==0){
39135 					too_little++;
39136 
39137 					if(conflictsString!=nullptr){
39138 						QString s=tr("Time constraint students set min hours per morning broken for subgroup: %1, day: %2, empty morning, but"
39139 						 " the constraint does not allow empty mornings, conflict increase=%3")
39140 						 .arg(r.internalSubgroupsList[i]->name)
39141 						 .arg(r.daysOfTheWeek[2*j])
39142 						 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*(1)));
39143 
39144 						dl.append(s);
39145 						cl.append(weightPercentage/100*1);
39146 
39147 						*conflictsString+= s+"\n";
39148 					}
39149 				}
39150 		}
39151 	}
39152 
39153 	assert(too_little>=0);
39154 
39155 	if(c.nPlacedActivities==r.nInternalActivities)
39156 		if(weightPercentage==100) //does not work for partial solutions
39157 			assert(too_little==0);
39158 
39159 	return too_little * weightPercentage / 100.0;
39160 }
39161 
isRelatedToActivity(Rules & r,Activity * a)39162 bool ConstraintStudentsSetMinHoursPerMorning::isRelatedToActivity(Rules& r, Activity* a)
39163 {
39164 	Q_UNUSED(r);
39165 	Q_UNUSED(a);
39166 
39167 	return false;
39168 }
39169 
isRelatedToTeacher(Teacher * t)39170 bool ConstraintStudentsSetMinHoursPerMorning::isRelatedToTeacher(Teacher* t)
39171 {
39172 	Q_UNUSED(t);
39173 
39174 	return false;
39175 }
39176 
isRelatedToSubject(Subject * s)39177 bool ConstraintStudentsSetMinHoursPerMorning::isRelatedToSubject(Subject* s)
39178 {
39179 	Q_UNUSED(s);
39180 
39181 	return false;
39182 }
39183 
isRelatedToActivityTag(ActivityTag * s)39184 bool ConstraintStudentsSetMinHoursPerMorning::isRelatedToActivityTag(ActivityTag* s)
39185 {
39186 	Q_UNUSED(s);
39187 
39188 	return false;
39189 }
39190 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)39191 bool ConstraintStudentsSetMinHoursPerMorning::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
39192 {
39193 	return r.setsShareStudents(this->students, s->name);
39194 }
39195 
hasWrongDayOrHour(Rules & r)39196 bool ConstraintStudentsSetMinHoursPerMorning::hasWrongDayOrHour(Rules& r)
39197 {
39198 	if(minHoursPerMorning>r.nHoursPerDay)
39199 		return true;
39200 
39201 	return false;
39202 }
39203 
canRepairWrongDayOrHour(Rules & r)39204 bool ConstraintStudentsSetMinHoursPerMorning::canRepairWrongDayOrHour(Rules& r)
39205 {
39206 	assert(hasWrongDayOrHour(r));
39207 
39208 	return true;
39209 }
39210 
repairWrongDayOrHour(Rules & r)39211 bool ConstraintStudentsSetMinHoursPerMorning::repairWrongDayOrHour(Rules& r)
39212 {
39213 	assert(hasWrongDayOrHour(r));
39214 
39215 	if(minHoursPerMorning>r.nHoursPerDay)
39216 		minHoursPerMorning=r.nHoursPerDay;
39217 
39218 	return true;
39219 }
39220 
39221 ////////////////////////////////////////////////////////////////////////////////////////////
39222 ////////////////////////////////////////////////////////////////////////////////////////////
39223 
ConstraintTeacherMaxZeroGapsPerAfternoon()39224 ConstraintTeacherMaxZeroGapsPerAfternoon::ConstraintTeacherMaxZeroGapsPerAfternoon()
39225 	: TimeConstraint()
39226 {
39227 	this->type = CONSTRAINT_TEACHER_MAX_ZERO_GAPS_PER_AFTERNOON;
39228 }
39229 
ConstraintTeacherMaxZeroGapsPerAfternoon(double wp,QString tn)39230 ConstraintTeacherMaxZeroGapsPerAfternoon::ConstraintTeacherMaxZeroGapsPerAfternoon(double wp, QString tn)
39231 	: TimeConstraint(wp)
39232 {
39233 	this->type = CONSTRAINT_TEACHER_MAX_ZERO_GAPS_PER_AFTERNOON;
39234 	this->teacherName=tn;
39235 }
39236 
computeInternalStructure(QWidget * parent,Rules & r)39237 bool ConstraintTeacherMaxZeroGapsPerAfternoon::computeInternalStructure(QWidget* parent, Rules& r)
39238 {
39239 	Q_UNUSED(parent);
39240 
39241 	//this->teacherIndex=r.searchTeacher(this->teacherName);
39242 	teacherIndex=r.teachersHash.value(teacherName, -1);
39243 	assert(this->teacherIndex>=0);
39244 	return true;
39245 }
39246 
hasInactiveActivities(Rules & r)39247 bool ConstraintTeacherMaxZeroGapsPerAfternoon::hasInactiveActivities(Rules& r)
39248 {
39249 	Q_UNUSED(r);
39250 	return false;
39251 }
39252 
getXmlDescription(Rules & r)39253 QString ConstraintTeacherMaxZeroGapsPerAfternoon::getXmlDescription(Rules& r){
39254 	Q_UNUSED(r);
39255 
39256 	QString s="<ConstraintTeacherMaxZeroGapsPerAfternoon>\n";
39257 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
39258 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
39259 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
39260 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
39261 	s+="</ConstraintTeacherMaxZeroGapsPerAfternoon>\n";
39262 	return s;
39263 }
39264 
getDescription(Rules & r)39265 QString ConstraintTeacherMaxZeroGapsPerAfternoon::getDescription(Rules& r){
39266 	Q_UNUSED(r);
39267 
39268 	QString begin=QString("");
39269 	if(!active)
39270 		begin="X - ";
39271 
39272 	QString end=QString("");
39273 	if(!comments.isEmpty())
39274 		end=", "+tr("C: %1", "Comments").arg(comments);
39275 
39276 	QString s;
39277 	s+=tr("Teacher max zero gaps per afternoon");s+=", ";
39278 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
39279 	s+=tr("T:%1", "Teacher").arg(this->teacherName);
39280 
39281 	return begin+s+end;
39282 }
39283 
getDetailedDescription(Rules & r)39284 QString ConstraintTeacherMaxZeroGapsPerAfternoon::getDetailedDescription(Rules& r){
39285 	Q_UNUSED(r);
39286 
39287 	QString s=tr("Time constraint"); s+="\n";
39288 	s+=tr("A teacher must respect a zero number of gaps per afternoon"); s+="\n";
39289 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
39290 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
39291 	s+=tr("Teacher=%1").arg(this->teacherName); s+="\n";
39292 
39293 	if(!active){
39294 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
39295 		s+="\n";
39296 	}
39297 	if(!comments.isEmpty()){
39298 		s+=tr("Comments=%1").arg(comments);
39299 		s+="\n";
39300 	}
39301 
39302 	return s;
39303 }
39304 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)39305 double ConstraintTeacherMaxZeroGapsPerAfternoon::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
39306 {
39307 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
39308 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
39309 		c.teachersMatrixReady=true;
39310 		c.subgroupsMatrixReady=true;
39311 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
39312 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
39313 
39314 		c.changedForMatrixCalculation=false;
39315 	}
39316 
39317 	int tg;
39318 	int i, j, k;
39319 	int totalGaps;
39320 
39321 	totalGaps=0;
39322 
39323 	i=this->teacherIndex;
39324 
39325 	for(j=0; j<r.nDaysPerWeek; j++){
39326 		if(j%2==0)
39327 			continue;
39328 		tg=0;
39329 		for(k=0; k<r.nHoursPerDay; k++)
39330 			if(teachersMatrix[i][j][k]>0){
39331 				assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
39332 				break;
39333 			}
39334 
39335 		int cnt=0;
39336 		for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
39337 			if(teachersMatrix[i][j][k]>0){
39338 				tg+=cnt;
39339 				cnt=0;
39340 			}
39341 			else
39342 				cnt++;
39343 		}
39344 		if(tg>0){
39345 			totalGaps+=tg;
39346 			//assert(this->weightPercentage<100); partial solutions might break this rule
39347 			if(conflictsString!=nullptr){
39348 				QString s=tr("Time constraint teacher max zero gaps per afternoon broken for teacher: %1, day: %2, conflicts factor increase=%3")
39349 					.arg(r.internalTeachersList[i]->name)
39350 					.arg(r.daysOfTheWeek[j])
39351 					.arg(CustomFETString::numberPlusTwoDigitsPrecision(tg*weightPercentage/100));
39352 
39353 				*conflictsString+= s+"\n";
39354 
39355 				dl.append(s);
39356 				cl.append(tg*weightPercentage/100);
39357 			}
39358 		}
39359 	}
39360 
39361 	if(c.nPlacedActivities==r.nInternalActivities)
39362 		if(weightPercentage==100)
39363 			assert(totalGaps==0); //for partial solutions this rule might be broken
39364 	return weightPercentage/100 * totalGaps;
39365 }
39366 
isRelatedToActivity(Rules & r,Activity * a)39367 bool ConstraintTeacherMaxZeroGapsPerAfternoon::isRelatedToActivity(Rules& r, Activity* a)
39368 {
39369 	Q_UNUSED(r);
39370 	Q_UNUSED(a);
39371 
39372 	return false;
39373 }
39374 
isRelatedToTeacher(Teacher * t)39375 bool ConstraintTeacherMaxZeroGapsPerAfternoon::isRelatedToTeacher(Teacher* t)
39376 {
39377 	if(this->teacherName==t->name)
39378 		return true;
39379 	return false;
39380 }
39381 
isRelatedToSubject(Subject * s)39382 bool ConstraintTeacherMaxZeroGapsPerAfternoon::isRelatedToSubject(Subject* s)
39383 {
39384 	Q_UNUSED(s);
39385 
39386 	return false;
39387 }
39388 
isRelatedToActivityTag(ActivityTag * s)39389 bool ConstraintTeacherMaxZeroGapsPerAfternoon::isRelatedToActivityTag(ActivityTag* s)
39390 {
39391 	Q_UNUSED(s);
39392 
39393 	return false;
39394 }
39395 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)39396 bool ConstraintTeacherMaxZeroGapsPerAfternoon::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
39397 {
39398 	Q_UNUSED(r);
39399 	Q_UNUSED(s);
39400 
39401 	return false;
39402 }
39403 
hasWrongDayOrHour(Rules & r)39404 bool ConstraintTeacherMaxZeroGapsPerAfternoon::hasWrongDayOrHour(Rules& r)
39405 {
39406 	Q_UNUSED(r);
39407 
39408 	return false;
39409 }
39410 
canRepairWrongDayOrHour(Rules & r)39411 bool ConstraintTeacherMaxZeroGapsPerAfternoon::canRepairWrongDayOrHour(Rules& r)
39412 {
39413 	assert(hasWrongDayOrHour(r));
39414 
39415 	return true;
39416 }
39417 
repairWrongDayOrHour(Rules & r)39418 bool ConstraintTeacherMaxZeroGapsPerAfternoon::repairWrongDayOrHour(Rules& r)
39419 {
39420 	assert(hasWrongDayOrHour(r));
39421 
39422 	return true;
39423 }
39424 
39425 ////////////////////////////////////////////////////////////////////////////////////////////
39426 ////////////////////////////////////////////////////////////////////////////////////////////
39427 
ConstraintTeachersMaxZeroGapsPerAfternoon()39428 ConstraintTeachersMaxZeroGapsPerAfternoon::ConstraintTeachersMaxZeroGapsPerAfternoon()
39429 	: TimeConstraint()
39430 {
39431 	this->type = CONSTRAINT_TEACHERS_MAX_ZERO_GAPS_PER_AFTERNOON;
39432 }
39433 
ConstraintTeachersMaxZeroGapsPerAfternoon(double wp)39434 ConstraintTeachersMaxZeroGapsPerAfternoon::ConstraintTeachersMaxZeroGapsPerAfternoon(double wp)
39435 	: TimeConstraint(wp)
39436 {
39437 	this->type = CONSTRAINT_TEACHERS_MAX_ZERO_GAPS_PER_AFTERNOON;
39438 }
39439 
computeInternalStructure(QWidget * parent,Rules & r)39440 bool ConstraintTeachersMaxZeroGapsPerAfternoon::computeInternalStructure(QWidget* parent, Rules& r)
39441 {
39442 	Q_UNUSED(parent);
39443 	Q_UNUSED(r);
39444 
39445 	return true;
39446 }
39447 
hasInactiveActivities(Rules & r)39448 bool ConstraintTeachersMaxZeroGapsPerAfternoon::hasInactiveActivities(Rules& r)
39449 {
39450 	Q_UNUSED(r);
39451 	return false;
39452 }
39453 
getXmlDescription(Rules & r)39454 QString ConstraintTeachersMaxZeroGapsPerAfternoon::getXmlDescription(Rules& r){
39455 	Q_UNUSED(r);
39456 
39457 	QString s="<ConstraintTeachersMaxZeroGapsPerAfternoon>\n";
39458 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
39459 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
39460 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
39461 	s+="</ConstraintTeachersMaxZeroGapsPerAfternoon>\n";
39462 	return s;
39463 }
39464 
getDescription(Rules & r)39465 QString ConstraintTeachersMaxZeroGapsPerAfternoon::getDescription(Rules& r){
39466 	Q_UNUSED(r);
39467 
39468 	QString begin=QString("");
39469 	if(!active)
39470 		begin="X - ";
39471 
39472 	QString end=QString("");
39473 	if(!comments.isEmpty())
39474 		end=", "+tr("C: %1", "Comments").arg(comments);
39475 
39476 	QString s;
39477 	s+=tr("Teachers max zero gaps per afternoon");s+=", ";
39478 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
39479 
39480 	return begin+s+end;
39481 }
39482 
getDetailedDescription(Rules & r)39483 QString ConstraintTeachersMaxZeroGapsPerAfternoon::getDetailedDescription(Rules& r){
39484 	Q_UNUSED(r);
39485 
39486 	QString s=tr("Time constraint"); s+="\n";
39487 	s+=tr("All teachers must respect a zero number of gaps per afternoon"); s+="\n";
39488 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
39489 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
39490 
39491 	if(!active){
39492 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
39493 		s+="\n";
39494 	}
39495 	if(!comments.isEmpty()){
39496 		s+=tr("Comments=%1").arg(comments);
39497 		s+="\n";
39498 	}
39499 
39500 	return s;
39501 }
39502 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)39503 double ConstraintTeachersMaxZeroGapsPerAfternoon::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
39504 {
39505 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
39506 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
39507 		c.teachersMatrixReady=true;
39508 		c.subgroupsMatrixReady=true;
39509 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
39510 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
39511 
39512 		c.changedForMatrixCalculation=false;
39513 	}
39514 
39515 	int tg;
39516 	int i, j, k;
39517 	int totalGaps;
39518 
39519 	totalGaps=0;
39520 
39521 	for(i=0; i<r.nInternalTeachers; i++){
39522 		for(j=0; j<r.nDaysPerWeek; j++){
39523 			if(j%2==0)
39524 				continue;
39525 			tg=0;
39526 			for(k=0; k<r.nHoursPerDay; k++)
39527 				if(teachersMatrix[i][j][k]>0){
39528 					assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
39529 					break;
39530 				}
39531 
39532 			int cnt=0;
39533 			for(; k<r.nHoursPerDay; k++) if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
39534 				if(teachersMatrix[i][j][k]>0){
39535 					tg+=cnt;
39536 					cnt=0;
39537 				}
39538 				else
39539 					cnt++;
39540 			}
39541 			if(tg>0){
39542 				totalGaps+=tg;
39543 				//assert(this->weightPercentage<100); partial solutions might break this rule
39544 				if(conflictsString!=nullptr){
39545 					QString s=tr("Time constraint teachers max zero gaps per afternoon broken for teacher: %1, day: %2, conflicts factor increase=%3")
39546 						.arg(r.internalTeachersList[i]->name)
39547 						.arg(r.daysOfTheWeek[j])
39548 						.arg(CustomFETString::numberPlusTwoDigitsPrecision(tg*weightPercentage/100));
39549 
39550 					*conflictsString+= s+"\n";
39551 
39552 					dl.append(s);
39553 					cl.append(tg*weightPercentage/100);
39554 				}
39555 			}
39556 		}
39557 	}
39558 
39559 	if(c.nPlacedActivities==r.nInternalActivities)
39560 		if(weightPercentage==100)
39561 			assert(totalGaps==0); //for partial solutions this rule might be broken
39562 	return weightPercentage/100 * totalGaps;
39563 }
39564 
isRelatedToActivity(Rules & r,Activity * a)39565 bool ConstraintTeachersMaxZeroGapsPerAfternoon::isRelatedToActivity(Rules& r, Activity* a)
39566 {
39567 	Q_UNUSED(r);
39568 	Q_UNUSED(a);
39569 
39570 	return false;
39571 }
39572 
isRelatedToTeacher(Teacher * t)39573 bool ConstraintTeachersMaxZeroGapsPerAfternoon::isRelatedToTeacher(Teacher* t)
39574 {
39575 	Q_UNUSED(t);
39576 
39577 	return true;
39578 }
39579 
isRelatedToSubject(Subject * s)39580 bool ConstraintTeachersMaxZeroGapsPerAfternoon::isRelatedToSubject(Subject* s)
39581 {
39582 	Q_UNUSED(s);
39583 
39584 	return false;
39585 }
39586 
isRelatedToActivityTag(ActivityTag * s)39587 bool ConstraintTeachersMaxZeroGapsPerAfternoon::isRelatedToActivityTag(ActivityTag* s)
39588 {
39589 	Q_UNUSED(s);
39590 
39591 	return false;
39592 }
39593 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)39594 bool ConstraintTeachersMaxZeroGapsPerAfternoon::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
39595 {
39596 	Q_UNUSED(r);
39597 	Q_UNUSED(s);
39598 
39599 	return false;
39600 }
39601 
hasWrongDayOrHour(Rules & r)39602 bool ConstraintTeachersMaxZeroGapsPerAfternoon::hasWrongDayOrHour(Rules& r)
39603 {
39604 	Q_UNUSED(r);
39605 
39606 	return false;
39607 }
39608 
canRepairWrongDayOrHour(Rules & r)39609 bool ConstraintTeachersMaxZeroGapsPerAfternoon::canRepairWrongDayOrHour(Rules& r)
39610 {
39611 	assert(hasWrongDayOrHour(r));
39612 
39613 	return true;
39614 }
39615 
repairWrongDayOrHour(Rules & r)39616 bool ConstraintTeachersMaxZeroGapsPerAfternoon::repairWrongDayOrHour(Rules& r)
39617 {
39618 	assert(hasWrongDayOrHour(r));
39619 
39620 	return true;
39621 }
39622 
39623 //2020-06-25
39624 ///////////////////////////////////////////////////////////////////////////////////////////
39625 ///////////////////////////////////////////////////////////////////////////////////////////
39626 
ConstraintStudentsSetMaxAfternoonsPerWeek()39627 ConstraintStudentsSetMaxAfternoonsPerWeek::ConstraintStudentsSetMaxAfternoonsPerWeek()
39628 	: TimeConstraint()
39629 {
39630 	this->type=CONSTRAINT_STUDENTS_SET_MAX_AFTERNOONS_PER_WEEK;
39631 }
39632 
ConstraintStudentsSetMaxAfternoonsPerWeek(double wp,int maxnd,const QString & sn)39633 ConstraintStudentsSetMaxAfternoonsPerWeek::ConstraintStudentsSetMaxAfternoonsPerWeek(double wp, int maxnd, const QString& sn)
39634 	 : TimeConstraint(wp)
39635 {
39636 	this->students = sn;
39637 	this->maxAfternoonsPerWeek=maxnd;
39638 	this->type=CONSTRAINT_STUDENTS_SET_MAX_AFTERNOONS_PER_WEEK;
39639 }
39640 
computeInternalStructure(QWidget * parent,Rules & r)39641 bool ConstraintStudentsSetMaxAfternoonsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
39642 {
39643 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
39644 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
39645 
39646 	if(ss==nullptr){
39647 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
39648 		 tr("Constraint students set max afternoons per week is wrong because it refers to inexistent students set."
39649 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
39650 
39651 		return false;
39652 	}
39653 
39654 	assert(ss!=nullptr);
39655 
39656 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
39657 	/*this->iSubgroupsList.clear();
39658 	if(ss->type==STUDENTS_SUBGROUP){
39659 		int tmp;
39660 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
39661 		assert(tmp>=0);
39662 		assert(tmp<r.nInternalSubgroups);
39663 		if(!this->iSubgroupsList.contains(tmp))
39664 			this->iSubgroupsList.append(tmp);
39665 	}
39666 	else if(ss->type==STUDENTS_GROUP){
39667 		StudentsGroup* stg=(StudentsGroup*)ss;
39668 		for(int i=0; i<stg->subgroupsList.size(); i++){
39669 			StudentsSubgroup* sts=stg->subgroupsList[i];
39670 			int tmp;
39671 			tmp=sts->indexInInternalSubgroupsList;
39672 			assert(tmp>=0);
39673 			assert(tmp<r.nInternalSubgroups);
39674 			if(!this->iSubgroupsList.contains(tmp))
39675 				this->iSubgroupsList.append(tmp);
39676 		}
39677 	}
39678 	else if(ss->type==STUDENTS_YEAR){
39679 		StudentsYear* sty=(StudentsYear*)ss;
39680 		for(int i=0; i<sty->groupsList.size(); i++){
39681 			StudentsGroup* stg=sty->groupsList[i];
39682 			for(int j=0; j<stg->subgroupsList.size(); j++){
39683 				StudentsSubgroup* sts=stg->subgroupsList[j];
39684 				int tmp;
39685 				tmp=sts->indexInInternalSubgroupsList;
39686 				assert(tmp>=0);
39687 				assert(tmp<r.nInternalSubgroups);
39688 				if(!this->iSubgroupsList.contains(tmp))
39689 					this->iSubgroupsList.append(tmp);
39690 			}
39691 		}
39692 	}
39693 	else
39694 		assert(0);*/
39695 
39696 	return true;
39697 }
39698 
hasInactiveActivities(Rules & r)39699 bool ConstraintStudentsSetMaxAfternoonsPerWeek::hasInactiveActivities(Rules& r)
39700 {
39701 	Q_UNUSED(r);
39702 	return false;
39703 }
39704 
getXmlDescription(Rules & r)39705 QString ConstraintStudentsSetMaxAfternoonsPerWeek::getXmlDescription(Rules& r)
39706 {
39707 	Q_UNUSED(r);
39708 
39709 	QString s="<ConstraintStudentsSetMaxAfternoonsPerWeek>\n";
39710 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
39711 	s+="	<Students>"+protect(this->students)+"</Students>\n";
39712 	s+="	<Max_Afternoons_Per_Week>"+CustomFETString::number(this->maxAfternoonsPerWeek)+"</Max_Afternoons_Per_Week>\n";
39713 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
39714 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
39715 	s+="</ConstraintStudentsSetMaxAfternoonsPerWeek>\n";
39716 	return s;
39717 }
39718 
getDescription(Rules & r)39719 QString ConstraintStudentsSetMaxAfternoonsPerWeek::getDescription(Rules& r){
39720 	Q_UNUSED(r);
39721 
39722 	QString begin=QString("");
39723 	if(!active)
39724 		begin="X - ";
39725 
39726 	QString end=QString("");
39727 	if(!comments.isEmpty())
39728 		end=", "+tr("C: %1", "Comments").arg(comments);
39729 
39730 	QString s=tr("Students set max afternoons per week");s+=", ";
39731 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
39732 	s+=tr("St:%1", "Abbreviation for students (sets)").arg(this->students);s+=", ";
39733 	s+=tr("MA:%1", "Abbreviation for max afternoons").arg(this->maxAfternoonsPerWeek);
39734 
39735 	return begin+s+end;
39736 }
39737 
getDetailedDescription(Rules & r)39738 QString ConstraintStudentsSetMaxAfternoonsPerWeek::getDetailedDescription(Rules& r){
39739 	Q_UNUSED(r);
39740 
39741 	QString s=tr("Time constraint");s+="\n";
39742 	s+=tr("A students set must respect the maximum number of afternoons per week");s+="\n";
39743 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
39744 	s+=tr("Students set=%1").arg(this->students);s+="\n";
39745 
39746 	s+=tr("Maximum afternoons per week=%1").arg(this->maxAfternoonsPerWeek);s+="\n";
39747 
39748 	if(!active){
39749 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
39750 		s+="\n";
39751 	}
39752 	if(!comments.isEmpty()){
39753 		s+=tr("Comments=%1").arg(comments);
39754 		s+="\n";
39755 	}
39756 
39757 	return s;
39758 }
39759 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)39760 double ConstraintStudentsSetMaxAfternoonsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
39761 {
39762 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
39763 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
39764 		c.teachersMatrixReady=true;
39765 		c.subgroupsMatrixReady=true;
39766 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
39767 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
39768 
39769 		c.changedForMatrixCalculation=false;
39770 	}
39771 
39772 	int nbroken;
39773 
39774 	nbroken=0;
39775 
39776 	for(int sbg : qAsConst(this->iSubgroupsList)){
39777 		/*bool ocDay[MAX_DAYS_PER_WEEK];
39778 		for(int d=0; d<r.nDaysPerWeek; d++){
39779 			ocDay[d]=false;
39780 			for(int h=0; h<r.nHoursPerDay; h++){
39781 				if(subgroupsMatrix[sbg][d][h]>0){
39782 					ocDay[d]=true;
39783 				}
39784 			}
39785 		}*/
39786 		int nOcDays=0;
39787 		/*for(int d=0; d<r.nDaysPerWeek; d++)
39788 			if(ocDay[d])
39789 				nOcDays++;*/
39790 		for(int d=0; d<r.nDaysPerWeek/2; d++){
39791 			int nh=0;
39792 			/*for(int h=0; h<r.nHoursPerDay; h++)
39793 				nh += subgroupsMatrix[sbg][2*d][h]>=1 ? 1 : 0;*/
39794 			for(int h=0; h<r.nHoursPerDay; h++)
39795 				nh += subgroupsMatrix[sbg][2*d+1][h]>=1 ? 1 : 0;
39796 			if(nh>0)
39797 				nOcDays++;
39798 		}
39799 		if(nOcDays > this->maxAfternoonsPerWeek){
39800 			nbroken+=nOcDays-this->maxAfternoonsPerWeek;
39801 
39802 			if((nOcDays-this->maxAfternoonsPerWeek)>0){
39803 				QString s= tr("Time constraint students set max afternoons per week broken for subgroup: %1, allowed %2 afternoons, required %3 afternoons.")
39804 				 .arg(r.internalSubgroupsList[sbg]->name)
39805 				 .arg(this->maxAfternoonsPerWeek)
39806 				 .arg(nOcDays);
39807 				s+=" ";
39808 				s += tr("This increases the conflicts total by %1")
39809 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxAfternoonsPerWeek)*weightPercentage/100));
39810 
39811 				dl.append(s);
39812 				cl.append((nOcDays-this->maxAfternoonsPerWeek)*weightPercentage/100);
39813 
39814 				*conflictsString += s+"\n";
39815 			}
39816 		}
39817 	}
39818 
39819 	if(weightPercentage==100)
39820 		assert(nbroken==0);
39821 	return weightPercentage/100 * nbroken;
39822 }
39823 
isRelatedToActivity(Rules & r,Activity * a)39824 bool ConstraintStudentsSetMaxAfternoonsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
39825 {
39826 	Q_UNUSED(r);
39827 	Q_UNUSED(a);
39828 
39829 	return false;
39830 }
39831 
isRelatedToTeacher(Teacher * t)39832 bool ConstraintStudentsSetMaxAfternoonsPerWeek::isRelatedToTeacher(Teacher* t)
39833 {
39834 	Q_UNUSED(t);
39835 	return false;
39836 }
39837 
isRelatedToSubject(Subject * s)39838 bool ConstraintStudentsSetMaxAfternoonsPerWeek::isRelatedToSubject(Subject* s)
39839 {
39840 	Q_UNUSED(s);
39841 
39842 	return false;
39843 }
39844 
isRelatedToActivityTag(ActivityTag * s)39845 bool ConstraintStudentsSetMaxAfternoonsPerWeek::isRelatedToActivityTag(ActivityTag* s)
39846 {
39847 	Q_UNUSED(s);
39848 
39849 	return false;
39850 }
39851 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)39852 bool ConstraintStudentsSetMaxAfternoonsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
39853 {
39854 	return r.setsShareStudents(this->students, s->name);
39855 }
39856 
hasWrongDayOrHour(Rules & r)39857 bool ConstraintStudentsSetMaxAfternoonsPerWeek::hasWrongDayOrHour(Rules& r)
39858 {
39859 	if(this->maxAfternoonsPerWeek>r.nDaysPerWeek/2)
39860 		return true;
39861 
39862 	return false;
39863 }
39864 
canRepairWrongDayOrHour(Rules & r)39865 bool ConstraintStudentsSetMaxAfternoonsPerWeek::canRepairWrongDayOrHour(Rules& r)
39866 {
39867 	assert(hasWrongDayOrHour(r));
39868 
39869 	return true;
39870 }
39871 
repairWrongDayOrHour(Rules & r)39872 bool ConstraintStudentsSetMaxAfternoonsPerWeek::repairWrongDayOrHour(Rules& r)
39873 {
39874 	assert(hasWrongDayOrHour(r));
39875 
39876 	if(this->maxAfternoonsPerWeek>r.nDaysPerWeek/2)
39877 		this->maxAfternoonsPerWeek=r.nDaysPerWeek/2;
39878 
39879 	return true;
39880 }
39881 
39882 ///////////////////////////////////////////////////////////////////////////////////////////
39883 ///////////////////////////////////////////////////////////////////////////////////////////
39884 
ConstraintStudentsMaxAfternoonsPerWeek()39885 ConstraintStudentsMaxAfternoonsPerWeek::ConstraintStudentsMaxAfternoonsPerWeek()
39886 	: TimeConstraint()
39887 {
39888 	this->type=CONSTRAINT_STUDENTS_MAX_AFTERNOONS_PER_WEEK;
39889 }
39890 
ConstraintStudentsMaxAfternoonsPerWeek(double wp,int maxnd)39891 ConstraintStudentsMaxAfternoonsPerWeek::ConstraintStudentsMaxAfternoonsPerWeek(double wp, int maxnd)
39892 	 : TimeConstraint(wp)
39893 {
39894 	this->maxAfternoonsPerWeek=maxnd;
39895 	this->type=CONSTRAINT_STUDENTS_MAX_AFTERNOONS_PER_WEEK;
39896 }
39897 
computeInternalStructure(QWidget * parent,Rules & r)39898 bool ConstraintStudentsMaxAfternoonsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
39899 {
39900 	Q_UNUSED(parent);
39901 	Q_UNUSED(r);
39902 
39903 	return true;
39904 }
39905 
hasInactiveActivities(Rules & r)39906 bool ConstraintStudentsMaxAfternoonsPerWeek::hasInactiveActivities(Rules& r)
39907 {
39908 	Q_UNUSED(r);
39909 	return false;
39910 }
39911 
getXmlDescription(Rules & r)39912 QString ConstraintStudentsMaxAfternoonsPerWeek::getXmlDescription(Rules& r)
39913 {
39914 	Q_UNUSED(r);
39915 
39916 	QString s="<ConstraintStudentsMaxAfternoonsPerWeek>\n";
39917 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
39918 	s+="	<Max_Afternoons_Per_Week>"+CustomFETString::number(this->maxAfternoonsPerWeek)+"</Max_Afternoons_Per_Week>\n";
39919 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
39920 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
39921 	s+="</ConstraintStudentsMaxAfternoonsPerWeek>\n";
39922 	return s;
39923 }
39924 
getDescription(Rules & r)39925 QString ConstraintStudentsMaxAfternoonsPerWeek::getDescription(Rules& r){
39926 	Q_UNUSED(r);
39927 
39928 	QString begin=QString("");
39929 	if(!active)
39930 		begin="X - ";
39931 
39932 	QString end=QString("");
39933 	if(!comments.isEmpty())
39934 		end=", "+tr("C: %1", "Comments").arg(comments);
39935 
39936 	QString s=tr("Students max afternoons per week");s+=", ";
39937 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
39938 	s+=tr("MA:%1", "Abbreviation for max afternoons").arg(this->maxAfternoonsPerWeek);
39939 
39940 	return begin+s+end;
39941 }
39942 
getDetailedDescription(Rules & r)39943 QString ConstraintStudentsMaxAfternoonsPerWeek::getDetailedDescription(Rules& r){
39944 	Q_UNUSED(r);
39945 
39946 	QString s=tr("Time constraint");s+="\n";
39947 	s+=tr("All students must respect the maximum number of afternoons per week");s+="\n";
39948 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
39949 
39950 	s+=tr("Maximum afternoons per week=%1").arg(this->maxAfternoonsPerWeek);s+="\n";
39951 
39952 	if(!active){
39953 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
39954 		s+="\n";
39955 	}
39956 	if(!comments.isEmpty()){
39957 		s+=tr("Comments=%1").arg(comments);
39958 		s+="\n";
39959 	}
39960 
39961 	return s;
39962 }
39963 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)39964 double ConstraintStudentsMaxAfternoonsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
39965 {
39966 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
39967 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
39968 		c.teachersMatrixReady=true;
39969 		c.subgroupsMatrixReady=true;
39970 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
39971 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
39972 
39973 		c.changedForMatrixCalculation=false;
39974 	}
39975 
39976 	int nbroken;
39977 
39978 	nbroken=0;
39979 
39980 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
39981 		/*bool ocDay[MAX_DAYS_PER_WEEK];
39982 		for(int d=0; d<r.nDaysPerWeek; d++){
39983 			ocDay[d]=false;
39984 			for(int h=0; h<r.nHoursPerDay; h++){
39985 				if(subgroupsMatrix[sbg][d][h]>0){
39986 					ocDay[d]=true;
39987 				}
39988 			}
39989 		}*/
39990 		int nOcDays=0;
39991 		/*for(int d=0; d<r.nDaysPerWeek; d++)
39992 			if(ocDay[d])
39993 				nOcDays++;*/
39994 		for(int d=0; d<r.nDaysPerWeek/2; d++){
39995 			int nh=0;
39996 			/*for(int h=0; h<r.nHoursPerDay; h++)
39997 				nh += subgroupsMatrix[sbg][2*d][h]>=1 ? 1 : 0;*/
39998 			for(int h=0; h<r.nHoursPerDay; h++)
39999 				nh += subgroupsMatrix[sbg][2*d+1][h]>=1 ? 1 : 0;
40000 			if(nh>0)
40001 				nOcDays++;
40002 		}
40003 		if(nOcDays > this->maxAfternoonsPerWeek){
40004 			nbroken+=nOcDays-this->maxAfternoonsPerWeek;
40005 
40006 			if((nOcDays-this->maxAfternoonsPerWeek)>0){
40007 				QString s= tr("Time constraint students max afternoons per week broken for subgroup: %1, allowed %2 afternoons, required %3 afternoons.")
40008 				 .arg(r.internalSubgroupsList[sbg]->name)
40009 				 .arg(this->maxAfternoonsPerWeek)
40010 				 .arg(nOcDays);
40011 				s+=" ";
40012 				s += tr("This increases the conflicts total by %1")
40013 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxAfternoonsPerWeek)*weightPercentage/100));
40014 
40015 				dl.append(s);
40016 				cl.append((nOcDays-this->maxAfternoonsPerWeek)*weightPercentage/100);
40017 
40018 				*conflictsString += s+"\n";
40019 			}
40020 		}
40021 	}
40022 
40023 	if(weightPercentage==100)
40024 		assert(nbroken==0);
40025 	return weightPercentage/100 * nbroken;
40026 }
40027 
isRelatedToActivity(Rules & r,Activity * a)40028 bool ConstraintStudentsMaxAfternoonsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
40029 {
40030 	Q_UNUSED(r);
40031 	Q_UNUSED(a);
40032 
40033 	return false;
40034 }
40035 
isRelatedToTeacher(Teacher * t)40036 bool ConstraintStudentsMaxAfternoonsPerWeek::isRelatedToTeacher(Teacher* t)
40037 {
40038 	Q_UNUSED(t);
40039 	return false;
40040 }
40041 
isRelatedToSubject(Subject * s)40042 bool ConstraintStudentsMaxAfternoonsPerWeek::isRelatedToSubject(Subject* s)
40043 {
40044 	Q_UNUSED(s);
40045 
40046 	return false;
40047 }
40048 
isRelatedToActivityTag(ActivityTag * s)40049 bool ConstraintStudentsMaxAfternoonsPerWeek::isRelatedToActivityTag(ActivityTag* s)
40050 {
40051 	Q_UNUSED(s);
40052 
40053 	return false;
40054 }
40055 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)40056 bool ConstraintStudentsMaxAfternoonsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
40057 {
40058 	Q_UNUSED(r);
40059 	Q_UNUSED(s);
40060 
40061 	return true;
40062 }
40063 
hasWrongDayOrHour(Rules & r)40064 bool ConstraintStudentsMaxAfternoonsPerWeek::hasWrongDayOrHour(Rules& r)
40065 {
40066 	if(this->maxAfternoonsPerWeek>r.nDaysPerWeek/2)
40067 		return true;
40068 
40069 	return false;
40070 }
40071 
canRepairWrongDayOrHour(Rules & r)40072 bool ConstraintStudentsMaxAfternoonsPerWeek::canRepairWrongDayOrHour(Rules& r)
40073 {
40074 	assert(hasWrongDayOrHour(r));
40075 
40076 	return true;
40077 }
40078 
repairWrongDayOrHour(Rules & r)40079 bool ConstraintStudentsMaxAfternoonsPerWeek::repairWrongDayOrHour(Rules& r)
40080 {
40081 	assert(hasWrongDayOrHour(r));
40082 
40083 	if(this->maxAfternoonsPerWeek>r.nDaysPerWeek/2)
40084 		this->maxAfternoonsPerWeek=r.nDaysPerWeek/2;
40085 
40086 	return true;
40087 }
40088 
40089 ///////////////////////////////////////////////////////////////////////////////////////////
40090 ///////////////////////////////////////////////////////////////////////////////////////////
40091 
ConstraintStudentsSetMaxMorningsPerWeek()40092 ConstraintStudentsSetMaxMorningsPerWeek::ConstraintStudentsSetMaxMorningsPerWeek()
40093 	: TimeConstraint()
40094 {
40095 	this->type=CONSTRAINT_STUDENTS_SET_MAX_MORNINGS_PER_WEEK;
40096 }
40097 
ConstraintStudentsSetMaxMorningsPerWeek(double wp,int maxnd,const QString & sn)40098 ConstraintStudentsSetMaxMorningsPerWeek::ConstraintStudentsSetMaxMorningsPerWeek(double wp, int maxnd, const QString& sn)
40099 	 : TimeConstraint(wp)
40100 {
40101 	this->students = sn;
40102 	this->maxMorningsPerWeek=maxnd;
40103 	this->type=CONSTRAINT_STUDENTS_SET_MAX_MORNINGS_PER_WEEK;
40104 }
40105 
computeInternalStructure(QWidget * parent,Rules & r)40106 bool ConstraintStudentsSetMaxMorningsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
40107 {
40108 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
40109 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
40110 
40111 	if(ss==nullptr){
40112 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
40113 		 tr("Constraint students set max mornings per week is wrong because it refers to inexistent students set."
40114 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
40115 
40116 		return false;
40117 	}
40118 
40119 	assert(ss!=nullptr);
40120 
40121 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
40122 	/*this->iSubgroupsList.clear();
40123 	if(ss->type==STUDENTS_SUBGROUP){
40124 		int tmp;
40125 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
40126 		assert(tmp>=0);
40127 		assert(tmp<r.nInternalSubgroups);
40128 		if(!this->iSubgroupsList.contains(tmp))
40129 			this->iSubgroupsList.append(tmp);
40130 	}
40131 	else if(ss->type==STUDENTS_GROUP){
40132 		StudentsGroup* stg=(StudentsGroup*)ss;
40133 		for(int i=0; i<stg->subgroupsList.size(); i++){
40134 			StudentsSubgroup* sts=stg->subgroupsList[i];
40135 			int tmp;
40136 			tmp=sts->indexInInternalSubgroupsList;
40137 			assert(tmp>=0);
40138 			assert(tmp<r.nInternalSubgroups);
40139 			if(!this->iSubgroupsList.contains(tmp))
40140 				this->iSubgroupsList.append(tmp);
40141 		}
40142 	}
40143 	else if(ss->type==STUDENTS_YEAR){
40144 		StudentsYear* sty=(StudentsYear*)ss;
40145 		for(int i=0; i<sty->groupsList.size(); i++){
40146 			StudentsGroup* stg=sty->groupsList[i];
40147 			for(int j=0; j<stg->subgroupsList.size(); j++){
40148 				StudentsSubgroup* sts=stg->subgroupsList[j];
40149 				int tmp;
40150 				tmp=sts->indexInInternalSubgroupsList;
40151 				assert(tmp>=0);
40152 				assert(tmp<r.nInternalSubgroups);
40153 				if(!this->iSubgroupsList.contains(tmp))
40154 					this->iSubgroupsList.append(tmp);
40155 			}
40156 		}
40157 	}
40158 	else
40159 		assert(0);*/
40160 
40161 	return true;
40162 }
40163 
hasInactiveActivities(Rules & r)40164 bool ConstraintStudentsSetMaxMorningsPerWeek::hasInactiveActivities(Rules& r)
40165 {
40166 	Q_UNUSED(r);
40167 	return false;
40168 }
40169 
getXmlDescription(Rules & r)40170 QString ConstraintStudentsSetMaxMorningsPerWeek::getXmlDescription(Rules& r)
40171 {
40172 	Q_UNUSED(r);
40173 
40174 	QString s="<ConstraintStudentsSetMaxMorningsPerWeek>\n";
40175 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
40176 	s+="	<Students>"+protect(this->students)+"</Students>\n";
40177 	s+="	<Max_Mornings_Per_Week>"+CustomFETString::number(this->maxMorningsPerWeek)+"</Max_Mornings_Per_Week>\n";
40178 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
40179 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
40180 	s+="</ConstraintStudentsSetMaxMorningsPerWeek>\n";
40181 	return s;
40182 }
40183 
getDescription(Rules & r)40184 QString ConstraintStudentsSetMaxMorningsPerWeek::getDescription(Rules& r){
40185 	Q_UNUSED(r);
40186 
40187 	QString begin=QString("");
40188 	if(!active)
40189 		begin="X - ";
40190 
40191 	QString end=QString("");
40192 	if(!comments.isEmpty())
40193 		end=", "+tr("C: %1", "Comments").arg(comments);
40194 
40195 	QString s=tr("Students set max mornings per week");s+=", ";
40196 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
40197 	s+=tr("St:%1", "Abbreviation for students (sets)").arg(this->students);s+=", ";
40198 	s+=tr("MM:%1", "Abbreviation for max mornings").arg(this->maxMorningsPerWeek);
40199 
40200 	return begin+s+end;
40201 }
40202 
getDetailedDescription(Rules & r)40203 QString ConstraintStudentsSetMaxMorningsPerWeek::getDetailedDescription(Rules& r){
40204 	Q_UNUSED(r);
40205 
40206 	QString s=tr("Time constraint");s+="\n";
40207 	s+=tr("A students set must respect the maximum number of mornings per week");s+="\n";
40208 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
40209 	s+=tr("Students set=%1").arg(this->students);s+="\n";
40210 
40211 	s+=tr("Maximum mornings per week=%1").arg(this->maxMorningsPerWeek);s+="\n";
40212 
40213 	if(!active){
40214 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
40215 		s+="\n";
40216 	}
40217 	if(!comments.isEmpty()){
40218 		s+=tr("Comments=%1").arg(comments);
40219 		s+="\n";
40220 	}
40221 
40222 	return s;
40223 }
40224 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)40225 double ConstraintStudentsSetMaxMorningsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
40226 {
40227 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
40228 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
40229 		c.teachersMatrixReady=true;
40230 		c.subgroupsMatrixReady=true;
40231 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
40232 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
40233 
40234 		c.changedForMatrixCalculation=false;
40235 	}
40236 
40237 	int nbroken;
40238 
40239 	nbroken=0;
40240 
40241 	for(int sbg : qAsConst(this->iSubgroupsList)){
40242 		/*bool ocDay[MAX_DAYS_PER_WEEK];
40243 		for(int d=0; d<r.nDaysPerWeek; d++){
40244 			ocDay[d]=false;
40245 			for(int h=0; h<r.nHoursPerDay; h++){
40246 				if(subgroupsMatrix[sbg][d][h]>0){
40247 					ocDay[d]=true;
40248 				}
40249 			}
40250 		}*/
40251 		int nOcDays=0;
40252 		/*for(int d=0; d<r.nDaysPerWeek; d++)
40253 			if(ocDay[d])
40254 				nOcDays++;*/
40255 		for(int d=0; d<r.nDaysPerWeek/2; d++){
40256 			int nh=0;
40257 			for(int h=0; h<r.nHoursPerDay; h++)
40258 				nh += subgroupsMatrix[sbg][2*d][h]>=1 ? 1 : 0;
40259 			/*for(int h=0; h<r.nHoursPerDay; h++)
40260 				nh += subgroupsMatrix[sbg][2*d+1][h]>=1 ? 1 : 0;*/
40261 			if(nh>0)
40262 				nOcDays++;
40263 		}
40264 		if(nOcDays > this->maxMorningsPerWeek){
40265 			nbroken+=nOcDays-this->maxMorningsPerWeek;
40266 
40267 			if((nOcDays-this->maxMorningsPerWeek)>0){
40268 				QString s= tr("Time constraint students set max mornings per week broken for subgroup: %1, allowed %2 mornings, required %3 mornings.")
40269 				 .arg(r.internalSubgroupsList[sbg]->name)
40270 				 .arg(this->maxMorningsPerWeek)
40271 				 .arg(nOcDays);
40272 				s+=" ";
40273 				s += tr("This increases the conflicts total by %1")
40274 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxMorningsPerWeek)*weightPercentage/100));
40275 
40276 				dl.append(s);
40277 				cl.append((nOcDays-this->maxMorningsPerWeek)*weightPercentage/100);
40278 
40279 				*conflictsString += s+"\n";
40280 			}
40281 		}
40282 	}
40283 
40284 	if(weightPercentage==100)
40285 		assert(nbroken==0);
40286 	return weightPercentage/100 * nbroken;
40287 }
40288 
isRelatedToActivity(Rules & r,Activity * a)40289 bool ConstraintStudentsSetMaxMorningsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
40290 {
40291 	Q_UNUSED(r);
40292 	Q_UNUSED(a);
40293 
40294 	return false;
40295 }
40296 
isRelatedToTeacher(Teacher * t)40297 bool ConstraintStudentsSetMaxMorningsPerWeek::isRelatedToTeacher(Teacher* t)
40298 {
40299 	Q_UNUSED(t);
40300 	return false;
40301 }
40302 
isRelatedToSubject(Subject * s)40303 bool ConstraintStudentsSetMaxMorningsPerWeek::isRelatedToSubject(Subject* s)
40304 {
40305 	Q_UNUSED(s);
40306 
40307 	return false;
40308 }
40309 
isRelatedToActivityTag(ActivityTag * s)40310 bool ConstraintStudentsSetMaxMorningsPerWeek::isRelatedToActivityTag(ActivityTag* s)
40311 {
40312 	Q_UNUSED(s);
40313 
40314 	return false;
40315 }
40316 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)40317 bool ConstraintStudentsSetMaxMorningsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
40318 {
40319 	return r.setsShareStudents(this->students, s->name);
40320 }
40321 
hasWrongDayOrHour(Rules & r)40322 bool ConstraintStudentsSetMaxMorningsPerWeek::hasWrongDayOrHour(Rules& r)
40323 {
40324 	if(this->maxMorningsPerWeek>r.nDaysPerWeek/2)
40325 		return true;
40326 
40327 	return false;
40328 }
40329 
canRepairWrongDayOrHour(Rules & r)40330 bool ConstraintStudentsSetMaxMorningsPerWeek::canRepairWrongDayOrHour(Rules& r)
40331 {
40332 	assert(hasWrongDayOrHour(r));
40333 
40334 	return true;
40335 }
40336 
repairWrongDayOrHour(Rules & r)40337 bool ConstraintStudentsSetMaxMorningsPerWeek::repairWrongDayOrHour(Rules& r)
40338 {
40339 	assert(hasWrongDayOrHour(r));
40340 
40341 	if(this->maxMorningsPerWeek>r.nDaysPerWeek/2)
40342 		this->maxMorningsPerWeek=r.nDaysPerWeek/2;
40343 
40344 	return true;
40345 }
40346 
40347 ///////////////////////////////////////////////////////////////////////////////////////////
40348 ///////////////////////////////////////////////////////////////////////////////////////////
40349 
ConstraintStudentsMaxMorningsPerWeek()40350 ConstraintStudentsMaxMorningsPerWeek::ConstraintStudentsMaxMorningsPerWeek()
40351 	: TimeConstraint()
40352 {
40353 	this->type=CONSTRAINT_STUDENTS_MAX_MORNINGS_PER_WEEK;
40354 }
40355 
ConstraintStudentsMaxMorningsPerWeek(double wp,int maxnd)40356 ConstraintStudentsMaxMorningsPerWeek::ConstraintStudentsMaxMorningsPerWeek(double wp, int maxnd)
40357 	 : TimeConstraint(wp)
40358 {
40359 	this->maxMorningsPerWeek=maxnd;
40360 	this->type=CONSTRAINT_STUDENTS_MAX_MORNINGS_PER_WEEK;
40361 }
40362 
computeInternalStructure(QWidget * parent,Rules & r)40363 bool ConstraintStudentsMaxMorningsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
40364 {
40365 	Q_UNUSED(parent);
40366 	Q_UNUSED(r);
40367 
40368 	return true;
40369 }
40370 
hasInactiveActivities(Rules & r)40371 bool ConstraintStudentsMaxMorningsPerWeek::hasInactiveActivities(Rules& r)
40372 {
40373 	Q_UNUSED(r);
40374 	return false;
40375 }
40376 
getXmlDescription(Rules & r)40377 QString ConstraintStudentsMaxMorningsPerWeek::getXmlDescription(Rules& r)
40378 {
40379 	Q_UNUSED(r);
40380 
40381 	QString s="<ConstraintStudentsMaxMorningsPerWeek>\n";
40382 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
40383 	s+="	<Max_Mornings_Per_Week>"+CustomFETString::number(this->maxMorningsPerWeek)+"</Max_Mornings_Per_Week>\n";
40384 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
40385 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
40386 	s+="</ConstraintStudentsMaxMorningsPerWeek>\n";
40387 	return s;
40388 }
40389 
getDescription(Rules & r)40390 QString ConstraintStudentsMaxMorningsPerWeek::getDescription(Rules& r){
40391 	Q_UNUSED(r);
40392 
40393 	QString begin=QString("");
40394 	if(!active)
40395 		begin="X - ";
40396 
40397 	QString end=QString("");
40398 	if(!comments.isEmpty())
40399 		end=", "+tr("C: %1", "Comments").arg(comments);
40400 
40401 	QString s=tr("Students max mornings per week");s+=", ";
40402 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
40403 	s+=tr("MM:%1", "Abbreviation for max mornings").arg(this->maxMorningsPerWeek);
40404 
40405 	return begin+s+end;
40406 }
40407 
getDetailedDescription(Rules & r)40408 QString ConstraintStudentsMaxMorningsPerWeek::getDetailedDescription(Rules& r){
40409 	Q_UNUSED(r);
40410 
40411 	QString s=tr("Time constraint");s+="\n";
40412 	s+=tr("All students must respect the maximum number of mornings per week");s+="\n";
40413 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
40414 
40415 	s+=tr("Maximum mornings per week=%1").arg(this->maxMorningsPerWeek);s+="\n";
40416 
40417 	if(!active){
40418 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
40419 		s+="\n";
40420 	}
40421 	if(!comments.isEmpty()){
40422 		s+=tr("Comments=%1").arg(comments);
40423 		s+="\n";
40424 	}
40425 
40426 	return s;
40427 }
40428 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)40429 double ConstraintStudentsMaxMorningsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
40430 {
40431 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
40432 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
40433 		c.teachersMatrixReady=true;
40434 		c.subgroupsMatrixReady=true;
40435 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
40436 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
40437 
40438 		c.changedForMatrixCalculation=false;
40439 	}
40440 
40441 	int nbroken;
40442 
40443 	nbroken=0;
40444 
40445 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
40446 		/*bool ocDay[MAX_DAYS_PER_WEEK];
40447 		for(int d=0; d<r.nDaysPerWeek; d++){
40448 			ocDay[d]=false;
40449 			for(int h=0; h<r.nHoursPerDay; h++){
40450 				if(subgroupsMatrix[sbg][d][h]>0){
40451 					ocDay[d]=true;
40452 				}
40453 			}
40454 		}*/
40455 		int nOcDays=0;
40456 		/*for(int d=0; d<r.nDaysPerWeek; d++)
40457 			if(ocDay[d])
40458 				nOcDays++;*/
40459 		for(int d=0; d<r.nDaysPerWeek/2; d++){
40460 			int nh=0;
40461 			for(int h=0; h<r.nHoursPerDay; h++)
40462 				nh += subgroupsMatrix[sbg][2*d][h]>=1 ? 1 : 0;
40463 			/*for(int h=0; h<r.nHoursPerDay; h++)
40464 				nh += subgroupsMatrix[sbg][2*d+1][h]>=1 ? 1 : 0;*/
40465 			if(nh>0)
40466 				nOcDays++;
40467 		}
40468 		if(nOcDays > this->maxMorningsPerWeek){
40469 			nbroken+=nOcDays-this->maxMorningsPerWeek;
40470 
40471 			if((nOcDays-this->maxMorningsPerWeek)>0){
40472 				QString s= tr("Time constraint students max mornings per week broken for subgroup: %1, allowed %2 mornings, required %3 mornings.")
40473 				 .arg(r.internalSubgroupsList[sbg]->name)
40474 				 .arg(this->maxMorningsPerWeek)
40475 				 .arg(nOcDays);
40476 				s+=" ";
40477 				s += tr("This increases the conflicts total by %1")
40478 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxMorningsPerWeek)*weightPercentage/100));
40479 
40480 				dl.append(s);
40481 				cl.append((nOcDays-this->maxMorningsPerWeek)*weightPercentage/100);
40482 
40483 				*conflictsString += s+"\n";
40484 			}
40485 		}
40486 	}
40487 
40488 	if(weightPercentage==100)
40489 		assert(nbroken==0);
40490 	return weightPercentage/100 * nbroken;
40491 }
40492 
isRelatedToActivity(Rules & r,Activity * a)40493 bool ConstraintStudentsMaxMorningsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
40494 {
40495 	Q_UNUSED(r);
40496 	Q_UNUSED(a);
40497 
40498 	return false;
40499 }
40500 
isRelatedToTeacher(Teacher * t)40501 bool ConstraintStudentsMaxMorningsPerWeek::isRelatedToTeacher(Teacher* t)
40502 {
40503 	Q_UNUSED(t);
40504 	return false;
40505 }
40506 
isRelatedToSubject(Subject * s)40507 bool ConstraintStudentsMaxMorningsPerWeek::isRelatedToSubject(Subject* s)
40508 {
40509 	Q_UNUSED(s);
40510 
40511 	return false;
40512 }
40513 
isRelatedToActivityTag(ActivityTag * s)40514 bool ConstraintStudentsMaxMorningsPerWeek::isRelatedToActivityTag(ActivityTag* s)
40515 {
40516 	Q_UNUSED(s);
40517 
40518 	return false;
40519 }
40520 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)40521 bool ConstraintStudentsMaxMorningsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
40522 {
40523 	Q_UNUSED(r);
40524 	Q_UNUSED(s);
40525 
40526 	return true;
40527 }
40528 
hasWrongDayOrHour(Rules & r)40529 bool ConstraintStudentsMaxMorningsPerWeek::hasWrongDayOrHour(Rules& r)
40530 {
40531 	if(this->maxMorningsPerWeek>r.nDaysPerWeek/2)
40532 		return true;
40533 
40534 	return false;
40535 }
40536 
canRepairWrongDayOrHour(Rules & r)40537 bool ConstraintStudentsMaxMorningsPerWeek::canRepairWrongDayOrHour(Rules& r)
40538 {
40539 	assert(hasWrongDayOrHour(r));
40540 
40541 	return true;
40542 }
40543 
repairWrongDayOrHour(Rules & r)40544 bool ConstraintStudentsMaxMorningsPerWeek::repairWrongDayOrHour(Rules& r)
40545 {
40546 	assert(hasWrongDayOrHour(r));
40547 
40548 	if(this->maxMorningsPerWeek>r.nDaysPerWeek/2)
40549 		this->maxMorningsPerWeek=r.nDaysPerWeek/2;
40550 
40551 	return true;
40552 }
40553 
40554 //2020-06-26
40555 ///////////////////////////////////////////////////////////////////////////////////////////
40556 ///////////////////////////////////////////////////////////////////////////////////////////
40557 
ConstraintStudentsSetMinAfternoonsPerWeek()40558 ConstraintStudentsSetMinAfternoonsPerWeek::ConstraintStudentsSetMinAfternoonsPerWeek()
40559 	: TimeConstraint()
40560 {
40561 	this->type=CONSTRAINT_STUDENTS_SET_MIN_AFTERNOONS_PER_WEEK;
40562 }
40563 
ConstraintStudentsSetMinAfternoonsPerWeek(double wp,int minnd,const QString & sn)40564 ConstraintStudentsSetMinAfternoonsPerWeek::ConstraintStudentsSetMinAfternoonsPerWeek(double wp, int minnd, const QString& sn)
40565 	 : TimeConstraint(wp)
40566 {
40567 	this->students = sn;
40568 	this->minAfternoonsPerWeek=minnd;
40569 	this->type=CONSTRAINT_STUDENTS_SET_MIN_AFTERNOONS_PER_WEEK;
40570 }
40571 
computeInternalStructure(QWidget * parent,Rules & r)40572 bool ConstraintStudentsSetMinAfternoonsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
40573 {
40574 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
40575 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
40576 
40577 	if(ss==nullptr){
40578 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
40579 		 tr("Constraint students set min afternoons per week is wrong because it refers to inexistent students set."
40580 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
40581 
40582 		return false;
40583 	}
40584 
40585 	assert(ss!=nullptr);
40586 
40587 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
40588 	/*this->iSubgroupsList.clear();
40589 	if(ss->type==STUDENTS_SUBGROUP){
40590 		int tmp;
40591 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
40592 		assert(tmp>=0);
40593 		assert(tmp<r.nInternalSubgroups);
40594 		if(!this->iSubgroupsList.contains(tmp))
40595 			this->iSubgroupsList.append(tmp);
40596 	}
40597 	else if(ss->type==STUDENTS_GROUP){
40598 		StudentsGroup* stg=(StudentsGroup*)ss;
40599 		for(int i=0; i<stg->subgroupsList.size(); i++){
40600 			StudentsSubgroup* sts=stg->subgroupsList[i];
40601 			int tmp;
40602 			tmp=sts->indexInInternalSubgroupsList;
40603 			assert(tmp>=0);
40604 			assert(tmp<r.nInternalSubgroups);
40605 			if(!this->iSubgroupsList.contains(tmp))
40606 				this->iSubgroupsList.append(tmp);
40607 		}
40608 	}
40609 	else if(ss->type==STUDENTS_YEAR){
40610 		StudentsYear* sty=(StudentsYear*)ss;
40611 		for(int i=0; i<sty->groupsList.size(); i++){
40612 			StudentsGroup* stg=sty->groupsList[i];
40613 			for(int j=0; j<stg->subgroupsList.size(); j++){
40614 				StudentsSubgroup* sts=stg->subgroupsList[j];
40615 				int tmp;
40616 				tmp=sts->indexInInternalSubgroupsList;
40617 				assert(tmp>=0);
40618 				assert(tmp<r.nInternalSubgroups);
40619 				if(!this->iSubgroupsList.contains(tmp))
40620 					this->iSubgroupsList.append(tmp);
40621 			}
40622 		}
40623 	}
40624 	else
40625 		assert(0);*/
40626 
40627 	return true;
40628 }
40629 
hasInactiveActivities(Rules & r)40630 bool ConstraintStudentsSetMinAfternoonsPerWeek::hasInactiveActivities(Rules& r)
40631 {
40632 	Q_UNUSED(r);
40633 	return false;
40634 }
40635 
getXmlDescription(Rules & r)40636 QString ConstraintStudentsSetMinAfternoonsPerWeek::getXmlDescription(Rules& r)
40637 {
40638 	Q_UNUSED(r);
40639 
40640 	QString s="<ConstraintStudentsSetMinAfternoonsPerWeek>\n";
40641 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
40642 	s+="	<Students>"+protect(this->students)+"</Students>\n";
40643 	s+="	<Min_Afternoons_Per_Week>"+CustomFETString::number(this->minAfternoonsPerWeek)+"</Min_Afternoons_Per_Week>\n";
40644 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
40645 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
40646 	s+="</ConstraintStudentsSetMinAfternoonsPerWeek>\n";
40647 	return s;
40648 }
40649 
getDescription(Rules & r)40650 QString ConstraintStudentsSetMinAfternoonsPerWeek::getDescription(Rules& r){
40651 	Q_UNUSED(r);
40652 
40653 	QString begin=QString("");
40654 	if(!active)
40655 		begin="X - ";
40656 
40657 	QString end=QString("");
40658 	if(!comments.isEmpty())
40659 		end=", "+tr("C: %1", "Comments").arg(comments);
40660 
40661 	QString s=tr("Students set min afternoons per week");s+=", ";
40662 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
40663 	s+=tr("St:%1", "Abbreviation for students (sets)").arg(this->students);s+=", ";
40664 	s+=tr("mA:%1", "Abbreviation for min afternoons").arg(this->minAfternoonsPerWeek);
40665 
40666 	return begin+s+end;
40667 }
40668 
getDetailedDescription(Rules & r)40669 QString ConstraintStudentsSetMinAfternoonsPerWeek::getDetailedDescription(Rules& r){
40670 	Q_UNUSED(r);
40671 
40672 	QString s=tr("Time constraint");s+="\n";
40673 	s+=tr("A students set must respect the minimum number of afternoons per week");s+="\n";
40674 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
40675 	s+=tr("Students set=%1").arg(this->students);s+="\n";
40676 
40677 	s+=tr("Minimum afternoons per week=%1").arg(this->minAfternoonsPerWeek);s+="\n";
40678 
40679 	if(!active){
40680 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
40681 		s+="\n";
40682 	}
40683 	if(!comments.isEmpty()){
40684 		s+=tr("Comments=%1").arg(comments);
40685 		s+="\n";
40686 	}
40687 
40688 	return s;
40689 }
40690 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)40691 double ConstraintStudentsSetMinAfternoonsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
40692 {
40693 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
40694 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
40695 		c.teachersMatrixReady=true;
40696 		c.subgroupsMatrixReady=true;
40697 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
40698 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
40699 
40700 		c.changedForMatrixCalculation=false;
40701 	}
40702 
40703 	int nbroken;
40704 
40705 	nbroken=0;
40706 
40707 	for(int sbg : qAsConst(this->iSubgroupsList)){
40708 		/*bool ocDay[MAX_DAYS_PER_WEEK];
40709 		for(int d=0; d<r.nDaysPerWeek; d++){
40710 			ocDay[d]=false;
40711 			for(int h=0; h<r.nHoursPerDay; h++){
40712 				if(subgroupsMatrix[sbg][d][h]>0){
40713 					ocDay[d]=true;
40714 				}
40715 			}
40716 		}*/
40717 		int nOcDays=0;
40718 		/*for(int d=0; d<r.nDaysPerWeek; d++)
40719 			if(ocDay[d])
40720 				nOcDays++;*/
40721 		for(int d=0; d<r.nDaysPerWeek/2; d++){
40722 			int nh=0;
40723 			/*for(int h=0; h<r.nHoursPerDay; h++)
40724 				nh += subgroupsMatrix[sbg][2*d][h]>=1 ? 1 : 0;*/
40725 			for(int h=0; h<r.nHoursPerDay; h++)
40726 				nh += subgroupsMatrix[sbg][2*d+1][h]>=1 ? 1 : 0;
40727 			if(nh>0)
40728 				nOcDays++;
40729 		}
40730 		if(nOcDays < this->minAfternoonsPerWeek){
40731 			nbroken+=-nOcDays+this->minAfternoonsPerWeek;
40732 
40733 			if((-nOcDays+this->minAfternoonsPerWeek)>0){
40734 				QString s= tr("Time constraint students set min %1 afternoons per week broken for subgroup: %2.")
40735 				 .arg(this->minAfternoonsPerWeek)
40736 				 .arg(r.internalSubgroupsList[sbg]->name);
40737 				s+=" ";
40738 				s += tr("This increases the conflicts total by %1")
40739 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((-nOcDays+this->minAfternoonsPerWeek)*weightPercentage/100));
40740 
40741 				dl.append(s);
40742 				cl.append((-nOcDays+this->minAfternoonsPerWeek)*weightPercentage/100);
40743 
40744 				*conflictsString += s+"\n";
40745 			}
40746 		}
40747 	}
40748 
40749 	if(c.nPlacedActivities==r.nInternalActivities)
40750 		if(weightPercentage==100)
40751 			assert(nbroken==0);
40752 	return weightPercentage/100 * nbroken;
40753 }
40754 
isRelatedToActivity(Rules & r,Activity * a)40755 bool ConstraintStudentsSetMinAfternoonsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
40756 {
40757 	Q_UNUSED(r);
40758 	Q_UNUSED(a);
40759 
40760 	return false;
40761 }
40762 
isRelatedToTeacher(Teacher * t)40763 bool ConstraintStudentsSetMinAfternoonsPerWeek::isRelatedToTeacher(Teacher* t)
40764 {
40765 	Q_UNUSED(t);
40766 	return false;
40767 }
40768 
isRelatedToSubject(Subject * s)40769 bool ConstraintStudentsSetMinAfternoonsPerWeek::isRelatedToSubject(Subject* s)
40770 {
40771 	Q_UNUSED(s);
40772 
40773 	return false;
40774 }
40775 
isRelatedToActivityTag(ActivityTag * s)40776 bool ConstraintStudentsSetMinAfternoonsPerWeek::isRelatedToActivityTag(ActivityTag* s)
40777 {
40778 	Q_UNUSED(s);
40779 
40780 	return false;
40781 }
40782 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)40783 bool ConstraintStudentsSetMinAfternoonsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
40784 {
40785 	return r.setsShareStudents(this->students, s->name);
40786 }
40787 
hasWrongDayOrHour(Rules & r)40788 bool ConstraintStudentsSetMinAfternoonsPerWeek::hasWrongDayOrHour(Rules& r)
40789 {
40790 	if(this->minAfternoonsPerWeek>r.nDaysPerWeek/2)
40791 		return true;
40792 
40793 	return false;
40794 }
40795 
canRepairWrongDayOrHour(Rules & r)40796 bool ConstraintStudentsSetMinAfternoonsPerWeek::canRepairWrongDayOrHour(Rules& r)
40797 {
40798 	assert(hasWrongDayOrHour(r));
40799 
40800 	return true;
40801 }
40802 
repairWrongDayOrHour(Rules & r)40803 bool ConstraintStudentsSetMinAfternoonsPerWeek::repairWrongDayOrHour(Rules& r)
40804 {
40805 	assert(hasWrongDayOrHour(r));
40806 
40807 	if(this->minAfternoonsPerWeek>r.nDaysPerWeek/2)
40808 		this->minAfternoonsPerWeek=r.nDaysPerWeek/2;
40809 
40810 	return true;
40811 }
40812 
40813 ///////////////////////////////////////////////////////////////////////////////////////////
40814 ///////////////////////////////////////////////////////////////////////////////////////////
40815 
ConstraintStudentsMinAfternoonsPerWeek()40816 ConstraintStudentsMinAfternoonsPerWeek::ConstraintStudentsMinAfternoonsPerWeek()
40817 	: TimeConstraint()
40818 {
40819 	this->type=CONSTRAINT_STUDENTS_MIN_AFTERNOONS_PER_WEEK;
40820 }
40821 
ConstraintStudentsMinAfternoonsPerWeek(double wp,int minnd)40822 ConstraintStudentsMinAfternoonsPerWeek::ConstraintStudentsMinAfternoonsPerWeek(double wp, int minnd)
40823 	 : TimeConstraint(wp)
40824 {
40825 	this->minAfternoonsPerWeek=minnd;
40826 	this->type=CONSTRAINT_STUDENTS_MIN_AFTERNOONS_PER_WEEK;
40827 }
40828 
computeInternalStructure(QWidget * parent,Rules & r)40829 bool ConstraintStudentsMinAfternoonsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
40830 {
40831 	Q_UNUSED(parent);
40832 	Q_UNUSED(r);
40833 
40834 	return true;
40835 }
40836 
hasInactiveActivities(Rules & r)40837 bool ConstraintStudentsMinAfternoonsPerWeek::hasInactiveActivities(Rules& r)
40838 {
40839 	Q_UNUSED(r);
40840 	return false;
40841 }
40842 
getXmlDescription(Rules & r)40843 QString ConstraintStudentsMinAfternoonsPerWeek::getXmlDescription(Rules& r)
40844 {
40845 	Q_UNUSED(r);
40846 
40847 	QString s="<ConstraintStudentsMinAfternoonsPerWeek>\n";
40848 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
40849 	s+="	<Min_Afternoons_Per_Week>"+CustomFETString::number(this->minAfternoonsPerWeek)+"</Min_Afternoons_Per_Week>\n";
40850 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
40851 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
40852 	s+="</ConstraintStudentsMinAfternoonsPerWeek>\n";
40853 	return s;
40854 }
40855 
getDescription(Rules & r)40856 QString ConstraintStudentsMinAfternoonsPerWeek::getDescription(Rules& r){
40857 	Q_UNUSED(r);
40858 
40859 	QString begin=QString("");
40860 	if(!active)
40861 		begin="X - ";
40862 
40863 	QString end=QString("");
40864 	if(!comments.isEmpty())
40865 		end=", "+tr("C: %1", "Comments").arg(comments);
40866 
40867 	QString s=tr("Students min afternoons per week");s+=", ";
40868 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
40869 	s+=tr("mA:%1", "Abbreviation for min afternoons").arg(this->minAfternoonsPerWeek);
40870 
40871 	return begin+s+end;
40872 }
40873 
getDetailedDescription(Rules & r)40874 QString ConstraintStudentsMinAfternoonsPerWeek::getDetailedDescription(Rules& r){
40875 	Q_UNUSED(r);
40876 
40877 	QString s=tr("Time constraint");s+="\n";
40878 	s+=tr("All students must respect the minimum number of afternoons per week");s+="\n";
40879 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
40880 
40881 	s+=tr("Minimum afternoons per week=%1").arg(this->minAfternoonsPerWeek);s+="\n";
40882 
40883 	if(!active){
40884 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
40885 		s+="\n";
40886 	}
40887 	if(!comments.isEmpty()){
40888 		s+=tr("Comments=%1").arg(comments);
40889 		s+="\n";
40890 	}
40891 
40892 	return s;
40893 }
40894 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)40895 double ConstraintStudentsMinAfternoonsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
40896 {
40897 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
40898 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
40899 		c.teachersMatrixReady=true;
40900 		c.subgroupsMatrixReady=true;
40901 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
40902 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
40903 
40904 		c.changedForMatrixCalculation=false;
40905 	}
40906 
40907 	int nbroken;
40908 
40909 	nbroken=0;
40910 
40911 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
40912 		/*bool ocDay[MAX_DAYS_PER_WEEK];
40913 		for(int d=0; d<r.nDaysPerWeek; d++){
40914 			ocDay[d]=false;
40915 			for(int h=0; h<r.nHoursPerDay; h++){
40916 				if(subgroupsMatrix[sbg][d][h]>0){
40917 					ocDay[d]=true;
40918 				}
40919 			}
40920 		}*/
40921 		int nOcDays=0;
40922 		/*for(int d=0; d<r.nDaysPerWeek; d++)
40923 			if(ocDay[d])
40924 				nOcDays++;*/
40925 		for(int d=0; d<r.nDaysPerWeek/2; d++){
40926 			int nh=0;
40927 			/*for(int h=0; h<r.nHoursPerDay; h++)
40928 				nh += subgroupsMatrix[sbg][2*d][h]>=1 ? 1 : 0;*/
40929 			for(int h=0; h<r.nHoursPerDay; h++)
40930 				nh += subgroupsMatrix[sbg][2*d+1][h]>=1 ? 1 : 0;
40931 			if(nh>0)
40932 				nOcDays++;
40933 		}
40934 		if(nOcDays < this->minAfternoonsPerWeek){
40935 			nbroken+=-nOcDays+this->minAfternoonsPerWeek;
40936 
40937 			if((-nOcDays+this->minAfternoonsPerWeek)>0){
40938 				QString s= tr("Time constraint students min %1 afternoons per week broken for subgroup: %2.")
40939 				 .arg(this->minAfternoonsPerWeek)
40940 				 .arg(r.internalSubgroupsList[sbg]->name);
40941 				s+=" ";
40942 				s += tr("This increases the conflicts total by %1")
40943 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((-nOcDays+this->minAfternoonsPerWeek)*weightPercentage/100));
40944 
40945 				dl.append(s);
40946 				cl.append((-nOcDays+this->minAfternoonsPerWeek)*weightPercentage/100);
40947 
40948 				*conflictsString += s+"\n";
40949 			}
40950 		}
40951 	}
40952 
40953 	if(c.nPlacedActivities==r.nInternalActivities)
40954 		if(weightPercentage==100)
40955 			assert(nbroken==0);
40956 	return weightPercentage/100 * nbroken;
40957 }
40958 
isRelatedToActivity(Rules & r,Activity * a)40959 bool ConstraintStudentsMinAfternoonsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
40960 {
40961 	Q_UNUSED(r);
40962 	Q_UNUSED(a);
40963 
40964 	return false;
40965 }
40966 
isRelatedToTeacher(Teacher * t)40967 bool ConstraintStudentsMinAfternoonsPerWeek::isRelatedToTeacher(Teacher* t)
40968 {
40969 	Q_UNUSED(t);
40970 	return false;
40971 }
40972 
isRelatedToSubject(Subject * s)40973 bool ConstraintStudentsMinAfternoonsPerWeek::isRelatedToSubject(Subject* s)
40974 {
40975 	Q_UNUSED(s);
40976 
40977 	return false;
40978 }
40979 
isRelatedToActivityTag(ActivityTag * s)40980 bool ConstraintStudentsMinAfternoonsPerWeek::isRelatedToActivityTag(ActivityTag* s)
40981 {
40982 	Q_UNUSED(s);
40983 
40984 	return false;
40985 }
40986 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)40987 bool ConstraintStudentsMinAfternoonsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
40988 {
40989 	Q_UNUSED(r);
40990 	Q_UNUSED(s);
40991 
40992 	return true;
40993 }
40994 
hasWrongDayOrHour(Rules & r)40995 bool ConstraintStudentsMinAfternoonsPerWeek::hasWrongDayOrHour(Rules& r)
40996 {
40997 	if(this->minAfternoonsPerWeek>r.nDaysPerWeek/2)
40998 		return true;
40999 
41000 	return false;
41001 }
41002 
canRepairWrongDayOrHour(Rules & r)41003 bool ConstraintStudentsMinAfternoonsPerWeek::canRepairWrongDayOrHour(Rules& r)
41004 {
41005 	assert(hasWrongDayOrHour(r));
41006 
41007 	return true;
41008 }
41009 
repairWrongDayOrHour(Rules & r)41010 bool ConstraintStudentsMinAfternoonsPerWeek::repairWrongDayOrHour(Rules& r)
41011 {
41012 	assert(hasWrongDayOrHour(r));
41013 
41014 	if(this->minAfternoonsPerWeek>r.nDaysPerWeek/2)
41015 		this->minAfternoonsPerWeek=r.nDaysPerWeek/2;
41016 
41017 	return true;
41018 }
41019 
41020 ///////////////////////////////////////////////////////////////////////////////////////////
41021 ///////////////////////////////////////////////////////////////////////////////////////////
41022 
ConstraintStudentsSetMinMorningsPerWeek()41023 ConstraintStudentsSetMinMorningsPerWeek::ConstraintStudentsSetMinMorningsPerWeek()
41024 	: TimeConstraint()
41025 {
41026 	this->type=CONSTRAINT_STUDENTS_SET_MIN_MORNINGS_PER_WEEK;
41027 }
41028 
ConstraintStudentsSetMinMorningsPerWeek(double wp,int minnd,const QString & sn)41029 ConstraintStudentsSetMinMorningsPerWeek::ConstraintStudentsSetMinMorningsPerWeek(double wp, int minnd, const QString& sn)
41030 	 : TimeConstraint(wp)
41031 {
41032 	this->students = sn;
41033 	this->minMorningsPerWeek=minnd;
41034 	this->type=CONSTRAINT_STUDENTS_SET_MIN_MORNINGS_PER_WEEK;
41035 }
41036 
computeInternalStructure(QWidget * parent,Rules & r)41037 bool ConstraintStudentsSetMinMorningsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
41038 {
41039 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
41040 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
41041 
41042 	if(ss==nullptr){
41043 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
41044 		 tr("Constraint students set min mornings per week is wrong because it refers to inexistent students set."
41045 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
41046 
41047 		return false;
41048 	}
41049 
41050 	assert(ss!=nullptr);
41051 
41052 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
41053 	/*this->iSubgroupsList.clear();
41054 	if(ss->type==STUDENTS_SUBGROUP){
41055 		int tmp;
41056 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
41057 		assert(tmp>=0);
41058 		assert(tmp<r.nInternalSubgroups);
41059 		if(!this->iSubgroupsList.contains(tmp))
41060 			this->iSubgroupsList.append(tmp);
41061 	}
41062 	else if(ss->type==STUDENTS_GROUP){
41063 		StudentsGroup* stg=(StudentsGroup*)ss;
41064 		for(int i=0; i<stg->subgroupsList.size(); i++){
41065 			StudentsSubgroup* sts=stg->subgroupsList[i];
41066 			int tmp;
41067 			tmp=sts->indexInInternalSubgroupsList;
41068 			assert(tmp>=0);
41069 			assert(tmp<r.nInternalSubgroups);
41070 			if(!this->iSubgroupsList.contains(tmp))
41071 				this->iSubgroupsList.append(tmp);
41072 		}
41073 	}
41074 	else if(ss->type==STUDENTS_YEAR){
41075 		StudentsYear* sty=(StudentsYear*)ss;
41076 		for(int i=0; i<sty->groupsList.size(); i++){
41077 			StudentsGroup* stg=sty->groupsList[i];
41078 			for(int j=0; j<stg->subgroupsList.size(); j++){
41079 				StudentsSubgroup* sts=stg->subgroupsList[j];
41080 				int tmp;
41081 				tmp=sts->indexInInternalSubgroupsList;
41082 				assert(tmp>=0);
41083 				assert(tmp<r.nInternalSubgroups);
41084 				if(!this->iSubgroupsList.contains(tmp))
41085 					this->iSubgroupsList.append(tmp);
41086 			}
41087 		}
41088 	}
41089 	else
41090 		assert(0);*/
41091 
41092 	return true;
41093 }
41094 
hasInactiveActivities(Rules & r)41095 bool ConstraintStudentsSetMinMorningsPerWeek::hasInactiveActivities(Rules& r)
41096 {
41097 	Q_UNUSED(r);
41098 	return false;
41099 }
41100 
getXmlDescription(Rules & r)41101 QString ConstraintStudentsSetMinMorningsPerWeek::getXmlDescription(Rules& r)
41102 {
41103 	Q_UNUSED(r);
41104 
41105 	QString s="<ConstraintStudentsSetMinMorningsPerWeek>\n";
41106 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
41107 	s+="	<Students>"+protect(this->students)+"</Students>\n";
41108 	s+="	<Min_Mornings_Per_Week>"+CustomFETString::number(this->minMorningsPerWeek)+"</Min_Mornings_Per_Week>\n";
41109 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
41110 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
41111 	s+="</ConstraintStudentsSetMinMorningsPerWeek>\n";
41112 	return s;
41113 }
41114 
getDescription(Rules & r)41115 QString ConstraintStudentsSetMinMorningsPerWeek::getDescription(Rules& r){
41116 	Q_UNUSED(r);
41117 
41118 	QString begin=QString("");
41119 	if(!active)
41120 		begin="X - ";
41121 
41122 	QString end=QString("");
41123 	if(!comments.isEmpty())
41124 		end=", "+tr("C: %1", "Comments").arg(comments);
41125 
41126 	QString s=tr("Students set min mornings per week");s+=", ";
41127 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
41128 	s+=tr("St:%1", "Abbreviation for students (sets)").arg(this->students);s+=", ";
41129 	s+=tr("mM:%1", "Abbreviation for min mornings").arg(this->minMorningsPerWeek);
41130 
41131 	return begin+s+end;
41132 }
41133 
getDetailedDescription(Rules & r)41134 QString ConstraintStudentsSetMinMorningsPerWeek::getDetailedDescription(Rules& r){
41135 	Q_UNUSED(r);
41136 
41137 	QString s=tr("Time constraint");s+="\n";
41138 	s+=tr("A students set must respect the minimum number of mornings per week");s+="\n";
41139 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
41140 	s+=tr("Students set=%1").arg(this->students);s+="\n";
41141 
41142 	s+=tr("Minimum mornings per week=%1").arg(this->minMorningsPerWeek);s+="\n";
41143 
41144 	if(!active){
41145 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
41146 		s+="\n";
41147 	}
41148 	if(!comments.isEmpty()){
41149 		s+=tr("Comments=%1").arg(comments);
41150 		s+="\n";
41151 	}
41152 
41153 	return s;
41154 }
41155 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)41156 double ConstraintStudentsSetMinMorningsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
41157 {
41158 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
41159 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
41160 		c.teachersMatrixReady=true;
41161 		c.subgroupsMatrixReady=true;
41162 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
41163 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
41164 
41165 		c.changedForMatrixCalculation=false;
41166 	}
41167 
41168 	int nbroken;
41169 
41170 	nbroken=0;
41171 
41172 	for(int sbg : qAsConst(this->iSubgroupsList)){
41173 		/*bool ocDay[MAX_DAYS_PER_WEEK];
41174 		for(int d=0; d<r.nDaysPerWeek; d++){
41175 			ocDay[d]=false;
41176 			for(int h=0; h<r.nHoursPerDay; h++){
41177 				if(subgroupsMatrix[sbg][d][h]>0){
41178 					ocDay[d]=true;
41179 				}
41180 			}
41181 		}*/
41182 		int nOcDays=0;
41183 		/*for(int d=0; d<r.nDaysPerWeek; d++)
41184 			if(ocDay[d])
41185 				nOcDays++;*/
41186 		for(int d=0; d<r.nDaysPerWeek/2; d++){
41187 			int nh=0;
41188 			for(int h=0; h<r.nHoursPerDay; h++)
41189 				nh += subgroupsMatrix[sbg][2*d][h]>=1 ? 1 : 0;
41190 			/*for(int h=0; h<r.nHoursPerDay; h++)
41191 				nh += subgroupsMatrix[sbg][2*d+1][h]>=1 ? 1 : 0;*/
41192 			if(nh>0)
41193 				nOcDays++;
41194 		}
41195 		if(nOcDays < this->minMorningsPerWeek){
41196 			nbroken+=-nOcDays+this->minMorningsPerWeek;
41197 
41198 			if((-nOcDays+this->minMorningsPerWeek)>0){
41199 				QString s= tr("Time constraint students set min %1 mornings per week broken for subgroup: %2.")
41200 				 .arg(this->minMorningsPerWeek)
41201 				 .arg(r.internalSubgroupsList[sbg]->name);
41202 				s+=" ";
41203 				s += tr("This increases the conflicts total by %1")
41204 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((-nOcDays+this->minMorningsPerWeek)*weightPercentage/100));
41205 
41206 				dl.append(s);
41207 				cl.append((-nOcDays+this->minMorningsPerWeek)*weightPercentage/100);
41208 
41209 				*conflictsString += s+"\n";
41210 			}
41211 		}
41212 	}
41213 
41214 	if(c.nPlacedActivities==r.nInternalActivities)
41215 		if(weightPercentage==100)
41216 			assert(nbroken==0);
41217 	return weightPercentage/100 * nbroken;
41218 }
41219 
isRelatedToActivity(Rules & r,Activity * a)41220 bool ConstraintStudentsSetMinMorningsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
41221 {
41222 	Q_UNUSED(r);
41223 	Q_UNUSED(a);
41224 
41225 	return false;
41226 }
41227 
isRelatedToTeacher(Teacher * t)41228 bool ConstraintStudentsSetMinMorningsPerWeek::isRelatedToTeacher(Teacher* t)
41229 {
41230 	Q_UNUSED(t);
41231 	return false;
41232 }
41233 
isRelatedToSubject(Subject * s)41234 bool ConstraintStudentsSetMinMorningsPerWeek::isRelatedToSubject(Subject* s)
41235 {
41236 	Q_UNUSED(s);
41237 
41238 	return false;
41239 }
41240 
isRelatedToActivityTag(ActivityTag * s)41241 bool ConstraintStudentsSetMinMorningsPerWeek::isRelatedToActivityTag(ActivityTag* s)
41242 {
41243 	Q_UNUSED(s);
41244 
41245 	return false;
41246 }
41247 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)41248 bool ConstraintStudentsSetMinMorningsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
41249 {
41250 	return r.setsShareStudents(this->students, s->name);
41251 }
41252 
hasWrongDayOrHour(Rules & r)41253 bool ConstraintStudentsSetMinMorningsPerWeek::hasWrongDayOrHour(Rules& r)
41254 {
41255 	if(this->minMorningsPerWeek>r.nDaysPerWeek/2)
41256 		return true;
41257 
41258 	return false;
41259 }
41260 
canRepairWrongDayOrHour(Rules & r)41261 bool ConstraintStudentsSetMinMorningsPerWeek::canRepairWrongDayOrHour(Rules& r)
41262 {
41263 	assert(hasWrongDayOrHour(r));
41264 
41265 	return true;
41266 }
41267 
repairWrongDayOrHour(Rules & r)41268 bool ConstraintStudentsSetMinMorningsPerWeek::repairWrongDayOrHour(Rules& r)
41269 {
41270 	assert(hasWrongDayOrHour(r));
41271 
41272 	if(this->minMorningsPerWeek>r.nDaysPerWeek/2)
41273 		this->minMorningsPerWeek=r.nDaysPerWeek/2;
41274 
41275 	return true;
41276 }
41277 
41278 ///////////////////////////////////////////////////////////////////////////////////////////
41279 ///////////////////////////////////////////////////////////////////////////////////////////
41280 
ConstraintStudentsMinMorningsPerWeek()41281 ConstraintStudentsMinMorningsPerWeek::ConstraintStudentsMinMorningsPerWeek()
41282 	: TimeConstraint()
41283 {
41284 	this->type=CONSTRAINT_STUDENTS_MIN_MORNINGS_PER_WEEK;
41285 }
41286 
ConstraintStudentsMinMorningsPerWeek(double wp,int minnd)41287 ConstraintStudentsMinMorningsPerWeek::ConstraintStudentsMinMorningsPerWeek(double wp, int minnd)
41288 	 : TimeConstraint(wp)
41289 {
41290 	this->minMorningsPerWeek=minnd;
41291 	this->type=CONSTRAINT_STUDENTS_MIN_MORNINGS_PER_WEEK;
41292 }
41293 
computeInternalStructure(QWidget * parent,Rules & r)41294 bool ConstraintStudentsMinMorningsPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
41295 {
41296 	Q_UNUSED(parent);
41297 	Q_UNUSED(r);
41298 
41299 	return true;
41300 }
41301 
hasInactiveActivities(Rules & r)41302 bool ConstraintStudentsMinMorningsPerWeek::hasInactiveActivities(Rules& r)
41303 {
41304 	Q_UNUSED(r);
41305 	return false;
41306 }
41307 
getXmlDescription(Rules & r)41308 QString ConstraintStudentsMinMorningsPerWeek::getXmlDescription(Rules& r)
41309 {
41310 	Q_UNUSED(r);
41311 
41312 	QString s="<ConstraintStudentsMinMorningsPerWeek>\n";
41313 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
41314 	s+="	<Min_Mornings_Per_Week>"+CustomFETString::number(this->minMorningsPerWeek)+"</Min_Mornings_Per_Week>\n";
41315 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
41316 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
41317 	s+="</ConstraintStudentsMinMorningsPerWeek>\n";
41318 	return s;
41319 }
41320 
getDescription(Rules & r)41321 QString ConstraintStudentsMinMorningsPerWeek::getDescription(Rules& r){
41322 	Q_UNUSED(r);
41323 
41324 	QString begin=QString("");
41325 	if(!active)
41326 		begin="X - ";
41327 
41328 	QString end=QString("");
41329 	if(!comments.isEmpty())
41330 		end=", "+tr("C: %1", "Comments").arg(comments);
41331 
41332 	QString s=tr("Students min mornings per week");s+=", ";
41333 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
41334 	s+=tr("mM:%1", "Abbreviation for min mornings").arg(this->minMorningsPerWeek);
41335 
41336 	return begin+s+end;
41337 }
41338 
getDetailedDescription(Rules & r)41339 QString ConstraintStudentsMinMorningsPerWeek::getDetailedDescription(Rules& r){
41340 	Q_UNUSED(r);
41341 
41342 	QString s=tr("Time constraint");s+="\n";
41343 	s+=tr("All students must respect the minimum number of mornings per week");s+="\n";
41344 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
41345 
41346 	s+=tr("Minimum mornings per week=%1").arg(this->minMorningsPerWeek);s+="\n";
41347 
41348 	if(!active){
41349 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
41350 		s+="\n";
41351 	}
41352 	if(!comments.isEmpty()){
41353 		s+=tr("Comments=%1").arg(comments);
41354 		s+="\n";
41355 	}
41356 
41357 	return s;
41358 }
41359 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)41360 double ConstraintStudentsMinMorningsPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
41361 {
41362 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
41363 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
41364 		c.teachersMatrixReady=true;
41365 		c.subgroupsMatrixReady=true;
41366 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
41367 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
41368 
41369 		c.changedForMatrixCalculation=false;
41370 	}
41371 
41372 	int nbroken;
41373 
41374 	nbroken=0;
41375 
41376 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
41377 		/*bool ocDay[MAX_DAYS_PER_WEEK];
41378 		for(int d=0; d<r.nDaysPerWeek; d++){
41379 			ocDay[d]=false;
41380 			for(int h=0; h<r.nHoursPerDay; h++){
41381 				if(subgroupsMatrix[sbg][d][h]>0){
41382 					ocDay[d]=true;
41383 				}
41384 			}
41385 		}*/
41386 		int nOcDays=0;
41387 		/*for(int d=0; d<r.nDaysPerWeek; d++)
41388 			if(ocDay[d])
41389 				nOcDays++;*/
41390 		for(int d=0; d<r.nDaysPerWeek/2; d++){
41391 			int nh=0;
41392 			for(int h=0; h<r.nHoursPerDay; h++)
41393 				nh += subgroupsMatrix[sbg][2*d][h]>=1 ? 1 : 0;
41394 			/*for(int h=0; h<r.nHoursPerDay; h++)
41395 				nh += subgroupsMatrix[sbg][2*d+1][h]>=1 ? 1 : 0;*/
41396 			if(nh>0)
41397 				nOcDays++;
41398 		}
41399 		if(nOcDays < this->minMorningsPerWeek){
41400 			nbroken+=-nOcDays+this->minMorningsPerWeek;
41401 
41402 			if((-nOcDays+this->minMorningsPerWeek)>0){
41403 				QString s= tr("Time constraint students min %1 mornings per week broken for subgroup: %2.")
41404 				 .arg(this->minMorningsPerWeek)
41405 				 .arg(r.internalSubgroupsList[sbg]->name);
41406 				s+=" ";
41407 				s += tr("This increases the conflicts total by %1")
41408 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((-nOcDays+this->minMorningsPerWeek)*weightPercentage/100));
41409 
41410 				dl.append(s);
41411 				cl.append((-nOcDays+this->minMorningsPerWeek)*weightPercentage/100);
41412 
41413 				*conflictsString += s+"\n";
41414 			}
41415 		}
41416 	}
41417 
41418 	if(c.nPlacedActivities==r.nInternalActivities)
41419 		if(weightPercentage==100)
41420 			assert(nbroken==0);
41421 	return weightPercentage/100 * nbroken;
41422 }
41423 
isRelatedToActivity(Rules & r,Activity * a)41424 bool ConstraintStudentsMinMorningsPerWeek::isRelatedToActivity(Rules& r, Activity* a)
41425 {
41426 	Q_UNUSED(r);
41427 	Q_UNUSED(a);
41428 
41429 	return false;
41430 }
41431 
isRelatedToTeacher(Teacher * t)41432 bool ConstraintStudentsMinMorningsPerWeek::isRelatedToTeacher(Teacher* t)
41433 {
41434 	Q_UNUSED(t);
41435 	return false;
41436 }
41437 
isRelatedToSubject(Subject * s)41438 bool ConstraintStudentsMinMorningsPerWeek::isRelatedToSubject(Subject* s)
41439 {
41440 	Q_UNUSED(s);
41441 
41442 	return false;
41443 }
41444 
isRelatedToActivityTag(ActivityTag * s)41445 bool ConstraintStudentsMinMorningsPerWeek::isRelatedToActivityTag(ActivityTag* s)
41446 {
41447 	Q_UNUSED(s);
41448 
41449 	return false;
41450 }
41451 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)41452 bool ConstraintStudentsMinMorningsPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
41453 {
41454 	Q_UNUSED(r);
41455 	Q_UNUSED(s);
41456 
41457 	return true;
41458 }
41459 
hasWrongDayOrHour(Rules & r)41460 bool ConstraintStudentsMinMorningsPerWeek::hasWrongDayOrHour(Rules& r)
41461 {
41462 	if(this->minMorningsPerWeek>r.nDaysPerWeek/2)
41463 		return true;
41464 
41465 	return false;
41466 }
41467 
canRepairWrongDayOrHour(Rules & r)41468 bool ConstraintStudentsMinMorningsPerWeek::canRepairWrongDayOrHour(Rules& r)
41469 {
41470 	assert(hasWrongDayOrHour(r));
41471 
41472 	return true;
41473 }
41474 
repairWrongDayOrHour(Rules & r)41475 bool ConstraintStudentsMinMorningsPerWeek::repairWrongDayOrHour(Rules& r)
41476 {
41477 	assert(hasWrongDayOrHour(r));
41478 
41479 	if(this->minMorningsPerWeek>r.nDaysPerWeek/2)
41480 		this->minMorningsPerWeek=r.nDaysPerWeek/2;
41481 
41482 	return true;
41483 }
41484 
41485 //2020-06-26
41486 ///////////////////////////////////////////////////////////////////////////////////////////
41487 ///////////////////////////////////////////////////////////////////////////////////////////
41488 
ConstraintStudentsSetMorningIntervalMaxDaysPerWeek()41489 ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::ConstraintStudentsSetMorningIntervalMaxDaysPerWeek()
41490 	: TimeConstraint()
41491 {
41492 	this->type=CONSTRAINT_STUDENTS_SET_MORNING_INTERVAL_MAX_DAYS_PER_WEEK;
41493 }
41494 
ConstraintStudentsSetMorningIntervalMaxDaysPerWeek(double wp,int maxnd,QString sn,int sh,int eh)41495 ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::ConstraintStudentsSetMorningIntervalMaxDaysPerWeek(double wp, int maxnd, QString sn, int sh, int eh)
41496 	 : TimeConstraint(wp)
41497 {
41498 	this->students = sn;
41499 	this->maxDaysPerWeek=maxnd;
41500 	this->type=CONSTRAINT_STUDENTS_SET_MORNING_INTERVAL_MAX_DAYS_PER_WEEK;
41501 	this->startHour=sh;
41502 	this->endHour=eh;
41503 	assert(sh<eh);
41504 	assert(sh>=0);
41505 }
41506 
computeInternalStructure(QWidget * parent,Rules & r)41507 bool ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
41508 {
41509 	if(this->startHour>=this->endHour){
41510 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
41511 		 tr("Constraint students set morning interval max days per week is wrong because start hour >= end hour."
41512 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
41513 
41514 		return false;
41515 	}
41516 	if(this->startHour<0){
41517 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
41518 		 tr("Constraint students set morning interval max days per week is wrong because start hour < first hour of the day."
41519 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
41520 
41521 		return false;
41522 	}
41523 	if(this->endHour>r.nHoursPerDay){
41524 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
41525 		 tr("Constraint students set morning interval max days per week is wrong because end hour > number of hours per day."
41526 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
41527 
41528 		return false;
41529 	}
41530 
41531 	/////////
41532 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
41533 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
41534 
41535 	if(ss==nullptr){
41536 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
41537 		 tr("Constraint students set morning interval max days per week is wrong because it refers to inexistent students set."
41538 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
41539 
41540 		return false;
41541 	}
41542 
41543 	assert(ss!=nullptr);
41544 
41545 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
41546 	/*this->iSubgroupsList.clear();
41547 	if(ss->type==STUDENTS_SUBGROUP){
41548 		int tmp;
41549 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
41550 		assert(tmp>=0);
41551 		assert(tmp<r.nInternalSubgroups);
41552 		if(!this->iSubgroupsList.contains(tmp))
41553 			this->iSubgroupsList.append(tmp);
41554 	}
41555 	else if(ss->type==STUDENTS_GROUP){
41556 		StudentsGroup* stg=(StudentsGroup*)ss;
41557 		for(int i=0; i<stg->subgroupsList.size(); i++){
41558 			StudentsSubgroup* sts=stg->subgroupsList[i];
41559 			int tmp;
41560 			tmp=sts->indexInInternalSubgroupsList;
41561 			assert(tmp>=0);
41562 			assert(tmp<r.nInternalSubgroups);
41563 			if(!this->iSubgroupsList.contains(tmp))
41564 				this->iSubgroupsList.append(tmp);
41565 		}
41566 	}
41567 	else if(ss->type==STUDENTS_YEAR){
41568 		StudentsYear* sty=(StudentsYear*)ss;
41569 		for(int i=0; i<sty->groupsList.size(); i++){
41570 			StudentsGroup* stg=sty->groupsList[i];
41571 			for(int j=0; j<stg->subgroupsList.size(); j++){
41572 				StudentsSubgroup* sts=stg->subgroupsList[j];
41573 				int tmp;
41574 				tmp=sts->indexInInternalSubgroupsList;
41575 				assert(tmp>=0);
41576 				assert(tmp<r.nInternalSubgroups);
41577 				if(!this->iSubgroupsList.contains(tmp))
41578 					this->iSubgroupsList.append(tmp);
41579 			}
41580 		}
41581 	}
41582 	else
41583 		assert(0);*/
41584 
41585 	return true;
41586 }
41587 
hasInactiveActivities(Rules & r)41588 bool ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
41589 {
41590 	Q_UNUSED(r);
41591 	return false;
41592 }
41593 
getXmlDescription(Rules & r)41594 QString ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
41595 {
41596 	Q_UNUSED(r);
41597 
41598 	QString s="<ConstraintStudentsSetMorningIntervalMaxDaysPerWeek>\n";
41599 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
41600 	s+="	<Students>"+protect(this->students)+"</Students>\n";
41601 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
41602 	if(this->endHour < r.nHoursPerDay){
41603 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
41604 	}
41605 	else{
41606 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
41607 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
41608 	}
41609 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
41610 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
41611 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
41612 	s+="</ConstraintStudentsSetMorningIntervalMaxDaysPerWeek>\n";
41613 	return s;
41614 }
41615 
getDescription(Rules & r)41616 QString ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::getDescription(Rules& r){
41617 	Q_UNUSED(r);
41618 
41619 	QString begin=QString("");
41620 	if(!active)
41621 		begin="X - ";
41622 
41623 	QString end=QString("");
41624 	if(!comments.isEmpty())
41625 		end=", "+tr("C: %1", "Comments").arg(comments);
41626 
41627 	QString s=tr("Students set morning interval max days per week");s+=", ";
41628 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
41629 	s+=tr("St:%1", "Abbreviation for students (sets)").arg(this->students);s+=", ";
41630 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);
41631 	s+=", ";
41632 	if(this->endHour<r.nHoursPerDay)
41633 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
41634 	else
41635 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
41636 	s+=", ";
41637 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
41638 
41639 	return begin+s+end;
41640 }
41641 
getDetailedDescription(Rules & r)41642 QString ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r){
41643 	Q_UNUSED(r);
41644 
41645 	QString s=tr("Time constraint");s+="\n";
41646 	s+=tr("A students set respects working in an hourly morning interval a maximum number of days per week");s+="\n";
41647 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
41648 	s+=tr("Students set=%1").arg(this->students);s+="\n";
41649 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
41650 
41651 	if(this->endHour<r.nHoursPerDay)
41652 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
41653 	else
41654 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
41655 	s+="\n";
41656 
41657 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
41658 
41659 	if(!active){
41660 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
41661 		s+="\n";
41662 	}
41663 	if(!comments.isEmpty()){
41664 		s+=tr("Comments=%1").arg(comments);
41665 		s+="\n";
41666 	}
41667 
41668 	return s;
41669 }
41670 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)41671 double ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
41672 {
41673 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
41674 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
41675 		c.teachersMatrixReady=true;
41676 		c.subgroupsMatrixReady=true;
41677 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
41678 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
41679 
41680 		c.changedForMatrixCalculation=false;
41681 	}
41682 
41683 	int nbroken;
41684 
41685 	nbroken=0;
41686 
41687 	Matrix1D<bool> ocDay;
41688 	ocDay.resize(r.nDaysPerWeek);
41689 	for(int sbg : qAsConst(this->iSubgroupsList)){
41690 		for(int d=0; d<r.nDaysPerWeek; d+=2){ //morning
41691 			ocDay[d]=false;
41692 			for(int h=startHour; h<endHour; h++){
41693 				if(subgroupsMatrix[sbg][d][h]>0){
41694 					ocDay[d]=true;
41695 				}
41696 			}
41697 		}
41698 		int nOcDays=0;
41699 		for(int d=0; d<r.nDaysPerWeek; d+=2) //morning
41700 			if(ocDay[d])
41701 				nOcDays++;
41702 		if(nOcDays > this->maxDaysPerWeek){
41703 			nbroken+=nOcDays-this->maxDaysPerWeek;
41704 
41705 			if((nOcDays-this->maxDaysPerWeek)>0){
41706 				QString s= tr("Time constraint students set morning interval max days per week broken for subgroup: %1, allowed %2 days, required %3 days.")
41707 				 .arg(r.internalSubgroupsList[sbg]->name)
41708 				 .arg(this->maxDaysPerWeek)
41709 				 .arg(nOcDays);
41710 				s+=" ";
41711 				s += tr("This increases the conflicts total by %1")
41712 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
41713 
41714 				dl.append(s);
41715 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
41716 
41717 				*conflictsString += s+"\n";
41718 			}
41719 		}
41720 	}
41721 
41722 	if(weightPercentage==100)
41723 		assert(nbroken==0);
41724 	return weightPercentage/100 * nbroken;
41725 }
41726 
isRelatedToActivity(Rules & r,Activity * a)41727 bool ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
41728 {
41729 	Q_UNUSED(r);
41730 	Q_UNUSED(a);
41731 
41732 	return false;
41733 }
41734 
isRelatedToTeacher(Teacher * t)41735 bool ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
41736 {
41737 	Q_UNUSED(t);
41738 	return false;
41739 }
41740 
isRelatedToSubject(Subject * s)41741 bool ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
41742 {
41743 	Q_UNUSED(s);
41744 
41745 	return false;
41746 }
41747 
isRelatedToActivityTag(ActivityTag * s)41748 bool ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
41749 {
41750 	Q_UNUSED(s);
41751 
41752 	return false;
41753 }
41754 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)41755 bool ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
41756 {
41757 	return r.setsShareStudents(this->students, s->name);
41758 }
41759 
hasWrongDayOrHour(Rules & r)41760 bool ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
41761 {
41762 	if(this->startHour>=r.nHoursPerDay)
41763 		return true;
41764 	if(this->endHour>r.nHoursPerDay)
41765 		return true;
41766 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
41767 		return true;
41768 
41769 	return false;
41770 }
41771 
canRepairWrongDayOrHour(Rules & r)41772 bool ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
41773 {
41774 	assert(hasWrongDayOrHour(r));
41775 
41776 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
41777 		return true;
41778 
41779 	return false;
41780 }
41781 
repairWrongDayOrHour(Rules & r)41782 bool ConstraintStudentsSetMorningIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
41783 {
41784 	assert(hasWrongDayOrHour(r));
41785 
41786 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
41787 
41788 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
41789 		this->maxDaysPerWeek=r.nDaysPerWeek/2;
41790 
41791 	return true;
41792 }
41793 
41794 ///////////////////////////////////////////////////////////////////////////////////////////
41795 ///////////////////////////////////////////////////////////////////////////////////////////
41796 
ConstraintStudentsMorningIntervalMaxDaysPerWeek()41797 ConstraintStudentsMorningIntervalMaxDaysPerWeek::ConstraintStudentsMorningIntervalMaxDaysPerWeek()
41798 	: TimeConstraint()
41799 {
41800 	this->type=CONSTRAINT_STUDENTS_MORNING_INTERVAL_MAX_DAYS_PER_WEEK;
41801 }
41802 
ConstraintStudentsMorningIntervalMaxDaysPerWeek(double wp,int maxnd,int sh,int eh)41803 ConstraintStudentsMorningIntervalMaxDaysPerWeek::ConstraintStudentsMorningIntervalMaxDaysPerWeek(double wp, int maxnd, int sh, int eh)
41804 	 : TimeConstraint(wp)
41805 {
41806 	this->maxDaysPerWeek=maxnd;
41807 	this->type=CONSTRAINT_STUDENTS_MORNING_INTERVAL_MAX_DAYS_PER_WEEK;
41808 	this->startHour=sh;
41809 	this->endHour=eh;
41810 	assert(sh<eh);
41811 	assert(sh>=0);
41812 }
41813 
computeInternalStructure(QWidget * parent,Rules & r)41814 bool ConstraintStudentsMorningIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
41815 {
41816 	if(this->startHour>=this->endHour){
41817 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
41818 		 tr("Constraint students morning interval max days per week is wrong because start hour >= end hour."
41819 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
41820 
41821 		return false;
41822 	}
41823 	if(this->startHour<0){
41824 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
41825 		 tr("Constraint students morning interval max days per week is wrong because start hour < first hour of the day."
41826 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
41827 
41828 		return false;
41829 	}
41830 	if(this->endHour>r.nHoursPerDay){
41831 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
41832 		 tr("Constraint students morning interval max days per week is wrong because end hour > number of hours per day."
41833 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
41834 
41835 		return false;
41836 	}
41837 
41838 	return true;
41839 }
41840 
hasInactiveActivities(Rules & r)41841 bool ConstraintStudentsMorningIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
41842 {
41843 	Q_UNUSED(r);
41844 	return false;
41845 }
41846 
getXmlDescription(Rules & r)41847 QString ConstraintStudentsMorningIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
41848 {
41849 	Q_UNUSED(r);
41850 
41851 	QString s="<ConstraintStudentsMorningIntervalMaxDaysPerWeek>\n";
41852 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
41853 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
41854 	if(this->endHour < r.nHoursPerDay){
41855 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
41856 	}
41857 	else{
41858 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
41859 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
41860 	}
41861 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
41862 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
41863 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
41864 	s+="</ConstraintStudentsMorningIntervalMaxDaysPerWeek>\n";
41865 	return s;
41866 }
41867 
getDescription(Rules & r)41868 QString ConstraintStudentsMorningIntervalMaxDaysPerWeek::getDescription(Rules& r){
41869 	Q_UNUSED(r);
41870 
41871 	QString begin=QString("");
41872 	if(!active)
41873 		begin="X - ";
41874 
41875 	QString end=QString("");
41876 	if(!comments.isEmpty())
41877 		end=", "+tr("C: %1", "Comments").arg(comments);
41878 
41879 	QString s=tr("Students morning interval max days per week");s+=", ";
41880 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
41881 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);
41882 	s+=", ";
41883 	if(this->endHour<r.nHoursPerDay)
41884 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
41885 	else
41886 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
41887 	s+=", ";
41888 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
41889 
41890 	return begin+s+end;
41891 }
41892 
getDetailedDescription(Rules & r)41893 QString ConstraintStudentsMorningIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r){
41894 	Q_UNUSED(r);
41895 
41896 	QString s=tr("Time constraint");s+="\n";
41897 	s+=tr("All students respect working in an hourly morning interval a maximum number of days per week");s+="\n";
41898 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
41899 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
41900 
41901 	if(this->endHour<r.nHoursPerDay)
41902 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
41903 	else
41904 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
41905 	s+="\n";
41906 
41907 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
41908 
41909 	if(!active){
41910 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
41911 		s+="\n";
41912 	}
41913 	if(!comments.isEmpty()){
41914 		s+=tr("Comments=%1").arg(comments);
41915 		s+="\n";
41916 	}
41917 
41918 	return s;
41919 }
41920 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)41921 double ConstraintStudentsMorningIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
41922 {
41923 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
41924 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
41925 		c.teachersMatrixReady=true;
41926 		c.subgroupsMatrixReady=true;
41927 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
41928 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
41929 
41930 		c.changedForMatrixCalculation=false;
41931 	}
41932 
41933 	int nbroken;
41934 
41935 	nbroken=0;
41936 
41937 	Matrix1D<bool> ocDay;
41938 	ocDay.resize(r.nDaysPerWeek);
41939 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
41940 		for(int d=0; d<r.nDaysPerWeek; d+=2){ //morning
41941 			ocDay[d]=false;
41942 			for(int h=startHour; h<endHour; h++){
41943 				if(subgroupsMatrix[sbg][d][h]>0){
41944 					ocDay[d]=true;
41945 				}
41946 			}
41947 		}
41948 		int nOcDays=0;
41949 		for(int d=0; d<r.nDaysPerWeek; d+=2) //morning
41950 			if(ocDay[d])
41951 				nOcDays++;
41952 		if(nOcDays > this->maxDaysPerWeek){
41953 			nbroken+=nOcDays-this->maxDaysPerWeek;
41954 
41955 			if((nOcDays-this->maxDaysPerWeek)>0){
41956 				QString s= tr("Time constraint students morning interval max days per week broken for subgroup: %1, allowed %2 days, required %3 days.")
41957 				 .arg(r.internalSubgroupsList[sbg]->name)
41958 				 .arg(this->maxDaysPerWeek)
41959 				 .arg(nOcDays);
41960 				s+=" ";
41961 				s += tr("This increases the conflicts total by %1")
41962 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
41963 
41964 				dl.append(s);
41965 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
41966 
41967 				*conflictsString += s+"\n";
41968 			}
41969 		}
41970 	}
41971 
41972 	if(weightPercentage==100)
41973 		assert(nbroken==0);
41974 	return weightPercentage/100 * nbroken;
41975 }
41976 
isRelatedToActivity(Rules & r,Activity * a)41977 bool ConstraintStudentsMorningIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
41978 {
41979 	Q_UNUSED(r);
41980 	Q_UNUSED(a);
41981 
41982 	return false;
41983 }
41984 
isRelatedToTeacher(Teacher * t)41985 bool ConstraintStudentsMorningIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
41986 {
41987 	Q_UNUSED(t);
41988 	return false;
41989 }
41990 
isRelatedToSubject(Subject * s)41991 bool ConstraintStudentsMorningIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
41992 {
41993 	Q_UNUSED(s);
41994 
41995 	return false;
41996 }
41997 
isRelatedToActivityTag(ActivityTag * s)41998 bool ConstraintStudentsMorningIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
41999 {
42000 	Q_UNUSED(s);
42001 
42002 	return false;
42003 }
42004 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)42005 bool ConstraintStudentsMorningIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
42006 {
42007 	Q_UNUSED(r);
42008 	Q_UNUSED(s);
42009 	return true;
42010 }
42011 
hasWrongDayOrHour(Rules & r)42012 bool ConstraintStudentsMorningIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
42013 {
42014 	if(this->startHour>=r.nHoursPerDay)
42015 		return true;
42016 	if(this->endHour>r.nHoursPerDay)
42017 		return true;
42018 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
42019 		return true;
42020 
42021 	return false;
42022 }
42023 
canRepairWrongDayOrHour(Rules & r)42024 bool ConstraintStudentsMorningIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
42025 {
42026 	assert(hasWrongDayOrHour(r));
42027 
42028 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
42029 		return true;
42030 
42031 	return false;
42032 }
42033 
repairWrongDayOrHour(Rules & r)42034 bool ConstraintStudentsMorningIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
42035 {
42036 	assert(hasWrongDayOrHour(r));
42037 
42038 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
42039 
42040 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
42041 		this->maxDaysPerWeek=r.nDaysPerWeek/2;
42042 
42043 	return true;
42044 }
42045 
42046 ///////////////////////////////////////////////////////////////////////////////////////////
42047 ///////////////////////////////////////////////////////////////////////////////////////////
42048 
ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek()42049 ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek()
42050 	: TimeConstraint()
42051 {
42052 	this->type=CONSTRAINT_STUDENTS_SET_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK;
42053 }
42054 
ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek(double wp,int maxnd,QString sn,int sh,int eh)42055 ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek(double wp, int maxnd, QString sn, int sh, int eh)
42056 	 : TimeConstraint(wp)
42057 {
42058 	this->students = sn;
42059 	this->maxDaysPerWeek=maxnd;
42060 	this->type=CONSTRAINT_STUDENTS_SET_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK;
42061 	this->startHour=sh;
42062 	this->endHour=eh;
42063 	assert(sh<eh);
42064 	assert(sh>=0);
42065 }
42066 
computeInternalStructure(QWidget * parent,Rules & r)42067 bool ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
42068 {
42069 	if(this->startHour>=this->endHour){
42070 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
42071 		 tr("Constraint students set afternoon interval max days per week is wrong because start hour >= end hour."
42072 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
42073 
42074 		return false;
42075 	}
42076 	if(this->startHour<0){
42077 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
42078 		 tr("Constraint students set afternoon interval max days per week is wrong because start hour < first hour of the day."
42079 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
42080 
42081 		return false;
42082 	}
42083 	if(this->endHour>r.nHoursPerDay){
42084 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
42085 		 tr("Constraint students set afternoon interval max days per week is wrong because end hour > number of hours per day."
42086 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
42087 
42088 		return false;
42089 	}
42090 
42091 	/////////
42092 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
42093 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
42094 
42095 	if(ss==nullptr){
42096 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
42097 		 tr("Constraint students set afternoon interval max days per week is wrong because it refers to inexistent students set."
42098 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
42099 
42100 		return false;
42101 	}
42102 
42103 	assert(ss!=nullptr);
42104 
42105 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
42106 	/*this->iSubgroupsList.clear();
42107 	if(ss->type==STUDENTS_SUBGROUP){
42108 		int tmp;
42109 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
42110 		assert(tmp>=0);
42111 		assert(tmp<r.nInternalSubgroups);
42112 		if(!this->iSubgroupsList.contains(tmp))
42113 			this->iSubgroupsList.append(tmp);
42114 	}
42115 	else if(ss->type==STUDENTS_GROUP){
42116 		StudentsGroup* stg=(StudentsGroup*)ss;
42117 		for(int i=0; i<stg->subgroupsList.size(); i++){
42118 			StudentsSubgroup* sts=stg->subgroupsList[i];
42119 			int tmp;
42120 			tmp=sts->indexInInternalSubgroupsList;
42121 			assert(tmp>=0);
42122 			assert(tmp<r.nInternalSubgroups);
42123 			if(!this->iSubgroupsList.contains(tmp))
42124 				this->iSubgroupsList.append(tmp);
42125 		}
42126 	}
42127 	else if(ss->type==STUDENTS_YEAR){
42128 		StudentsYear* sty=(StudentsYear*)ss;
42129 		for(int i=0; i<sty->groupsList.size(); i++){
42130 			StudentsGroup* stg=sty->groupsList[i];
42131 			for(int j=0; j<stg->subgroupsList.size(); j++){
42132 				StudentsSubgroup* sts=stg->subgroupsList[j];
42133 				int tmp;
42134 				tmp=sts->indexInInternalSubgroupsList;
42135 				assert(tmp>=0);
42136 				assert(tmp<r.nInternalSubgroups);
42137 				if(!this->iSubgroupsList.contains(tmp))
42138 					this->iSubgroupsList.append(tmp);
42139 			}
42140 		}
42141 	}
42142 	else
42143 		assert(0);*/
42144 
42145 	return true;
42146 }
42147 
hasInactiveActivities(Rules & r)42148 bool ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
42149 {
42150 	Q_UNUSED(r);
42151 	return false;
42152 }
42153 
getXmlDescription(Rules & r)42154 QString ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
42155 {
42156 	Q_UNUSED(r);
42157 
42158 	QString s="<ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek>\n";
42159 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
42160 	s+="	<Students>"+protect(this->students)+"</Students>\n";
42161 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
42162 	if(this->endHour < r.nHoursPerDay){
42163 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
42164 	}
42165 	else{
42166 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
42167 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
42168 	}
42169 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
42170 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
42171 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
42172 	s+="</ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek>\n";
42173 	return s;
42174 }
42175 
getDescription(Rules & r)42176 QString ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::getDescription(Rules& r){
42177 	Q_UNUSED(r);
42178 
42179 	QString begin=QString("");
42180 	if(!active)
42181 		begin="X - ";
42182 
42183 	QString end=QString("");
42184 	if(!comments.isEmpty())
42185 		end=", "+tr("C: %1", "Comments").arg(comments);
42186 
42187 	QString s=tr("Students set afternoon interval max days per week");s+=", ";
42188 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
42189 	s+=tr("St:%1", "Abbreviation for students (sets)").arg(this->students);s+=", ";
42190 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);
42191 	s+=", ";
42192 	if(this->endHour<r.nHoursPerDay)
42193 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
42194 	else
42195 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
42196 	s+=", ";
42197 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
42198 
42199 	return begin+s+end;
42200 }
42201 
getDetailedDescription(Rules & r)42202 QString ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r){
42203 	Q_UNUSED(r);
42204 
42205 	QString s=tr("Time constraint");s+="\n";
42206 	s+=tr("A students set respects working in an hourly afternoon interval a maximum number of days per week");s+="\n";
42207 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
42208 	s+=tr("Students set=%1").arg(this->students);s+="\n";
42209 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
42210 
42211 	if(this->endHour<r.nHoursPerDay)
42212 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
42213 	else
42214 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
42215 	s+="\n";
42216 
42217 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
42218 
42219 	if(!active){
42220 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
42221 		s+="\n";
42222 	}
42223 	if(!comments.isEmpty()){
42224 		s+=tr("Comments=%1").arg(comments);
42225 		s+="\n";
42226 	}
42227 
42228 	return s;
42229 }
42230 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)42231 double ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
42232 {
42233 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
42234 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
42235 		c.teachersMatrixReady=true;
42236 		c.subgroupsMatrixReady=true;
42237 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
42238 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
42239 
42240 		c.changedForMatrixCalculation=false;
42241 	}
42242 
42243 	int nbroken;
42244 
42245 	nbroken=0;
42246 
42247 	Matrix1D<bool> ocDay;
42248 	ocDay.resize(r.nDaysPerWeek);
42249 	for(int sbg : qAsConst(this->iSubgroupsList)){
42250 		for(int d=1; d<r.nDaysPerWeek; d+=2){ //afternoon
42251 			ocDay[d]=false;
42252 			for(int h=startHour; h<endHour; h++){
42253 				if(subgroupsMatrix[sbg][d][h]>0){
42254 					ocDay[d]=true;
42255 				}
42256 			}
42257 		}
42258 		int nOcDays=0;
42259 		for(int d=1; d<r.nDaysPerWeek; d+=2) //afternoon
42260 			if(ocDay[d])
42261 				nOcDays++;
42262 		if(nOcDays > this->maxDaysPerWeek){
42263 			nbroken+=nOcDays-this->maxDaysPerWeek;
42264 
42265 			if((nOcDays-this->maxDaysPerWeek)>0){
42266 				QString s= tr("Time constraint students set afternoon interval max days per week broken for subgroup: %1, allowed %2 days, required %3 days.")
42267 				 .arg(r.internalSubgroupsList[sbg]->name)
42268 				 .arg(this->maxDaysPerWeek)
42269 				 .arg(nOcDays);
42270 				s+=" ";
42271 				s += tr("This increases the conflicts total by %1")
42272 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
42273 
42274 				dl.append(s);
42275 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
42276 
42277 				*conflictsString += s+"\n";
42278 			}
42279 		}
42280 	}
42281 
42282 	if(weightPercentage==100)
42283 		assert(nbroken==0);
42284 	return weightPercentage/100 * nbroken;
42285 }
42286 
isRelatedToActivity(Rules & r,Activity * a)42287 bool ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
42288 {
42289 	Q_UNUSED(r);
42290 	Q_UNUSED(a);
42291 
42292 	return false;
42293 }
42294 
isRelatedToTeacher(Teacher * t)42295 bool ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
42296 {
42297 	Q_UNUSED(t);
42298 	return false;
42299 }
42300 
isRelatedToSubject(Subject * s)42301 bool ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
42302 {
42303 	Q_UNUSED(s);
42304 
42305 	return false;
42306 }
42307 
isRelatedToActivityTag(ActivityTag * s)42308 bool ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
42309 {
42310 	Q_UNUSED(s);
42311 
42312 	return false;
42313 }
42314 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)42315 bool ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
42316 {
42317 	return r.setsShareStudents(this->students, s->name);
42318 }
42319 
hasWrongDayOrHour(Rules & r)42320 bool ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
42321 {
42322 	if(this->startHour>=r.nHoursPerDay)
42323 		return true;
42324 	if(this->endHour>r.nHoursPerDay)
42325 		return true;
42326 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
42327 		return true;
42328 
42329 	return false;
42330 }
42331 
canRepairWrongDayOrHour(Rules & r)42332 bool ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
42333 {
42334 	assert(hasWrongDayOrHour(r));
42335 
42336 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
42337 		return true;
42338 
42339 	return false;
42340 }
42341 
repairWrongDayOrHour(Rules & r)42342 bool ConstraintStudentsSetAfternoonIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
42343 {
42344 	assert(hasWrongDayOrHour(r));
42345 
42346 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
42347 
42348 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
42349 		this->maxDaysPerWeek=r.nDaysPerWeek/2;
42350 
42351 	return true;
42352 }
42353 
42354 ///////////////////////////////////////////////////////////////////////////////////////////
42355 ///////////////////////////////////////////////////////////////////////////////////////////
42356 
ConstraintStudentsAfternoonIntervalMaxDaysPerWeek()42357 ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::ConstraintStudentsAfternoonIntervalMaxDaysPerWeek()
42358 	: TimeConstraint()
42359 {
42360 	this->type=CONSTRAINT_STUDENTS_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK;
42361 }
42362 
ConstraintStudentsAfternoonIntervalMaxDaysPerWeek(double wp,int maxnd,int sh,int eh)42363 ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::ConstraintStudentsAfternoonIntervalMaxDaysPerWeek(double wp, int maxnd, int sh, int eh)
42364 	 : TimeConstraint(wp)
42365 {
42366 	this->maxDaysPerWeek=maxnd;
42367 	this->type=CONSTRAINT_STUDENTS_AFTERNOON_INTERVAL_MAX_DAYS_PER_WEEK;
42368 	this->startHour=sh;
42369 	this->endHour=eh;
42370 	assert(sh<eh);
42371 	assert(sh>=0);
42372 }
42373 
computeInternalStructure(QWidget * parent,Rules & r)42374 bool ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::computeInternalStructure(QWidget* parent, Rules& r)
42375 {
42376 	if(this->startHour>=this->endHour){
42377 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
42378 		 tr("Constraint students afternoon interval max days per week is wrong because start hour >= end hour."
42379 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
42380 
42381 		return false;
42382 	}
42383 	if(this->startHour<0){
42384 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
42385 		 tr("Constraint students afternoon interval max days per week is wrong because start hour < first hour of the day."
42386 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
42387 
42388 		return false;
42389 	}
42390 	if(this->endHour>r.nHoursPerDay){
42391 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
42392 		 tr("Constraint students afternoon interval max days per week is wrong because end hour > number of hours per day."
42393 		 " Please correct it. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
42394 
42395 		return false;
42396 	}
42397 
42398 	return true;
42399 }
42400 
hasInactiveActivities(Rules & r)42401 bool ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::hasInactiveActivities(Rules& r)
42402 {
42403 	Q_UNUSED(r);
42404 	return false;
42405 }
42406 
getXmlDescription(Rules & r)42407 QString ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::getXmlDescription(Rules& r)
42408 {
42409 	Q_UNUSED(r);
42410 
42411 	QString s="<ConstraintStudentsAfternoonIntervalMaxDaysPerWeek>\n";
42412 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
42413 	s+="	<Interval_Start_Hour>"+protect(r.hoursOfTheDay[this->startHour])+"</Interval_Start_Hour>\n";
42414 	if(this->endHour < r.nHoursPerDay){
42415 		s+="	<Interval_End_Hour>"+protect(r.hoursOfTheDay[this->endHour])+"</Interval_End_Hour>\n";
42416 	}
42417 	else{
42418 		s+="	<Interval_End_Hour></Interval_End_Hour>\n";
42419 		s+="	<!-- Interval_End_Hour void means the end of the day (which has no name) -->\n";
42420 	}
42421 	s+="	<Max_Days_Per_Week>"+CustomFETString::number(this->maxDaysPerWeek)+"</Max_Days_Per_Week>\n";
42422 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
42423 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
42424 	s+="</ConstraintStudentsAfternoonIntervalMaxDaysPerWeek>\n";
42425 	return s;
42426 }
42427 
getDescription(Rules & r)42428 QString ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::getDescription(Rules& r){
42429 	Q_UNUSED(r);
42430 
42431 	QString begin=QString("");
42432 	if(!active)
42433 		begin="X - ";
42434 
42435 	QString end=QString("");
42436 	if(!comments.isEmpty())
42437 		end=", "+tr("C: %1", "Comments").arg(comments);
42438 
42439 	QString s=tr("Students afternoon interval max days per week");s+=", ";
42440 	s+=tr("WP:%1%", "Abbreviation for weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
42441 	s+=tr("ISH:%1", "Abbreviation for interval start hour").arg(r.hoursOfTheDay[this->startHour]);
42442 	s+=", ";
42443 	if(this->endHour<r.nHoursPerDay)
42444 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(r.hoursOfTheDay[this->endHour]);
42445 	else
42446 		s+=tr("IEH:%1", "Abbreviation for interval end hour").arg(tr("End of the day"));
42447 	s+=", ";
42448 	s+=tr("MD:%1", "Abbreviation for max days").arg(this->maxDaysPerWeek);
42449 
42450 	return begin+s+end;
42451 }
42452 
getDetailedDescription(Rules & r)42453 QString ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::getDetailedDescription(Rules& r){
42454 	Q_UNUSED(r);
42455 
42456 	QString s=tr("Time constraint");s+="\n";
42457 	s+=tr("All students respect working in an hourly afternoon interval a maximum number of days per week");s+="\n";
42458 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
42459 	s+=tr("Interval start hour=%1").arg(r.hoursOfTheDay[this->startHour]);s+="\n";
42460 
42461 	if(this->endHour<r.nHoursPerDay)
42462 		s+=tr("Interval end hour=%1").arg(r.hoursOfTheDay[this->endHour]);
42463 	else
42464 		s+=tr("Interval end hour=%1").arg(tr("End of the day"));
42465 	s+="\n";
42466 
42467 	s+=tr("Maximum days per week=%1").arg(this->maxDaysPerWeek);s+="\n";
42468 
42469 	if(!active){
42470 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
42471 		s+="\n";
42472 	}
42473 	if(!comments.isEmpty()){
42474 		s+=tr("Comments=%1").arg(comments);
42475 		s+="\n";
42476 	}
42477 
42478 	return s;
42479 }
42480 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)42481 double ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
42482 {
42483 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
42484 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
42485 		c.teachersMatrixReady=true;
42486 		c.subgroupsMatrixReady=true;
42487 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
42488 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
42489 
42490 		c.changedForMatrixCalculation=false;
42491 	}
42492 
42493 	int nbroken;
42494 
42495 	nbroken=0;
42496 
42497 	Matrix1D<bool> ocDay;
42498 	ocDay.resize(r.nDaysPerWeek);
42499 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
42500 		for(int d=1; d<r.nDaysPerWeek; d+=2){ //afternoon
42501 			ocDay[d]=false;
42502 			for(int h=startHour; h<endHour; h++){
42503 				if(subgroupsMatrix[sbg][d][h]>0){
42504 					ocDay[d]=true;
42505 				}
42506 			}
42507 		}
42508 		int nOcDays=0;
42509 		for(int d=1; d<r.nDaysPerWeek; d+=2) //afternoon
42510 			if(ocDay[d])
42511 				nOcDays++;
42512 		if(nOcDays > this->maxDaysPerWeek){
42513 			nbroken+=nOcDays-this->maxDaysPerWeek;
42514 
42515 			if((nOcDays-this->maxDaysPerWeek)>0){
42516 				QString s= tr("Time constraint students afternoon interval max days per week broken for subgroup: %1, allowed %2 days, required %3 days.")
42517 				 .arg(r.internalSubgroupsList[sbg]->name)
42518 				 .arg(this->maxDaysPerWeek)
42519 				 .arg(nOcDays);
42520 				s+=" ";
42521 				s += tr("This increases the conflicts total by %1")
42522 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nOcDays-this->maxDaysPerWeek)*weightPercentage/100));
42523 
42524 				dl.append(s);
42525 				cl.append((nOcDays-this->maxDaysPerWeek)*weightPercentage/100);
42526 
42527 				*conflictsString += s+"\n";
42528 			}
42529 		}
42530 	}
42531 
42532 	if(weightPercentage==100)
42533 		assert(nbroken==0);
42534 	return weightPercentage/100 * nbroken;
42535 }
42536 
isRelatedToActivity(Rules & r,Activity * a)42537 bool ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::isRelatedToActivity(Rules& r, Activity* a)
42538 {
42539 	Q_UNUSED(r);
42540 	Q_UNUSED(a);
42541 
42542 	return false;
42543 }
42544 
isRelatedToTeacher(Teacher * t)42545 bool ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::isRelatedToTeacher(Teacher* t)
42546 {
42547 	Q_UNUSED(t);
42548 	return false;
42549 }
42550 
isRelatedToSubject(Subject * s)42551 bool ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::isRelatedToSubject(Subject* s)
42552 {
42553 	Q_UNUSED(s);
42554 
42555 	return false;
42556 }
42557 
isRelatedToActivityTag(ActivityTag * s)42558 bool ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::isRelatedToActivityTag(ActivityTag* s)
42559 {
42560 	Q_UNUSED(s);
42561 
42562 	return false;
42563 }
42564 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)42565 bool ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
42566 {
42567 	Q_UNUSED(r);
42568 	Q_UNUSED(s);
42569 	return true;
42570 }
42571 
hasWrongDayOrHour(Rules & r)42572 bool ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::hasWrongDayOrHour(Rules& r)
42573 {
42574 	if(this->startHour>=r.nHoursPerDay)
42575 		return true;
42576 	if(this->endHour>r.nHoursPerDay)
42577 		return true;
42578 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
42579 		return true;
42580 
42581 	return false;
42582 }
42583 
canRepairWrongDayOrHour(Rules & r)42584 bool ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::canRepairWrongDayOrHour(Rules& r)
42585 {
42586 	assert(hasWrongDayOrHour(r));
42587 
42588 	if(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay)
42589 		return true;
42590 
42591 	return false;
42592 }
42593 
repairWrongDayOrHour(Rules & r)42594 bool ConstraintStudentsAfternoonIntervalMaxDaysPerWeek::repairWrongDayOrHour(Rules& r)
42595 {
42596 	assert(hasWrongDayOrHour(r));
42597 
42598 	assert(this->startHour<r.nHoursPerDay && this->endHour<=r.nHoursPerDay);
42599 
42600 	if(this->maxDaysPerWeek>r.nDaysPerWeek/2)
42601 		this->maxDaysPerWeek=r.nDaysPerWeek/2;
42602 
42603 	return true;
42604 }
42605 
42606 //2020-06-28
42607 ///////////////////////////////////////////////////////////////////////////////////////////
42608 ///////////////////////////////////////////////////////////////////////////////////////////
42609 
ConstraintTeacherMaxHoursPerAllAfternoons()42610 ConstraintTeacherMaxHoursPerAllAfternoons::ConstraintTeacherMaxHoursPerAllAfternoons()
42611 	: TimeConstraint()
42612 {
42613 	this->type=CONSTRAINT_TEACHER_MAX_HOURS_PER_ALL_AFTERNOONS;
42614 }
42615 
ConstraintTeacherMaxHoursPerAllAfternoons(double wp,int maxhours,const QString & teacher)42616 ConstraintTeacherMaxHoursPerAllAfternoons::ConstraintTeacherMaxHoursPerAllAfternoons(double wp, int maxhours, const QString& teacher)
42617  : TimeConstraint(wp)
42618  {
42619 	assert(maxhours>0);
42620 	this->maxHoursPerAllAfternoons=maxhours;
42621 	this->teacherName=teacher;
42622 
42623 	this->type=CONSTRAINT_TEACHER_MAX_HOURS_PER_ALL_AFTERNOONS;
42624 }
42625 
computeInternalStructure(QWidget * parent,Rules & r)42626 bool ConstraintTeacherMaxHoursPerAllAfternoons::computeInternalStructure(QWidget* parent, Rules& r)
42627 {
42628 	Q_UNUSED(parent);
42629 
42630 	//this->teacher_ID=r.searchTeacher(this->teacherName);
42631 	teacher_ID=r.teachersHash.value(teacherName, -1);
42632 	assert(this->teacher_ID>=0);
42633 
42634 	return true;
42635 }
42636 
hasInactiveActivities(Rules & r)42637 bool ConstraintTeacherMaxHoursPerAllAfternoons::hasInactiveActivities(Rules& r)
42638 {
42639 	Q_UNUSED(r);
42640 	return false;
42641 }
42642 
getXmlDescription(Rules & r)42643 QString ConstraintTeacherMaxHoursPerAllAfternoons::getXmlDescription(Rules& r){
42644 	Q_UNUSED(r);
42645 
42646 	QString s="<ConstraintTeacherMaxHoursPerAllAfternoons>\n";
42647 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
42648 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
42649 	s+="	<Maximum_Hours_Per_All_Afternoons>"+CustomFETString::number(this->maxHoursPerAllAfternoons)+"</Maximum_Hours_Per_All_Afternoons>\n";
42650 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
42651 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
42652 	s+="</ConstraintTeacherMaxHoursPerAllAfternoons>\n";
42653 	return s;
42654 }
42655 
getDescription(Rules & r)42656 QString ConstraintTeacherMaxHoursPerAllAfternoons::getDescription(Rules& r){
42657 	Q_UNUSED(r);
42658 
42659 	QString begin=QString("");
42660 	if(!active)
42661 		begin="X - ";
42662 
42663 	QString end=QString("");
42664 	if(!comments.isEmpty())
42665 		end=", "+tr("C: %1", "Comments").arg(comments);
42666 
42667 	QString s;
42668 	s+=tr("Teacher max hours per all afternoons");s+=", ";
42669 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
42670 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
42671 	s+=tr("MHAA:%1", "Maximum hours per all afternoons").arg(this->maxHoursPerAllAfternoons);
42672 
42673 	return begin+s+end;
42674 }
42675 
getDetailedDescription(Rules & r)42676 QString ConstraintTeacherMaxHoursPerAllAfternoons::getDetailedDescription(Rules& r){
42677 	Q_UNUSED(r);
42678 
42679 	QString s=tr("Time constraint");s+="\n";
42680 	s+=tr("A teacher must respect the maximum number of hours per all afternoons");s+="\n";
42681 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
42682 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
42683 	s+=tr("Maximum hours per all afternoons=%1").arg(this->maxHoursPerAllAfternoons);s+="\n";
42684 
42685 	if(!active){
42686 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
42687 		s+="\n";
42688 	}
42689 	if(!comments.isEmpty()){
42690 		s+=tr("Comments=%1").arg(comments);
42691 		s+="\n";
42692 	}
42693 
42694 	return s;
42695 }
42696 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)42697 double ConstraintTeacherMaxHoursPerAllAfternoons::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
42698 {
42699 	Q_UNUSED(cl);
42700 	Q_UNUSED(dl);
42701 	Q_UNUSED(conflictsString);
42702 
42703 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
42704 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
42705 		c.teachersMatrixReady=true;
42706 		c.subgroupsMatrixReady=true;
42707 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
42708 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
42709 
42710 		c.changedForMatrixCalculation=false;
42711 	}
42712 
42713 	int nbroken=0;
42714 
42715 	int i=this->teacher_ID;
42716 	int n_hours=0;
42717 	for(int d=1; d<r.nDaysPerWeek; d+=2) //afternoon
42718 		for(int h=0; h<r.nHoursPerDay; h++)
42719 			if(teachersMatrix[i][d][h]>0)
42720 				n_hours++;
42721 
42722 	if(n_hours>this->maxHoursPerAllAfternoons)
42723 		nbroken++;
42724 
42725 	assert(weightPercentage==100);
42726 	if(weightPercentage==100)
42727 		assert(nbroken==0);
42728 
42729 	return weightPercentage/100 * nbroken;
42730 }
42731 
isRelatedToActivity(Rules & r,Activity * a)42732 bool ConstraintTeacherMaxHoursPerAllAfternoons::isRelatedToActivity(Rules& r, Activity* a)
42733 {
42734 	Q_UNUSED(r);
42735 	Q_UNUSED(a);
42736 
42737 	return false;
42738 }
42739 
isRelatedToTeacher(Teacher * t)42740 bool ConstraintTeacherMaxHoursPerAllAfternoons::isRelatedToTeacher(Teacher* t)
42741 {
42742 	if(this->teacherName==t->name)
42743 		return true;
42744 	return false;
42745 }
42746 
isRelatedToSubject(Subject * s)42747 bool ConstraintTeacherMaxHoursPerAllAfternoons::isRelatedToSubject(Subject* s)
42748 {
42749 	Q_UNUSED(s);
42750 
42751 	return false;
42752 }
42753 
isRelatedToActivityTag(ActivityTag * s)42754 bool ConstraintTeacherMaxHoursPerAllAfternoons::isRelatedToActivityTag(ActivityTag* s)
42755 {
42756 	Q_UNUSED(s);
42757 
42758 	return false;
42759 }
42760 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)42761 bool ConstraintTeacherMaxHoursPerAllAfternoons::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
42762 {
42763 	Q_UNUSED(r);
42764 	Q_UNUSED(s);
42765 
42766 	return false;
42767 }
42768 
hasWrongDayOrHour(Rules & r)42769 bool ConstraintTeacherMaxHoursPerAllAfternoons::hasWrongDayOrHour(Rules& r)
42770 {
42771 	if(maxHoursPerAllAfternoons>r.nDaysPerWeek*r.nHoursPerDay/2)
42772 		return true;
42773 
42774 	return false;
42775 }
42776 
canRepairWrongDayOrHour(Rules & r)42777 bool ConstraintTeacherMaxHoursPerAllAfternoons::canRepairWrongDayOrHour(Rules& r)
42778 {
42779 	assert(hasWrongDayOrHour(r));
42780 
42781 	return true;
42782 }
42783 
repairWrongDayOrHour(Rules & r)42784 bool ConstraintTeacherMaxHoursPerAllAfternoons::repairWrongDayOrHour(Rules& r)
42785 {
42786 	assert(hasWrongDayOrHour(r));
42787 
42788 	if(maxHoursPerAllAfternoons>r.nDaysPerWeek*r.nHoursPerDay/2)
42789 		maxHoursPerAllAfternoons=r.nDaysPerWeek*r.nHoursPerDay/2;
42790 
42791 	return true;
42792 }
42793 
42794 ///////////////////////////////////////////////////////////////////////////////////////////
42795 ///////////////////////////////////////////////////////////////////////////////////////////
42796 
ConstraintTeachersMaxHoursPerAllAfternoons()42797 ConstraintTeachersMaxHoursPerAllAfternoons::ConstraintTeachersMaxHoursPerAllAfternoons()
42798 	: TimeConstraint()
42799 {
42800 	this->type=CONSTRAINT_TEACHERS_MAX_HOURS_PER_ALL_AFTERNOONS;
42801 }
42802 
ConstraintTeachersMaxHoursPerAllAfternoons(double wp,int maxhours)42803 ConstraintTeachersMaxHoursPerAllAfternoons::ConstraintTeachersMaxHoursPerAllAfternoons(double wp, int maxhours)
42804  : TimeConstraint(wp)
42805  {
42806 	assert(maxhours>0);
42807 	this->maxHoursPerAllAfternoons=maxhours;
42808 
42809 	this->type=CONSTRAINT_TEACHERS_MAX_HOURS_PER_ALL_AFTERNOONS;
42810 }
42811 
computeInternalStructure(QWidget * parent,Rules & r)42812 bool ConstraintTeachersMaxHoursPerAllAfternoons::computeInternalStructure(QWidget* parent, Rules& r)
42813 {
42814 	Q_UNUSED(parent);
42815 	Q_UNUSED(r);
42816 
42817 	return true;
42818 }
42819 
hasInactiveActivities(Rules & r)42820 bool ConstraintTeachersMaxHoursPerAllAfternoons::hasInactiveActivities(Rules& r)
42821 {
42822 	Q_UNUSED(r);
42823 	return false;
42824 }
42825 
getXmlDescription(Rules & r)42826 QString ConstraintTeachersMaxHoursPerAllAfternoons::getXmlDescription(Rules& r){
42827 	Q_UNUSED(r);
42828 
42829 	QString s="<ConstraintTeachersMaxHoursPerAllAfternoons>\n";
42830 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
42831 	s+="	<Maximum_Hours_Per_All_Afternoons>"+CustomFETString::number(this->maxHoursPerAllAfternoons)+"</Maximum_Hours_Per_All_Afternoons>\n";
42832 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
42833 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
42834 	s+="</ConstraintTeachersMaxHoursPerAllAfternoons>\n";
42835 	return s;
42836 }
42837 
getDescription(Rules & r)42838 QString ConstraintTeachersMaxHoursPerAllAfternoons::getDescription(Rules& r){
42839 	Q_UNUSED(r);
42840 
42841 	QString begin=QString("");
42842 	if(!active)
42843 		begin="X - ";
42844 
42845 	QString end=QString("");
42846 	if(!comments.isEmpty())
42847 		end=", "+tr("C: %1", "Comments").arg(comments);
42848 
42849 	QString s;
42850 	s+=tr("Teachers max hours per all afternoons");s+=", ";
42851 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
42852 	s+=tr("MHAA:%1", "Maximum hours per all afternoons").arg(this->maxHoursPerAllAfternoons);
42853 
42854 	return begin+s+end;
42855 }
42856 
getDetailedDescription(Rules & r)42857 QString ConstraintTeachersMaxHoursPerAllAfternoons::getDetailedDescription(Rules& r){
42858 	Q_UNUSED(r);
42859 
42860 	QString s=tr("Time constraint");s+="\n";
42861 	s+=tr("All teachers must respect the maximum number of hours per all afternoons");s+="\n";
42862 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
42863 	s+=tr("Maximum hours per all afternoons=%1").arg(this->maxHoursPerAllAfternoons);s+="\n";
42864 
42865 	if(!active){
42866 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
42867 		s+="\n";
42868 	}
42869 	if(!comments.isEmpty()){
42870 		s+=tr("Comments=%1").arg(comments);
42871 		s+="\n";
42872 	}
42873 
42874 	return s;
42875 }
42876 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)42877 double ConstraintTeachersMaxHoursPerAllAfternoons::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
42878 {
42879 	Q_UNUSED(cl);
42880 	Q_UNUSED(dl);
42881 	Q_UNUSED(conflictsString);
42882 
42883 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
42884 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
42885 		c.teachersMatrixReady=true;
42886 		c.subgroupsMatrixReady=true;
42887 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
42888 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
42889 
42890 		c.changedForMatrixCalculation=false;
42891 	}
42892 
42893 	int nbroken=0;
42894 
42895 	for(int i=0; i<r.nInternalTeachers; i++){
42896 		int n_hours=0;
42897 		for(int d=1; d<r.nDaysPerWeek; d+=2) //afternoon
42898 			for(int h=0; h<r.nHoursPerDay; h++)
42899 				if(teachersMatrix[i][d][h]>0)
42900 					n_hours++;
42901 
42902 		if(n_hours>this->maxHoursPerAllAfternoons)
42903 			nbroken++;
42904 	}
42905 
42906 	assert(weightPercentage==100);
42907 	if(weightPercentage==100)
42908 		assert(nbroken==0);
42909 
42910 	return weightPercentage/100 * nbroken;
42911 }
42912 
isRelatedToActivity(Rules & r,Activity * a)42913 bool ConstraintTeachersMaxHoursPerAllAfternoons::isRelatedToActivity(Rules& r, Activity* a)
42914 {
42915 	Q_UNUSED(r);
42916 	Q_UNUSED(a);
42917 
42918 	return false;
42919 }
42920 
isRelatedToTeacher(Teacher * t)42921 bool ConstraintTeachersMaxHoursPerAllAfternoons::isRelatedToTeacher(Teacher* t)
42922 {
42923 	Q_UNUSED(t);
42924 
42925 	return true;
42926 }
42927 
isRelatedToSubject(Subject * s)42928 bool ConstraintTeachersMaxHoursPerAllAfternoons::isRelatedToSubject(Subject* s)
42929 {
42930 	Q_UNUSED(s);
42931 
42932 	return false;
42933 }
42934 
isRelatedToActivityTag(ActivityTag * s)42935 bool ConstraintTeachersMaxHoursPerAllAfternoons::isRelatedToActivityTag(ActivityTag* s)
42936 {
42937 	Q_UNUSED(s);
42938 
42939 	return false;
42940 }
42941 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)42942 bool ConstraintTeachersMaxHoursPerAllAfternoons::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
42943 {
42944 	Q_UNUSED(r);
42945 	Q_UNUSED(s);
42946 
42947 	return false;
42948 }
42949 
hasWrongDayOrHour(Rules & r)42950 bool ConstraintTeachersMaxHoursPerAllAfternoons::hasWrongDayOrHour(Rules& r)
42951 {
42952 	if(maxHoursPerAllAfternoons>r.nDaysPerWeek*r.nHoursPerDay/2)
42953 		return true;
42954 
42955 	return false;
42956 }
42957 
canRepairWrongDayOrHour(Rules & r)42958 bool ConstraintTeachersMaxHoursPerAllAfternoons::canRepairWrongDayOrHour(Rules& r)
42959 {
42960 	assert(hasWrongDayOrHour(r));
42961 
42962 	return true;
42963 }
42964 
repairWrongDayOrHour(Rules & r)42965 bool ConstraintTeachersMaxHoursPerAllAfternoons::repairWrongDayOrHour(Rules& r)
42966 {
42967 	assert(hasWrongDayOrHour(r));
42968 
42969 	if(maxHoursPerAllAfternoons>r.nDaysPerWeek*r.nHoursPerDay/2)
42970 		maxHoursPerAllAfternoons=r.nDaysPerWeek*r.nHoursPerDay/2;
42971 
42972 	return true;
42973 }
42974 
42975 //2020-06-28
42976 ///////////////////////////////////////////////////////////////////////////////////////////
42977 ///////////////////////////////////////////////////////////////////////////////////////////
42978 
ConstraintStudentsSetMaxHoursPerAllAfternoons()42979 ConstraintStudentsSetMaxHoursPerAllAfternoons::ConstraintStudentsSetMaxHoursPerAllAfternoons()
42980 	: TimeConstraint()
42981 {
42982 	this->type=CONSTRAINT_STUDENTS_SET_MAX_HOURS_PER_ALL_AFTERNOONS;
42983 }
42984 
ConstraintStudentsSetMaxHoursPerAllAfternoons(double wp,int maxhours,const QString & st)42985 ConstraintStudentsSetMaxHoursPerAllAfternoons::ConstraintStudentsSetMaxHoursPerAllAfternoons(double wp, int maxhours, const QString& st)
42986  : TimeConstraint(wp)
42987  {
42988 	assert(maxhours>0);
42989 	this->maxHoursPerAllAfternoons=maxhours;
42990 	this->students=st;
42991 
42992 	this->type=CONSTRAINT_STUDENTS_SET_MAX_HOURS_PER_ALL_AFTERNOONS;
42993 }
42994 
computeInternalStructure(QWidget * parent,Rules & r)42995 bool ConstraintStudentsSetMaxHoursPerAllAfternoons::computeInternalStructure(QWidget* parent, Rules& r)
42996 {
42997 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
42998 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
42999 
43000 	if(ss==nullptr){
43001 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
43002 		 tr("Constraint students set max hours per all afternoons is wrong because it refers to inexistent students set."
43003 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
43004 
43005 		return false;
43006 	}
43007 
43008 	assert(ss!=nullptr);
43009 
43010 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
43011 	/*this->iSubgroupsList.clear();
43012 	if(ss->type==STUDENTS_SUBGROUP){
43013 		int tmp;
43014 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
43015 		assert(tmp>=0);
43016 		assert(tmp<r.nInternalSubgroups);
43017 		if(!this->iSubgroupsList.contains(tmp))
43018 			this->iSubgroupsList.append(tmp);
43019 	}
43020 	else if(ss->type==STUDENTS_GROUP){
43021 		StudentsGroup* stg=(StudentsGroup*)ss;
43022 		for(int i=0; i<stg->subgroupsList.size(); i++){
43023 			StudentsSubgroup* sts=stg->subgroupsList[i];
43024 			int tmp;
43025 			tmp=sts->indexInInternalSubgroupsList;
43026 			assert(tmp>=0);
43027 			assert(tmp<r.nInternalSubgroups);
43028 			if(!this->iSubgroupsList.contains(tmp))
43029 				this->iSubgroupsList.append(tmp);
43030 		}
43031 	}
43032 	else if(ss->type==STUDENTS_YEAR){
43033 		StudentsYear* sty=(StudentsYear*)ss;
43034 		for(int i=0; i<sty->groupsList.size(); i++){
43035 			StudentsGroup* stg=sty->groupsList[i];
43036 			for(int j=0; j<stg->subgroupsList.size(); j++){
43037 				StudentsSubgroup* sts=stg->subgroupsList[j];
43038 				int tmp;
43039 				tmp=sts->indexInInternalSubgroupsList;
43040 				assert(tmp>=0);
43041 				assert(tmp<r.nInternalSubgroups);
43042 				if(!this->iSubgroupsList.contains(tmp))
43043 					this->iSubgroupsList.append(tmp);
43044 			}
43045 		}
43046 	}
43047 	else
43048 		assert(0);*/
43049 
43050 	return true;
43051 }
43052 
hasInactiveActivities(Rules & r)43053 bool ConstraintStudentsSetMaxHoursPerAllAfternoons::hasInactiveActivities(Rules& r)
43054 {
43055 	Q_UNUSED(r);
43056 	return false;
43057 }
43058 
getXmlDescription(Rules & r)43059 QString ConstraintStudentsSetMaxHoursPerAllAfternoons::getXmlDescription(Rules& r){
43060 	Q_UNUSED(r);
43061 
43062 	QString s="<ConstraintStudentsSetMaxHoursPerAllAfternoons>\n";
43063 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
43064 	s+="	<Students>"+protect(this->students)+"</Students>\n";
43065 	s+="	<Maximum_Hours_Per_All_Afternoons>"+CustomFETString::number(this->maxHoursPerAllAfternoons)+"</Maximum_Hours_Per_All_Afternoons>\n";
43066 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
43067 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
43068 	s+="</ConstraintStudentsSetMaxHoursPerAllAfternoons>\n";
43069 	return s;
43070 }
43071 
getDescription(Rules & r)43072 QString ConstraintStudentsSetMaxHoursPerAllAfternoons::getDescription(Rules& r){
43073 	Q_UNUSED(r);
43074 
43075 	QString begin=QString("");
43076 	if(!active)
43077 		begin="X - ";
43078 
43079 	QString end=QString("");
43080 	if(!comments.isEmpty())
43081 		end=", "+tr("C: %1", "Comments").arg(comments);
43082 
43083 	QString s;
43084 	s+=tr("Students set max hours per all afternoons");s+=", ";
43085 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
43086 	s+=tr("St:%1", "Students").arg(this->students);s+=", ";
43087 	s+=tr("MHAA:%1", "Maximum hours per all afternoons").arg(this->maxHoursPerAllAfternoons);
43088 
43089 	return begin+s+end;
43090 }
43091 
getDetailedDescription(Rules & r)43092 QString ConstraintStudentsSetMaxHoursPerAllAfternoons::getDetailedDescription(Rules& r){
43093 	Q_UNUSED(r);
43094 
43095 	QString s=tr("Time constraint");s+="\n";
43096 	s+=tr("A students set must respect the maximum number of hours per all afternoons");s+="\n";
43097 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
43098 	s+=tr("Students=%1").arg(this->students);s+="\n";
43099 	s+=tr("Maximum hours per all afternoons=%1").arg(this->maxHoursPerAllAfternoons);s+="\n";
43100 
43101 	if(!active){
43102 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
43103 		s+="\n";
43104 	}
43105 	if(!comments.isEmpty()){
43106 		s+=tr("Comments=%1").arg(comments);
43107 		s+="\n";
43108 	}
43109 
43110 	return s;
43111 }
43112 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)43113 double ConstraintStudentsSetMaxHoursPerAllAfternoons::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
43114 {
43115 	Q_UNUSED(cl);
43116 	Q_UNUSED(dl);
43117 	Q_UNUSED(conflictsString);
43118 
43119 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
43120 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
43121 		c.teachersMatrixReady=true;
43122 		c.subgroupsMatrixReady=true;
43123 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
43124 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
43125 
43126 		c.changedForMatrixCalculation=false;
43127 	}
43128 
43129 	int nbroken=0;
43130 
43131 	for(int i : qAsConst(this->iSubgroupsList)){
43132 		int n_hours=0;
43133 		for(int d=1; d<r.nDaysPerWeek; d+=2) //afternoon
43134 			for(int h=0; h<r.nHoursPerDay; h++)
43135 				if(subgroupsMatrix[i][d][h]>0)
43136 					n_hours++;
43137 
43138 		if(n_hours>this->maxHoursPerAllAfternoons)
43139 			nbroken++;
43140 	}
43141 
43142 	assert(weightPercentage==100);
43143 	if(weightPercentage==100)
43144 		assert(nbroken==0);
43145 
43146 	return weightPercentage/100 * nbroken;
43147 }
43148 
isRelatedToActivity(Rules & r,Activity * a)43149 bool ConstraintStudentsSetMaxHoursPerAllAfternoons::isRelatedToActivity(Rules& r, Activity* a)
43150 {
43151 	Q_UNUSED(r);
43152 	Q_UNUSED(a);
43153 
43154 	return false;
43155 }
43156 
isRelatedToTeacher(Teacher * t)43157 bool ConstraintStudentsSetMaxHoursPerAllAfternoons::isRelatedToTeacher(Teacher* t)
43158 {
43159 	Q_UNUSED(t);
43160 
43161 	return false;
43162 }
43163 
isRelatedToSubject(Subject * s)43164 bool ConstraintStudentsSetMaxHoursPerAllAfternoons::isRelatedToSubject(Subject* s)
43165 {
43166 	Q_UNUSED(s);
43167 
43168 	return false;
43169 }
43170 
isRelatedToActivityTag(ActivityTag * s)43171 bool ConstraintStudentsSetMaxHoursPerAllAfternoons::isRelatedToActivityTag(ActivityTag* s)
43172 {
43173 	Q_UNUSED(s);
43174 
43175 	return false;
43176 }
43177 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)43178 bool ConstraintStudentsSetMaxHoursPerAllAfternoons::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
43179 {
43180 	return r.setsShareStudents(this->students, s->name);
43181 }
43182 
hasWrongDayOrHour(Rules & r)43183 bool ConstraintStudentsSetMaxHoursPerAllAfternoons::hasWrongDayOrHour(Rules& r)
43184 {
43185 	if(maxHoursPerAllAfternoons>r.nDaysPerWeek*r.nHoursPerDay/2)
43186 		return true;
43187 
43188 	return false;
43189 }
43190 
canRepairWrongDayOrHour(Rules & r)43191 bool ConstraintStudentsSetMaxHoursPerAllAfternoons::canRepairWrongDayOrHour(Rules& r)
43192 {
43193 	assert(hasWrongDayOrHour(r));
43194 
43195 	return true;
43196 }
43197 
repairWrongDayOrHour(Rules & r)43198 bool ConstraintStudentsSetMaxHoursPerAllAfternoons::repairWrongDayOrHour(Rules& r)
43199 {
43200 	assert(hasWrongDayOrHour(r));
43201 
43202 	if(maxHoursPerAllAfternoons>r.nDaysPerWeek*r.nHoursPerDay/2)
43203 		maxHoursPerAllAfternoons=r.nDaysPerWeek*r.nHoursPerDay/2;
43204 
43205 	return true;
43206 }
43207 
43208 ///////////////////////////////////////////////////////////////////////////////////////////
43209 ///////////////////////////////////////////////////////////////////////////////////////////
43210 
ConstraintStudentsMaxHoursPerAllAfternoons()43211 ConstraintStudentsMaxHoursPerAllAfternoons::ConstraintStudentsMaxHoursPerAllAfternoons()
43212 	: TimeConstraint()
43213 {
43214 	this->type=CONSTRAINT_STUDENTS_MAX_HOURS_PER_ALL_AFTERNOONS;
43215 }
43216 
ConstraintStudentsMaxHoursPerAllAfternoons(double wp,int maxhours)43217 ConstraintStudentsMaxHoursPerAllAfternoons::ConstraintStudentsMaxHoursPerAllAfternoons(double wp, int maxhours)
43218  : TimeConstraint(wp)
43219  {
43220 	assert(maxhours>0);
43221 	this->maxHoursPerAllAfternoons=maxhours;
43222 
43223 	this->type=CONSTRAINT_STUDENTS_MAX_HOURS_PER_ALL_AFTERNOONS;
43224 }
43225 
computeInternalStructure(QWidget * parent,Rules & r)43226 bool ConstraintStudentsMaxHoursPerAllAfternoons::computeInternalStructure(QWidget* parent, Rules& r)
43227 {
43228 	Q_UNUSED(parent);
43229 	Q_UNUSED(r);
43230 
43231 	return true;
43232 }
43233 
hasInactiveActivities(Rules & r)43234 bool ConstraintStudentsMaxHoursPerAllAfternoons::hasInactiveActivities(Rules& r)
43235 {
43236 	Q_UNUSED(r);
43237 	return false;
43238 }
43239 
getXmlDescription(Rules & r)43240 QString ConstraintStudentsMaxHoursPerAllAfternoons::getXmlDescription(Rules& r){
43241 	Q_UNUSED(r);
43242 
43243 	QString s="<ConstraintStudentsMaxHoursPerAllAfternoons>\n";
43244 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
43245 	s+="	<Maximum_Hours_Per_All_Afternoons>"+CustomFETString::number(this->maxHoursPerAllAfternoons)+"</Maximum_Hours_Per_All_Afternoons>\n";
43246 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
43247 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
43248 	s+="</ConstraintStudentsMaxHoursPerAllAfternoons>\n";
43249 	return s;
43250 }
43251 
getDescription(Rules & r)43252 QString ConstraintStudentsMaxHoursPerAllAfternoons::getDescription(Rules& r){
43253 	Q_UNUSED(r);
43254 
43255 	QString begin=QString("");
43256 	if(!active)
43257 		begin="X - ";
43258 
43259 	QString end=QString("");
43260 	if(!comments.isEmpty())
43261 		end=", "+tr("C: %1", "Comments").arg(comments);
43262 
43263 	QString s;
43264 	s+=tr("Students max hours per all afternoons");s+=", ";
43265 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
43266 	s+=tr("MHAA:%1", "Maximum hours per all afternoons").arg(this->maxHoursPerAllAfternoons);
43267 
43268 	return begin+s+end;
43269 }
43270 
getDetailedDescription(Rules & r)43271 QString ConstraintStudentsMaxHoursPerAllAfternoons::getDetailedDescription(Rules& r){
43272 	Q_UNUSED(r);
43273 
43274 	QString s=tr("Time constraint");s+="\n";
43275 	s+=tr("All students must respect the maximum number of hours per all afternoons");s+="\n";
43276 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
43277 	s+=tr("Maximum hours per all afternoons=%1").arg(this->maxHoursPerAllAfternoons);s+="\n";
43278 
43279 	if(!active){
43280 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
43281 		s+="\n";
43282 	}
43283 	if(!comments.isEmpty()){
43284 		s+=tr("Comments=%1").arg(comments);
43285 		s+="\n";
43286 	}
43287 
43288 	return s;
43289 }
43290 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)43291 double ConstraintStudentsMaxHoursPerAllAfternoons::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
43292 {
43293 	Q_UNUSED(cl);
43294 	Q_UNUSED(dl);
43295 	Q_UNUSED(conflictsString);
43296 
43297 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
43298 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
43299 		c.teachersMatrixReady=true;
43300 		c.subgroupsMatrixReady=true;
43301 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
43302 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
43303 
43304 		c.changedForMatrixCalculation=false;
43305 	}
43306 
43307 	int nbroken=0;
43308 
43309 	for(int i=0; i<r.nInternalSubgroups; i++){
43310 		int n_hours=0;
43311 		for(int d=1; d<r.nDaysPerWeek; d+=2) //afternoon
43312 			for(int h=0; h<r.nHoursPerDay; h++)
43313 				if(subgroupsMatrix[i][d][h]>0)
43314 					n_hours++;
43315 
43316 		if(n_hours>this->maxHoursPerAllAfternoons)
43317 			nbroken++;
43318 	}
43319 
43320 	assert(weightPercentage==100);
43321 	if(weightPercentage==100)
43322 		assert(nbroken==0);
43323 
43324 	return weightPercentage/100 * nbroken;
43325 }
43326 
isRelatedToActivity(Rules & r,Activity * a)43327 bool ConstraintStudentsMaxHoursPerAllAfternoons::isRelatedToActivity(Rules& r, Activity* a)
43328 {
43329 	Q_UNUSED(r);
43330 	Q_UNUSED(a);
43331 
43332 	return false;
43333 }
43334 
isRelatedToTeacher(Teacher * t)43335 bool ConstraintStudentsMaxHoursPerAllAfternoons::isRelatedToTeacher(Teacher* t)
43336 {
43337 	Q_UNUSED(t);
43338 
43339 	return false;
43340 }
43341 
isRelatedToSubject(Subject * s)43342 bool ConstraintStudentsMaxHoursPerAllAfternoons::isRelatedToSubject(Subject* s)
43343 {
43344 	Q_UNUSED(s);
43345 
43346 	return false;
43347 }
43348 
isRelatedToActivityTag(ActivityTag * s)43349 bool ConstraintStudentsMaxHoursPerAllAfternoons::isRelatedToActivityTag(ActivityTag* s)
43350 {
43351 	Q_UNUSED(s);
43352 
43353 	return false;
43354 }
43355 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)43356 bool ConstraintStudentsMaxHoursPerAllAfternoons::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
43357 {
43358 	Q_UNUSED(r);
43359 	Q_UNUSED(s);
43360 
43361 	return true;
43362 }
43363 
hasWrongDayOrHour(Rules & r)43364 bool ConstraintStudentsMaxHoursPerAllAfternoons::hasWrongDayOrHour(Rules& r)
43365 {
43366 	if(maxHoursPerAllAfternoons>r.nDaysPerWeek*r.nHoursPerDay/2)
43367 		return true;
43368 
43369 	return false;
43370 }
43371 
canRepairWrongDayOrHour(Rules & r)43372 bool ConstraintStudentsMaxHoursPerAllAfternoons::canRepairWrongDayOrHour(Rules& r)
43373 {
43374 	assert(hasWrongDayOrHour(r));
43375 
43376 	return true;
43377 }
43378 
repairWrongDayOrHour(Rules & r)43379 bool ConstraintStudentsMaxHoursPerAllAfternoons::repairWrongDayOrHour(Rules& r)
43380 {
43381 	assert(hasWrongDayOrHour(r));
43382 
43383 	if(maxHoursPerAllAfternoons>r.nDaysPerWeek*r.nHoursPerDay/2)
43384 		maxHoursPerAllAfternoons=r.nDaysPerWeek*r.nHoursPerDay/2;
43385 
43386 	return true;
43387 }
43388 
43389 ///////////////////////////////////////////////////////////////////////////////////////////
43390 ///////////////////////////////////////////////////////////////////////////////////////////
43391 
ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon()43392 ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon()
43393 	: TimeConstraint()
43394 {
43395 	this->type=CONSTRAINT_TEACHER_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON;
43396 	this->minRestingHours=-1;
43397 }
43398 
ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon(double wp,int minrestinghours,const QString & teacher)43399 ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon(double wp, int minrestinghours, const QString& teacher)
43400  : TimeConstraint(wp)
43401  {
43402 	assert(minrestinghours>0);
43403 	this->minRestingHours=minrestinghours;
43404 	this->teacherName=teacher;
43405 
43406 	this->type=CONSTRAINT_TEACHER_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON;
43407 }
43408 
computeInternalStructure(QWidget * parent,Rules & r)43409 bool ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::computeInternalStructure(QWidget* parent, Rules& r)
43410 {
43411 	Q_UNUSED(parent);
43412 
43413 	//this->teacher_ID=r.searchTeacher(this->teacherName);
43414 	teacher_ID=r.teachersHash.value(teacherName, -1);
43415 	assert(this->teacher_ID>=0);
43416 	return true;
43417 }
43418 
hasInactiveActivities(Rules & r)43419 bool ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::hasInactiveActivities(Rules& r)
43420 {
43421 	Q_UNUSED(r);
43422 	return false;
43423 }
43424 
getXmlDescription(Rules & r)43425 QString ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::getXmlDescription(Rules& r){
43426 	Q_UNUSED(r);
43427 
43428 	QString s="<ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon>\n";
43429 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
43430 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
43431 	s+="	<Minimum_Resting_Hours>"+CustomFETString::number(this->minRestingHours)+"</Minimum_Resting_Hours>\n";
43432 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
43433 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
43434 	s+="</ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon>\n";
43435 	return s;
43436 }
43437 
getDescription(Rules & r)43438 QString ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::getDescription(Rules& r){
43439 	Q_UNUSED(r);
43440 
43441 	QString begin=QString("");
43442 	if(!active)
43443 		begin="X - ";
43444 
43445 	QString end=QString("");
43446 	if(!comments.isEmpty())
43447 		end=", "+tr("C: %1", "Comments").arg(comments);
43448 
43449 	QString s;
43450 	s+=tr("Teacher min resting hours between morning and afternoon");s+=", ";
43451 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
43452 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
43453 	s+=tr("mRH:%1", "Minimum resting hours").arg(this->minRestingHours);
43454 
43455 	return begin+s+end;
43456 }
43457 
getDetailedDescription(Rules & r)43458 QString ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::getDetailedDescription(Rules& r){
43459 	Q_UNUSED(r);
43460 
43461 	QString s=tr("Time constraint");s+="\n";
43462 	s+=tr("A teacher must respect the minimum resting hours (between morning and afternoon)");s+="\n";
43463 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
43464 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
43465 	s+=tr("Minimum resting hours=%1").arg(this->minRestingHours);s+="\n";
43466 
43467 	if(!active){
43468 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
43469 		s+="\n";
43470 	}
43471 	if(!comments.isEmpty()){
43472 		s+=tr("Comments=%1").arg(comments);
43473 		s+="\n";
43474 	}
43475 
43476 	return s;
43477 }
43478 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)43479 double ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
43480 {
43481 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
43482 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
43483 		c.teachersMatrixReady=true;
43484 		c.subgroupsMatrixReady=true;
43485 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
43486 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
43487 
43488 		c.changedForMatrixCalculation=false;
43489 	}
43490 
43491 	Q_UNUSED(cl);
43492 	Q_UNUSED(dl);
43493 	Q_UNUSED(conflictsString);
43494 
43495 	assert(this->weightPercentage==100.0);
43496 
43497 	int nbroken=0;
43498 
43499 	for(int d=0; d<r.nDaysPerWeek; d+=2){ //morning
43500 		int cnt=0;
43501 		for(int h=r.nHoursPerDay-1; h>=0; h--){
43502 			if(teachersMatrix[this->teacher_ID][d][h]>0) //morning
43503 				break;
43504 			else
43505 				cnt++;
43506 		}
43507 		for(int h=0; h<r.nHoursPerDay; h++){
43508 			if(teachersMatrix[this->teacher_ID][d+1][h]>0) //afternoon
43509 				break;
43510 			else
43511 				cnt++;
43512 		}
43513 		if(cnt < this->minRestingHours)
43514 			nbroken++;
43515 	}
43516 
43517 	assert(nbroken==0);
43518 
43519 	return nbroken;
43520 }
43521 
isRelatedToActivity(Rules & r,Activity * a)43522 bool ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::isRelatedToActivity(Rules& r, Activity* a)
43523 {
43524 	Q_UNUSED(r);
43525 	Q_UNUSED(a);
43526 
43527 	return false;
43528 }
43529 
isRelatedToTeacher(Teacher * t)43530 bool ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::isRelatedToTeacher(Teacher* t)
43531 {
43532 	if(this->teacherName==t->name)
43533 		return true;
43534 	return false;
43535 }
43536 
isRelatedToSubject(Subject * s)43537 bool ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::isRelatedToSubject(Subject* s)
43538 {
43539 	Q_UNUSED(s);
43540 
43541 	return false;
43542 }
43543 
isRelatedToActivityTag(ActivityTag * s)43544 bool ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::isRelatedToActivityTag(ActivityTag* s)
43545 {
43546 	Q_UNUSED(s);
43547 
43548 	return false;
43549 }
43550 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)43551 bool ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
43552 {
43553 	Q_UNUSED(r);
43554 	Q_UNUSED(s);
43555 
43556 	return false;
43557 }
43558 
hasWrongDayOrHour(Rules & r)43559 bool ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::hasWrongDayOrHour(Rules& r)
43560 {
43561 	if(minRestingHours>2*r.nHoursPerDay)
43562 		return true;
43563 
43564 	return false;
43565 }
43566 
canRepairWrongDayOrHour(Rules & r)43567 bool ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::canRepairWrongDayOrHour(Rules& r)
43568 {
43569 	assert(hasWrongDayOrHour(r));
43570 
43571 	return true;
43572 }
43573 
repairWrongDayOrHour(Rules & r)43574 bool ConstraintTeacherMinRestingHoursBetweenMorningAndAfternoon::repairWrongDayOrHour(Rules& r)
43575 {
43576 	assert(hasWrongDayOrHour(r));
43577 
43578 	if(minRestingHours>2*r.nHoursPerDay)
43579 		minRestingHours=2*r.nHoursPerDay;
43580 
43581 	return true;
43582 }
43583 
43584 ///////////////////////////////////////////////////////////////////////////////////////////
43585 ///////////////////////////////////////////////////////////////////////////////////////////
43586 
ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon()43587 ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon()
43588 	: TimeConstraint()
43589 {
43590 	this->type=CONSTRAINT_TEACHERS_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON;
43591 	this->minRestingHours=-1;
43592 }
43593 
ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon(double wp,int minrestinghours)43594 ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon(double wp, int minrestinghours)
43595  : TimeConstraint(wp)
43596  {
43597 	assert(minrestinghours>0);
43598 	this->minRestingHours=minrestinghours;
43599 
43600 	this->type=CONSTRAINT_TEACHERS_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON;
43601 }
43602 
computeInternalStructure(QWidget * parent,Rules & r)43603 bool ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::computeInternalStructure(QWidget* parent, Rules& r)
43604 {
43605 	Q_UNUSED(parent);
43606 	Q_UNUSED(r);
43607 
43608 	return true;
43609 }
43610 
hasInactiveActivities(Rules & r)43611 bool ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::hasInactiveActivities(Rules& r)
43612 {
43613 	Q_UNUSED(r);
43614 	return false;
43615 }
43616 
getXmlDescription(Rules & r)43617 QString ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::getXmlDescription(Rules& r){
43618 	Q_UNUSED(r);
43619 
43620 	QString s="<ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon>\n";
43621 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
43622 	s+="	<Minimum_Resting_Hours>"+CustomFETString::number(this->minRestingHours)+"</Minimum_Resting_Hours>\n";
43623 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
43624 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
43625 	s+="</ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon>\n";
43626 	return s;
43627 }
43628 
getDescription(Rules & r)43629 QString ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::getDescription(Rules& r){
43630 	Q_UNUSED(r);
43631 
43632 	QString begin=QString("");
43633 	if(!active)
43634 		begin="X - ";
43635 
43636 	QString end=QString("");
43637 	if(!comments.isEmpty())
43638 		end=", "+tr("C: %1", "Comments").arg(comments);
43639 
43640 	QString s;
43641 	s+=tr("Teachers min resting hours between morning and afternoon");s+=", ";
43642 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
43643 	s+=tr("mRH:%1", "Minimum resting hours").arg(this->minRestingHours);
43644 
43645 	return begin+s+end;
43646 }
43647 
getDetailedDescription(Rules & r)43648 QString ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::getDetailedDescription(Rules& r){
43649 	Q_UNUSED(r);
43650 
43651 	QString s=tr("Time constraint");s+="\n";
43652 	s+=tr("All teachers must respect the minimum resting hours (between morning and afternoon)");s+="\n";
43653 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
43654 	s+=tr("Minimum resting hours=%1").arg(this->minRestingHours);s+="\n";
43655 
43656 	if(!active){
43657 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
43658 		s+="\n";
43659 	}
43660 	if(!comments.isEmpty()){
43661 		s+=tr("Comments=%1").arg(comments);
43662 		s+="\n";
43663 	}
43664 
43665 	return s;
43666 }
43667 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)43668 double ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
43669 {
43670 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
43671 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
43672 		c.teachersMatrixReady=true;
43673 		c.subgroupsMatrixReady=true;
43674 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
43675 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
43676 
43677 		c.changedForMatrixCalculation=false;
43678 	}
43679 
43680 	Q_UNUSED(cl);
43681 	Q_UNUSED(dl);
43682 	Q_UNUSED(conflictsString);
43683 
43684 	assert(this->weightPercentage==100.0);
43685 
43686 	int nbroken=0;
43687 
43688 	for(int tch=0; tch<r.nInternalTeachers; tch++){
43689 		for(int d=0; d<r.nDaysPerWeek; d+=2){ //morning
43690 			int cnt=0;
43691 			for(int h=r.nHoursPerDay-1; h>=0; h--){
43692 				if(teachersMatrix[tch][d][h]>0) //morning
43693 					break;
43694 				else
43695 					cnt++;
43696 			}
43697 			for(int h=0; h<r.nHoursPerDay; h++){
43698 				if(teachersMatrix[tch][d+1][h]>0) //afternoon
43699 					break;
43700 				else
43701 					cnt++;
43702 			}
43703 			if(cnt < this->minRestingHours)
43704 				nbroken++;
43705 		}
43706 	}
43707 
43708 	assert(nbroken==0);
43709 
43710 	return nbroken;
43711 }
43712 
isRelatedToActivity(Rules & r,Activity * a)43713 bool ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::isRelatedToActivity(Rules& r, Activity* a)
43714 {
43715 	Q_UNUSED(r);
43716 	Q_UNUSED(a);
43717 
43718 	return false;
43719 }
43720 
isRelatedToTeacher(Teacher * t)43721 bool ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::isRelatedToTeacher(Teacher* t)
43722 {
43723 	Q_UNUSED(t);
43724 
43725 	return true;
43726 }
43727 
isRelatedToSubject(Subject * s)43728 bool ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::isRelatedToSubject(Subject* s)
43729 {
43730 	Q_UNUSED(s);
43731 
43732 	return false;
43733 }
43734 
isRelatedToActivityTag(ActivityTag * s)43735 bool ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::isRelatedToActivityTag(ActivityTag* s)
43736 {
43737 	Q_UNUSED(s);
43738 
43739 	return false;
43740 }
43741 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)43742 bool ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
43743 {
43744 	Q_UNUSED(r);
43745 	Q_UNUSED(s);
43746 
43747 	return false;
43748 }
43749 
hasWrongDayOrHour(Rules & r)43750 bool ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::hasWrongDayOrHour(Rules& r)
43751 {
43752 	if(minRestingHours>2*r.nHoursPerDay)
43753 		return true;
43754 
43755 	return false;
43756 }
43757 
canRepairWrongDayOrHour(Rules & r)43758 bool ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::canRepairWrongDayOrHour(Rules& r)
43759 {
43760 	assert(hasWrongDayOrHour(r));
43761 
43762 	return true;
43763 }
43764 
repairWrongDayOrHour(Rules & r)43765 bool ConstraintTeachersMinRestingHoursBetweenMorningAndAfternoon::repairWrongDayOrHour(Rules& r)
43766 {
43767 	assert(hasWrongDayOrHour(r));
43768 
43769 	if(minRestingHours>2*r.nHoursPerDay)
43770 		minRestingHours=2*r.nHoursPerDay;
43771 
43772 	return true;
43773 }
43774 
43775 ////////////////////////////////////////////////////////////////////////////////////////////
43776 ////////////////////////////////////////////////////////////////////////////////////////////
43777 
ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon()43778 ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon()
43779 	: TimeConstraint()
43780 {
43781 	this->type = CONSTRAINT_STUDENTS_SET_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON;
43782 	this->minRestingHours = -1;
43783 }
43784 
ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon(double wp,int minrestinghours,const QString & sn)43785 ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon(double wp, int minrestinghours, const QString& sn)
43786 	: TimeConstraint(wp)
43787 {
43788 	this->minRestingHours = minrestinghours;
43789 	this->students = sn;
43790 	this->type = CONSTRAINT_STUDENTS_SET_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON;
43791 }
43792 
hasInactiveActivities(Rules & r)43793 bool ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::hasInactiveActivities(Rules& r)
43794 {
43795 	Q_UNUSED(r);
43796 	return false;
43797 }
43798 
getXmlDescription(Rules & r)43799 QString ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::getXmlDescription(Rules& r)
43800 {
43801 	Q_UNUSED(r);
43802 
43803 	QString s="<ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon>\n";
43804 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
43805 	s+="	<Minimum_Resting_Hours>"+CustomFETString::number(this->minRestingHours)+"</Minimum_Resting_Hours>\n";
43806 	s+="	<Students>"+protect(this->students)+"</Students>\n";
43807 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
43808 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
43809 	s+="</ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon>\n";
43810 	return s;
43811 }
43812 
getDescription(Rules & r)43813 QString ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::getDescription(Rules& r)
43814 {
43815 	Q_UNUSED(r);
43816 
43817 	QString begin=QString("");
43818 	if(!active)
43819 		begin="X - ";
43820 
43821 	QString end=QString("");
43822 	if(!comments.isEmpty())
43823 		end=", "+tr("C: %1", "Comments").arg(comments);
43824 
43825 	QString s;
43826 	s+=tr("Students set min resting hours between morning and afternoon");s+=", ";
43827 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
43828 	s+=tr("St:%1", "Students (set)").arg(this->students); s+=", ";
43829 	s+=tr("mRH:%1", "Minimum resting hours").arg(this->minRestingHours);
43830 
43831 	return begin+s+end;
43832 }
43833 
getDetailedDescription(Rules & r)43834 QString ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::getDetailedDescription(Rules& r)
43835 {
43836 	Q_UNUSED(r);
43837 
43838 	QString s=tr("Time constraint");s+="\n";
43839 	s+=tr("A students set must respect the minimum resting hours (between morning and afternoon)");s+="\n";
43840 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
43841 	s+=tr("Students set=%1").arg(this->students);s+="\n";
43842 	s+=tr("Minimum resting hours=%1").arg(this->minRestingHours);s+="\n";
43843 
43844 	if(!active){
43845 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
43846 		s+="\n";
43847 	}
43848 	if(!comments.isEmpty()){
43849 		s+=tr("Comments=%1").arg(comments);
43850 		s+="\n";
43851 	}
43852 
43853 	return s;
43854 }
43855 
computeInternalStructure(QWidget * parent,Rules & r)43856 bool ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::computeInternalStructure(QWidget* parent, Rules& r)
43857 {
43858 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
43859 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
43860 
43861 	if(ss==nullptr){
43862 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
43863 		 tr("Constraint students set min resting hours between morning and afternoon is wrong because it refers to inexistent students set."
43864 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
43865 
43866 		return false;
43867 	}
43868 
43869 	assert(ss!=nullptr);
43870 
43871 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
43872 	/*this->iSubgroupsList.clear();
43873 	if(ss->type==STUDENTS_SUBGROUP){
43874 		int tmp;
43875 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
43876 		assert(tmp>=0);
43877 		assert(tmp<r.nInternalSubgroups);
43878 		if(!this->iSubgroupsList.contains(tmp))
43879 			this->iSubgroupsList.append(tmp);
43880 	}
43881 	else if(ss->type==STUDENTS_GROUP){
43882 		StudentsGroup* stg=(StudentsGroup*)ss;
43883 		for(int i=0; i<stg->subgroupsList.size(); i++){
43884 			StudentsSubgroup* sts=stg->subgroupsList[i];
43885 			int tmp;
43886 			tmp=sts->indexInInternalSubgroupsList;
43887 			assert(tmp>=0);
43888 			assert(tmp<r.nInternalSubgroups);
43889 			if(!this->iSubgroupsList.contains(tmp))
43890 				this->iSubgroupsList.append(tmp);
43891 		}
43892 	}
43893 	else if(ss->type==STUDENTS_YEAR){
43894 		StudentsYear* sty=(StudentsYear*)ss;
43895 		for(int i=0; i<sty->groupsList.size(); i++){
43896 			StudentsGroup* stg=sty->groupsList[i];
43897 			for(int j=0; j<stg->subgroupsList.size(); j++){
43898 				StudentsSubgroup* sts=stg->subgroupsList[j];
43899 				int tmp;
43900 				tmp=sts->indexInInternalSubgroupsList;
43901 				assert(tmp>=0);
43902 				assert(tmp<r.nInternalSubgroups);
43903 				if(!this->iSubgroupsList.contains(tmp))
43904 					this->iSubgroupsList.append(tmp);
43905 			}
43906 		}
43907 	}
43908 	else
43909 		assert(0);*/
43910 
43911 	return true;
43912 }
43913 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)43914 double ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
43915 {
43916 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
43917 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
43918 		c.teachersMatrixReady=true;
43919 		c.subgroupsMatrixReady=true;
43920 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
43921 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
43922 
43923 		c.changedForMatrixCalculation=false;
43924 	}
43925 
43926 	Q_UNUSED(cl);
43927 	Q_UNUSED(dl);
43928 	Q_UNUSED(conflictsString);
43929 
43930 	assert(this->weightPercentage==100.0);
43931 
43932 	int nbroken=0;
43933 
43934 	for(int sbg : qAsConst(this->iSubgroupsList)){
43935 		for(int d=0; d<r.nDaysPerWeek; d+=2){ //morning
43936 			int cnt=0;
43937 			for(int h=r.nHoursPerDay-1; h>=0; h--){
43938 				if(subgroupsMatrix[sbg][d][h]>0) //morning
43939 					break;
43940 				else
43941 					cnt++;
43942 			}
43943 			for(int h=0; h<r.nHoursPerDay; h++){
43944 				if(subgroupsMatrix[sbg][d+1][h]>0) //afternoon
43945 					break;
43946 				else
43947 					cnt++;
43948 			}
43949 			if(cnt < this->minRestingHours)
43950 				nbroken++;
43951 		}
43952 	}
43953 
43954 	assert(nbroken==0);
43955 
43956 	return nbroken;
43957 }
43958 
isRelatedToActivity(Rules & r,Activity * a)43959 bool ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::isRelatedToActivity(Rules& r, Activity* a)
43960 {
43961 	Q_UNUSED(r);
43962 	Q_UNUSED(a);
43963 
43964 	return false;
43965 }
43966 
isRelatedToTeacher(Teacher * t)43967 bool ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::isRelatedToTeacher(Teacher* t)
43968 {
43969 	Q_UNUSED(t);
43970 
43971 	return false;
43972 }
43973 
isRelatedToSubject(Subject * s)43974 bool ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::isRelatedToSubject(Subject* s)
43975 {
43976 	Q_UNUSED(s);
43977 
43978 	return false;
43979 }
43980 
isRelatedToActivityTag(ActivityTag * s)43981 bool ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::isRelatedToActivityTag(ActivityTag* s)
43982 {
43983 	Q_UNUSED(s);
43984 
43985 	return false;
43986 }
43987 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)43988 bool ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
43989 {
43990 	return r.setsShareStudents(this->students, s->name);
43991 }
43992 
hasWrongDayOrHour(Rules & r)43993 bool ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::hasWrongDayOrHour(Rules& r)
43994 {
43995 	if(minRestingHours>2*r.nHoursPerDay)
43996 		return true;
43997 
43998 	return false;
43999 }
44000 
canRepairWrongDayOrHour(Rules & r)44001 bool ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::canRepairWrongDayOrHour(Rules& r)
44002 {
44003 	assert(hasWrongDayOrHour(r));
44004 
44005 	return true;
44006 }
44007 
repairWrongDayOrHour(Rules & r)44008 bool ConstraintStudentsSetMinRestingHoursBetweenMorningAndAfternoon::repairWrongDayOrHour(Rules& r)
44009 {
44010 	assert(hasWrongDayOrHour(r));
44011 
44012 	if(minRestingHours>2*r.nHoursPerDay)
44013 		minRestingHours=2*r.nHoursPerDay;
44014 
44015 	return true;
44016 }
44017 
44018 ////////////////////////////////////////////////////////////////////////////////////////////
44019 ////////////////////////////////////////////////////////////////////////////////////////////
44020 
ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon()44021 ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon()
44022 	: TimeConstraint()
44023 {
44024 	this->type = CONSTRAINT_STUDENTS_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON;
44025 	this->minRestingHours = -1;
44026 }
44027 
ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon(double wp,int minrestinghours)44028 ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon(double wp, int minrestinghours)
44029 	: TimeConstraint(wp)
44030 {
44031 	this->minRestingHours = minrestinghours;
44032 	this->type = CONSTRAINT_STUDENTS_MIN_RESTING_HOURS_BETWEEN_MORNING_AND_AFTERNOON;
44033 }
44034 
hasInactiveActivities(Rules & r)44035 bool ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::hasInactiveActivities(Rules& r)
44036 {
44037 	Q_UNUSED(r);
44038 	return false;
44039 }
44040 
getXmlDescription(Rules & r)44041 QString ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::getXmlDescription(Rules& r)
44042 {
44043 	Q_UNUSED(r);
44044 
44045 	QString s="<ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon>\n";
44046 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
44047 	s+="	<Minimum_Resting_Hours>"+CustomFETString::number(this->minRestingHours)+"</Minimum_Resting_Hours>\n";
44048 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
44049 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
44050 	s+="</ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon>\n";
44051 	return s;
44052 }
44053 
getDescription(Rules & r)44054 QString ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::getDescription(Rules& r)
44055 {
44056 	Q_UNUSED(r);
44057 
44058 	QString begin=QString("");
44059 	if(!active)
44060 		begin="X - ";
44061 
44062 	QString end=QString("");
44063 	if(!comments.isEmpty())
44064 		end=", "+tr("C: %1", "Comments").arg(comments);
44065 
44066 	QString s;
44067 	s+=tr("Students min resting hours between morning and afternoon");s+=", ";
44068 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
44069 	s+=tr("mRH:%1", "Minimum resting hours").arg(this->minRestingHours);
44070 
44071 	return begin+s+end;
44072 }
44073 
getDetailedDescription(Rules & r)44074 QString ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::getDetailedDescription(Rules& r)
44075 {
44076 	Q_UNUSED(r);
44077 
44078 	QString s=tr("Time constraint");s+="\n";
44079 	s+=tr("All students must respect the minimum resting hours (between morning and afternoon)");s+="\n";
44080 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
44081 	s+=tr("Minimum resting hours=%1").arg(this->minRestingHours);s+="\n";
44082 
44083 	if(!active){
44084 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
44085 		s+="\n";
44086 	}
44087 	if(!comments.isEmpty()){
44088 		s+=tr("Comments=%1").arg(comments);
44089 		s+="\n";
44090 	}
44091 
44092 	return s;
44093 }
44094 
computeInternalStructure(QWidget * parent,Rules & r)44095 bool ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::computeInternalStructure(QWidget* parent, Rules& r)
44096 {
44097 	Q_UNUSED(parent);
44098 	Q_UNUSED(r);
44099 
44100 	return true;
44101 }
44102 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)44103 double ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
44104 {
44105 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
44106 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
44107 		c.teachersMatrixReady=true;
44108 		c.subgroupsMatrixReady=true;
44109 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
44110 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
44111 
44112 		c.changedForMatrixCalculation=false;
44113 	}
44114 
44115 	Q_UNUSED(cl);
44116 	Q_UNUSED(dl);
44117 	Q_UNUSED(conflictsString);
44118 
44119 	assert(this->weightPercentage==100.0);
44120 
44121 	int nbroken=0;
44122 
44123 	for(int sbg=0; sbg<r.nInternalSubgroups; sbg++){
44124 		for(int d=0; d<r.nDaysPerWeek; d+=2){ //morning
44125 			int cnt=0;
44126 			for(int h=r.nHoursPerDay-1; h>=0; h--){
44127 				if(subgroupsMatrix[sbg][d][h]>0) //morning
44128 					break;
44129 				else
44130 					cnt++;
44131 			}
44132 			for(int h=0; h<r.nHoursPerDay; h++){
44133 				if(subgroupsMatrix[sbg][d+1][h]>0) //afternoon
44134 					break;
44135 				else
44136 					cnt++;
44137 			}
44138 			if(cnt < this->minRestingHours)
44139 				nbroken++;
44140 		}
44141 	}
44142 
44143 	assert(nbroken==0);
44144 
44145 	return nbroken;
44146 }
44147 
isRelatedToActivity(Rules & r,Activity * a)44148 bool ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::isRelatedToActivity(Rules& r, Activity* a)
44149 {
44150 	Q_UNUSED(r);
44151 	Q_UNUSED(a);
44152 
44153 	return false;
44154 }
44155 
isRelatedToTeacher(Teacher * t)44156 bool ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::isRelatedToTeacher(Teacher* t)
44157 {
44158 	Q_UNUSED(t);
44159 
44160 	return false;
44161 }
44162 
isRelatedToSubject(Subject * s)44163 bool ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::isRelatedToSubject(Subject* s)
44164 {
44165 	Q_UNUSED(s);
44166 
44167 	return false;
44168 }
44169 
isRelatedToActivityTag(ActivityTag * s)44170 bool ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::isRelatedToActivityTag(ActivityTag* s)
44171 {
44172 	Q_UNUSED(s);
44173 
44174 	return false;
44175 }
44176 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)44177 bool ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
44178 {
44179 	Q_UNUSED(r);
44180 	Q_UNUSED(s);
44181 
44182 	return true;
44183 }
44184 
hasWrongDayOrHour(Rules & r)44185 bool ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::hasWrongDayOrHour(Rules& r)
44186 {
44187 	if(minRestingHours>2*r.nHoursPerDay)
44188 		return true;
44189 
44190 	return false;
44191 }
44192 
canRepairWrongDayOrHour(Rules & r)44193 bool ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::canRepairWrongDayOrHour(Rules& r)
44194 {
44195 	assert(hasWrongDayOrHour(r));
44196 
44197 	return true;
44198 }
44199 
repairWrongDayOrHour(Rules & r)44200 bool ConstraintStudentsMinRestingHoursBetweenMorningAndAfternoon::repairWrongDayOrHour(Rules& r)
44201 {
44202 	assert(hasWrongDayOrHour(r));
44203 
44204 	if(minRestingHours>2*r.nHoursPerDay)
44205 		minRestingHours=2*r.nHoursPerDay;
44206 
44207 	return true;
44208 }
44209 
44210 ////////////////////////////////////////////////////////////////////////////////////////////
44211 ////////////////////////////////////////////////////////////////////////////////////////////
44212 
ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour()44213 ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour()
44214 	: TimeConstraint()
44215 {
44216 	this->type = CONSTRAINT_STUDENTS_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
44217 }
44218 
ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour(double wp,int mBSH)44219 ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour(double wp, int mBSH)
44220 	: TimeConstraint(wp)
44221 {
44222 	this->type = CONSTRAINT_STUDENTS_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
44223 	this->maxBeginningsAtSecondHour=mBSH;
44224 }
44225 
computeInternalStructure(QWidget * parent,Rules & r)44226 bool ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
44227 {
44228 	Q_UNUSED(parent);
44229 	Q_UNUSED(r);
44230 
44231 	return true;
44232 }
44233 
hasInactiveActivities(Rules & r)44234 bool ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
44235 {
44236 	Q_UNUSED(r);
44237 	return false;
44238 }
44239 
getXmlDescription(Rules & r)44240 QString ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
44241 {
44242 	Q_UNUSED(r);
44243 
44244 	QString s="<ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour>\n";
44245 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
44246 	s+="	<Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
44247 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
44248 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
44249 	s+="</ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour>\n";
44250 	return s;
44251 }
44252 
getDescription(Rules & r)44253 QString ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
44254 {
44255 	Q_UNUSED(r);
44256 
44257 	QString begin=QString("");
44258 	if(!active)
44259 		begin="X - ";
44260 
44261 	QString end=QString("");
44262 	if(!comments.isEmpty())
44263 		end=", "+tr("C: %1", "Comments").arg(comments);
44264 
44265 	QString s;
44266 	s+=tr("Students must begin afternoons early, respecting maximum %1 arrivals at second hour")
44267 	 .arg(this->maxBeginningsAtSecondHour);
44268 	s+=", ";
44269 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
44270 
44271 	return begin+s+end;
44272 }
44273 
getDetailedDescription(Rules & r)44274 QString ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
44275 {
44276 	Q_UNUSED(r);
44277 
44278 	QString s=tr("Time constraint");s+="\n";
44279 	s+=tr("All students must begin the afternoons early, respecting maximum %1 later arrivals, at second hour")
44280 	 .arg(this->maxBeginningsAtSecondHour);s+="\n";
44281 	s+=tr("(breaks and students set not available not counted)");s+="\n";
44282 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
44283 
44284 	if(!active){
44285 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
44286 		s+="\n";
44287 	}
44288 	if(!comments.isEmpty()){
44289 		s+=tr("Comments=%1").arg(comments);
44290 		s+="\n";
44291 	}
44292 
44293 	return s;
44294 }
44295 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)44296 double ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
44297 {
44298 	//considers the condition that the hours of subgroups begin as early as possible
44299 
44300 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
44301 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
44302 		c.teachersMatrixReady=true;
44303 		c.subgroupsMatrixReady=true;
44304 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
44305 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
44306 
44307 		c.changedForMatrixCalculation=false;
44308 	}
44309 
44310 	int conflTotal=0;
44311 
44312 	for(int i=0; i<r.nInternalSubgroups; i++){
44313 		int nGapsFirstHour=0;
44314 		for(int j=0; j<r.nDaysPerWeek; j++){
44315 			if(j%2==0)
44316 				continue;
44317 
44318 			int k;
44319 			for(k=0; k<r.nHoursPerDay; k++)
44320 				if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k])
44321 					break;
44322 
44323 			bool firstHourOccupied=false;
44324 			if(k<r.nHoursPerDay)
44325 				if(subgroupsMatrix[i][j][k]>0)
44326 					firstHourOccupied=true;
44327 
44328 			bool dayOccupied=firstHourOccupied;
44329 
44330 			bool illegalGap=false;
44331 
44332 			if(!dayOccupied){
44333 				for(k++; k<r.nHoursPerDay; k++){
44334 					if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
44335 						if(subgroupsMatrix[i][j][k]>0){
44336 							dayOccupied=true;
44337 							break;
44338 						}
44339 						else{
44340 							illegalGap=true;
44341 						}
44342 					}
44343 				}
44344 			}
44345 
44346 			if(dayOccupied && illegalGap){
44347 				if(conflictsString!=nullptr){
44348 					QString s=tr("Constraint students afternoons early max %1 beginnings at second hour broken for subgroup %2, on day %3,"
44349 					 " because students have an illegal gap, increases conflicts total by %4")
44350 					 .arg(this->maxBeginningsAtSecondHour)
44351 					 .arg(r.internalSubgroupsList[i]->name)
44352 					 .arg(r.daysOfTheWeek[j])
44353 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
44354 
44355 					dl.append(s);
44356 					cl.append(1*weightPercentage/100);
44357 
44358 					*conflictsString+= s+"\n";
44359 
44360 					conflTotal+=1;
44361 				}
44362 
44363 				if(c.nPlacedActivities==r.nInternalActivities){
44364 					assert(0);
44365 				}
44366 			}
44367 
44368 			if(dayOccupied && !firstHourOccupied)
44369 				nGapsFirstHour++;
44370 		}
44371 
44372 		if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
44373 			if(conflictsString!=nullptr){
44374 				QString s=tr("Constraint students afternoons early max %1 beginnings at second hour broken for subgroup %2,"
44375 				 " because students have too many arrivals at second hour, increases conflicts total by %3")
44376 				 .arg(this->maxBeginningsAtSecondHour)
44377 				 .arg(r.internalSubgroupsList[i]->name)
44378 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
44379 
44380 				dl.append(s);
44381 				cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
44382 
44383 				*conflictsString+= s+"\n";
44384 
44385 				conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
44386 			}
44387 
44388 			if(c.nPlacedActivities==r.nInternalActivities){
44389 				assert(0);
44390 			}
44391 		}
44392 	}
44393 
44394 	if(c.nPlacedActivities==r.nInternalActivities)
44395 		if(weightPercentage==100)    //might be broken for partial solutions
44396 			assert(conflTotal==0);
44397 	return weightPercentage/100 * conflTotal;
44398 }
44399 
isRelatedToActivity(Rules & r,Activity * a)44400 bool ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
44401 {
44402 	Q_UNUSED(r);
44403 	Q_UNUSED(a);
44404 
44405 	return false;
44406 }
44407 
isRelatedToTeacher(Teacher * t)44408 bool ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
44409 {
44410 	Q_UNUSED(t);
44411 
44412 	return false;
44413 }
44414 
isRelatedToSubject(Subject * s)44415 bool ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
44416 {
44417 	Q_UNUSED(s);
44418 
44419 	return false;
44420 }
44421 
isRelatedToActivityTag(ActivityTag * s)44422 bool ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
44423 {
44424 	Q_UNUSED(s);
44425 
44426 	return false;
44427 }
44428 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)44429 bool ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
44430 {
44431 	Q_UNUSED(r);
44432 	Q_UNUSED(s);
44433 
44434 	return true;
44435 }
44436 
hasWrongDayOrHour(Rules & r)44437 bool ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
44438 {
44439 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
44440 		return true;
44441 
44442 	return false;
44443 }
44444 
canRepairWrongDayOrHour(Rules & r)44445 bool ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
44446 {
44447 	assert(hasWrongDayOrHour(r));
44448 
44449 	return true;
44450 }
44451 
repairWrongDayOrHour(Rules & r)44452 bool ConstraintStudentsAfternoonsEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
44453 {
44454 	assert(hasWrongDayOrHour(r));
44455 
44456 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
44457 		maxBeginningsAtSecondHour=r.nDaysPerWeek/2;
44458 
44459 	return true;
44460 }
44461 
44462 ////////////////////////////////////////////////////////////////////////////////////////////
44463 ////////////////////////////////////////////////////////////////////////////////////////////
44464 
ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour()44465 ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour()
44466 	: TimeConstraint()
44467 {
44468 	this->type = CONSTRAINT_STUDENTS_SET_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
44469 }
44470 
ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour(double wp,int mBSH,const QString & students)44471 ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour(double wp, int mBSH, const QString& students)
44472 	: TimeConstraint(wp)
44473 {
44474 	this->type = CONSTRAINT_STUDENTS_SET_AFTERNOONS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
44475 	this->students=students;
44476 	this->maxBeginningsAtSecondHour=mBSH;
44477 }
44478 
computeInternalStructure(QWidget * parent,Rules & r)44479 bool ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
44480 {
44481 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
44482 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
44483 
44484 	if(ss==nullptr){
44485 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
44486 		 tr("Constraint students set afternoons early is wrong because it refers to inexistent students set."
44487 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
44488 
44489 		return false;
44490 	}
44491 
44492 	assert(ss!=nullptr);
44493 
44494 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
44495 	/*this->iSubgroupsList.clear();
44496 	if(ss->type==STUDENTS_SUBGROUP){
44497 		int tmp;
44498 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
44499 		assert(tmp>=0);
44500 		assert(tmp<r.nInternalSubgroups);
44501 		if(!this->iSubgroupsList.contains(tmp))
44502 			this->iSubgroupsList.append(tmp);
44503 	}
44504 	else if(ss->type==STUDENTS_GROUP){
44505 		StudentsGroup* stg=(StudentsGroup*)ss;
44506 		for(int i=0; i<stg->subgroupsList.size(); i++){
44507 			StudentsSubgroup* sts=stg->subgroupsList[i];
44508 			int tmp;
44509 			tmp=sts->indexInInternalSubgroupsList;
44510 			assert(tmp>=0);
44511 			assert(tmp<r.nInternalSubgroups);
44512 			if(!this->iSubgroupsList.contains(tmp))
44513 				this->iSubgroupsList.append(tmp);
44514 		}
44515 	}
44516 	else if(ss->type==STUDENTS_YEAR){
44517 		StudentsYear* sty=(StudentsYear*)ss;
44518 		for(int i=0; i<sty->groupsList.size(); i++){
44519 			StudentsGroup* stg=sty->groupsList[i];
44520 			for(int j=0; j<stg->subgroupsList.size(); j++){
44521 				StudentsSubgroup* sts=stg->subgroupsList[j];
44522 				int tmp;
44523 				tmp=sts->indexInInternalSubgroupsList;
44524 				assert(tmp>=0);
44525 				assert(tmp<r.nInternalSubgroups);
44526 				if(!this->iSubgroupsList.contains(tmp))
44527 					this->iSubgroupsList.append(tmp);
44528 			}
44529 		}
44530 	}
44531 	else
44532 		assert(0);*/
44533 	return true;
44534 }
44535 
hasInactiveActivities(Rules & r)44536 bool ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
44537 {
44538 	Q_UNUSED(r);
44539 	return false;
44540 }
44541 
getXmlDescription(Rules & r)44542 QString ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
44543 {
44544 	Q_UNUSED(r);
44545 
44546 	QString s="<ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour>\n";
44547 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
44548 	s+="	<Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
44549 	s+="	<Students>"+protect(this->students)+"</Students>\n";
44550 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
44551 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
44552 	s+="</ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour>\n";
44553 	return s;
44554 }
44555 
getDescription(Rules & r)44556 QString ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
44557 {
44558 	Q_UNUSED(r);
44559 
44560 	QString begin=QString("");
44561 	if(!active)
44562 		begin="X - ";
44563 
44564 	QString end=QString("");
44565 	if(!comments.isEmpty())
44566 		end=", "+tr("C: %1", "Comments").arg(comments);
44567 
44568 	QString s;
44569 
44570 	s+=tr("Students set must begin the afternoons early, respecting maximum %1 arrivals at second hour")
44571 	 .arg(this->maxBeginningsAtSecondHour); s+=", ";
44572 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
44573 	s+=tr("St:%1", "Students set").arg(this->students);
44574 
44575 	return begin+s+end;
44576 }
44577 
getDetailedDescription(Rules & r)44578 QString ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
44579 {
44580 	Q_UNUSED(r);
44581 
44582 	QString s=tr("Time constraint");s+="\n";
44583 
44584 	s+=tr("A students set must begin the afternoons early, respecting a maximum number of later arrivals, at second hour"); s+="\n";
44585 	s+=tr("(breaks and students set not available not counted)");s+="\n";
44586 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
44587 	s+=tr("Students set=%1").arg(this->students); s+="\n";
44588 	s+=tr("Maximum number of arrivals at the second hour=%1").arg(this->maxBeginningsAtSecondHour);s+="\n";
44589 
44590 	if(!active){
44591 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
44592 		s+="\n";
44593 	}
44594 	if(!comments.isEmpty()){
44595 		s+=tr("Comments=%1").arg(comments);
44596 		s+="\n";
44597 	}
44598 
44599 	return s;
44600 }
44601 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)44602 double ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
44603 {
44604 	//considers the condition that the hours of subgroups begin as early as possible
44605 
44606 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
44607 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
44608 		c.teachersMatrixReady=true;
44609 		c.subgroupsMatrixReady=true;
44610 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
44611 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
44612 
44613 		c.changedForMatrixCalculation=false;
44614 	}
44615 
44616 	int conflTotal=0;
44617 
44618 	for(int i : qAsConst(this->iSubgroupsList)){
44619 		int nGapsFirstHour=0;
44620 		for(int j=0; j<r.nDaysPerWeek; j++){
44621 			if(j%2==0)
44622 				continue;
44623 
44624 			int k;
44625 			for(k=0; k<r.nHoursPerDay; k++)
44626 				if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k])
44627 					break;
44628 
44629 			bool firstHourOccupied=false;
44630 			if(k<r.nHoursPerDay)
44631 				if(subgroupsMatrix[i][j][k]>0)
44632 					firstHourOccupied=true;
44633 
44634 			bool dayOccupied=firstHourOccupied;
44635 
44636 			bool illegalGap=false;
44637 
44638 			if(!dayOccupied){
44639 				for(k++; k<r.nHoursPerDay; k++){
44640 					if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
44641 						if(subgroupsMatrix[i][j][k]>0){
44642 							dayOccupied=true;
44643 							break;
44644 						}
44645 						else{
44646 							illegalGap=true;
44647 						}
44648 					}
44649 				}
44650 			}
44651 
44652 			if(dayOccupied && illegalGap){
44653 				if(conflictsString!=nullptr){
44654 					QString s=tr("Constraint students set afternoons early max %1 beginnings at second hour broken for subgroup %2, on day %3,"
44655 					 " because students have an illegal gap, increases conflicts total by %4")
44656 					 .arg(this->maxBeginningsAtSecondHour)
44657 					 .arg(r.internalSubgroupsList[i]->name)
44658 					 .arg(r.daysOfTheWeek[j])
44659 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
44660 
44661 					dl.append(s);
44662 					cl.append(1*weightPercentage/100);
44663 
44664 					*conflictsString+= s+"\n";
44665 
44666 					conflTotal+=1;
44667 				}
44668 
44669 				if(c.nPlacedActivities==r.nInternalActivities)
44670 					assert(0);
44671 			}
44672 
44673 			if(dayOccupied && !firstHourOccupied)
44674 				nGapsFirstHour++;
44675 		}
44676 
44677 		if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
44678 			if(conflictsString!=nullptr){
44679 				QString s=tr("Constraint students set afternoons early max %1 beginnings at second hour broken for subgroup %2,"
44680 				 " because students have too many arrivals at second hour, increases conflicts total by %3")
44681 				 .arg(this->maxBeginningsAtSecondHour)
44682 				 .arg(r.internalSubgroupsList[i]->name)
44683 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
44684 
44685 				dl.append(s);
44686 				cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
44687 
44688 				*conflictsString+= s+"\n";
44689 
44690 				conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
44691 			}
44692 
44693 			if(c.nPlacedActivities==r.nInternalActivities)
44694 				assert(0);
44695 		}
44696 	}
44697 
44698 	if(c.nPlacedActivities==r.nInternalActivities)
44699 		if(weightPercentage==100)    //might be broken for partial solutions
44700 			assert(conflTotal==0);
44701 	return weightPercentage/100 * conflTotal;
44702 }
44703 
isRelatedToActivity(Rules & r,Activity * a)44704 bool ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
44705 {
44706 	Q_UNUSED(r);
44707 	Q_UNUSED(a);
44708 
44709 	return false;
44710 }
44711 
isRelatedToTeacher(Teacher * t)44712 bool ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
44713 {
44714 	Q_UNUSED(t);
44715 
44716 	return false;
44717 }
44718 
isRelatedToSubject(Subject * s)44719 bool ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
44720 {
44721 	Q_UNUSED(s);
44722 
44723 	return false;
44724 }
44725 
isRelatedToActivityTag(ActivityTag * s)44726 bool ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
44727 {
44728 	Q_UNUSED(s);
44729 
44730 	return false;
44731 }
44732 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)44733 bool ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
44734 {
44735 	return r.setsShareStudents(this->students, s->name);
44736 }
44737 
hasWrongDayOrHour(Rules & r)44738 bool ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
44739 {
44740 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
44741 		return true;
44742 
44743 	return false;
44744 }
44745 
canRepairWrongDayOrHour(Rules & r)44746 bool ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
44747 {
44748 	assert(hasWrongDayOrHour(r));
44749 
44750 	return true;
44751 }
44752 
repairWrongDayOrHour(Rules & r)44753 bool ConstraintStudentsSetAfternoonsEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
44754 {
44755 	assert(hasWrongDayOrHour(r));
44756 
44757 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
44758 		maxBeginningsAtSecondHour=r.nDaysPerWeek/2;
44759 
44760 	return true;
44761 }
44762 
44763 //2020-07-29
44764 ////////////////////////////////////////////////////////////////////////////////////////////
44765 ////////////////////////////////////////////////////////////////////////////////////////////
44766 
ConstraintTeachersMaxGapsPerWeekForRealDays()44767 ConstraintTeachersMaxGapsPerWeekForRealDays::ConstraintTeachersMaxGapsPerWeekForRealDays()
44768 	: TimeConstraint()
44769 {
44770 	this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS;
44771 	this->maxGaps=-1;
44772 }
44773 
ConstraintTeachersMaxGapsPerWeekForRealDays(double wp,int mg)44774 ConstraintTeachersMaxGapsPerWeekForRealDays::ConstraintTeachersMaxGapsPerWeekForRealDays(double wp, int mg)
44775 	: TimeConstraint(wp)
44776 {
44777 	this->type = CONSTRAINT_TEACHERS_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS;
44778 	this->maxGaps=mg;
44779 }
44780 
computeInternalStructure(QWidget * parent,Rules & r)44781 bool ConstraintTeachersMaxGapsPerWeekForRealDays::computeInternalStructure(QWidget* parent, Rules& r)
44782 {
44783 	Q_UNUSED(parent);
44784 	Q_UNUSED(r);
44785 
44786 	return true;
44787 }
44788 
hasInactiveActivities(Rules & r)44789 bool ConstraintTeachersMaxGapsPerWeekForRealDays::hasInactiveActivities(Rules& r)
44790 {
44791 	Q_UNUSED(r);
44792 	return false;
44793 }
44794 
getXmlDescription(Rules & r)44795 QString ConstraintTeachersMaxGapsPerWeekForRealDays::getXmlDescription(Rules& r){
44796 	Q_UNUSED(r);
44797 
44798 	QString s="<ConstraintTeachersMaxGapsPerWeekForRealDays>\n";
44799 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
44800 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
44801 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
44802 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
44803 	s+="</ConstraintTeachersMaxGapsPerWeekForRealDays>\n";
44804 	return s;
44805 }
44806 
getDescription(Rules & r)44807 QString ConstraintTeachersMaxGapsPerWeekForRealDays::getDescription(Rules& r){
44808 	Q_UNUSED(r);
44809 
44810 	QString begin=QString("");
44811 	if(!active)
44812 		begin="X - ";
44813 
44814 	QString end=QString("");
44815 	if(!comments.isEmpty())
44816 		end=", "+tr("C: %1", "Comments").arg(comments);
44817 
44818 	QString s;
44819 	s+="! ";
44820 	s+=tr("Teachers max gaps per week for real days");s+=", ";
44821 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
44822 	s+=tr("MG:%1", "Max gaps (per week for real days)").arg(this->maxGaps);
44823 
44824 	return begin+s+end;
44825 }
44826 
getDetailedDescription(Rules & r)44827 QString ConstraintTeachersMaxGapsPerWeekForRealDays::getDetailedDescription(Rules& r){
44828 	Q_UNUSED(r);
44829 
44830 	QString s=tr("Time constraint");s+="\n";
44831 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
44832 	s+=tr("All teachers must respect the maximum gaps per week for real days");s+="\n";
44833 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
44834 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
44835 	s+=tr("Maximum gaps per week for real day=%1").arg(this->maxGaps); s+="\n";
44836 
44837 	if(!active){
44838 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
44839 		s+="\n";
44840 	}
44841 	if(!comments.isEmpty()){
44842 		s+=tr("Comments=%1").arg(comments);
44843 		s+="\n";
44844 	}
44845 
44846 	return s;
44847 }
44848 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)44849 double ConstraintTeachersMaxGapsPerWeekForRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
44850 {
44851 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
44852 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
44853 		c.teachersMatrixReady=true;
44854 		c.subgroupsMatrixReady=true;
44855 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
44856 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
44857 
44858 		c.changedForMatrixCalculation=false;
44859 	}
44860 
44861 	int tg;
44862 	int i, j, k;
44863 
44864 	int real_d, double_h;
44865 
44866 	int totalExtraGaps=0;
44867 
44868 	for(i=0; i<r.nInternalTeachers; i++){
44869 		tg=0;
44870 		for(real_d=0; real_d<r.nDaysPerWeek/2; real_d++){
44871 			for(double_h=0; double_h<2*r.nHoursPerDay; double_h++){
44872 				if(double_h<r.nHoursPerDay)
44873 					j=2*real_d;
44874 				else
44875 					j=2*real_d+1;
44876 				k=double_h%r.nHoursPerDay;
44877 				if(teachersMatrix[i][j][k]>0){
44878 					assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
44879 					break;
44880 				}
44881 			}
44882 
44883 			int cnt=0;
44884 			for(; double_h<2*r.nHoursPerDay; double_h++){
44885 				if(double_h<r.nHoursPerDay)
44886 					j=2*real_d;
44887 				else
44888 					j=2*real_d+1;
44889 				k=double_h%r.nHoursPerDay;
44890 				if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
44891 					if(teachersMatrix[i][j][k]>0){
44892 						tg+=cnt;
44893 						cnt=0;
44894 					}
44895 					else{
44896 						cnt++;
44897 					}
44898 				}
44899 			}
44900 		}
44901 		if(tg>this->maxGaps){
44902 			//assert(this->weightPercentage<100); partial solutions might break this rule
44903 
44904 			if(conflictsString!=nullptr){
44905 				QString s=tr("Time constraint teachers max gaps per week for real days broken for teacher %1, conflicts factor increase=%2")
44906 					.arg(r.internalTeachersList[i]->name)
44907 					.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps)*weightPercentage/100));
44908 
44909 				*conflictsString+= s+"\n";
44910 
44911 				dl.append(s);
44912 				cl.append((tg-maxGaps)*weightPercentage/100);
44913 			}
44914 
44915 			totalExtraGaps+=tg-maxGaps;
44916 		}
44917 	}
44918 
44919 	if(c.nPlacedActivities==r.nInternalActivities)
44920 		if(weightPercentage==100)
44921 			assert(totalExtraGaps==0); //for partial solutions this rule might be broken
44922 	return weightPercentage/100 * totalExtraGaps;
44923 }
44924 
isRelatedToActivity(Rules & r,Activity * a)44925 bool ConstraintTeachersMaxGapsPerWeekForRealDays::isRelatedToActivity(Rules& r, Activity* a)
44926 {
44927 	Q_UNUSED(r);
44928 	Q_UNUSED(a);
44929 
44930 	return false;
44931 }
44932 
isRelatedToTeacher(Teacher * t)44933 bool ConstraintTeachersMaxGapsPerWeekForRealDays::isRelatedToTeacher(Teacher* t)
44934 {
44935 	Q_UNUSED(t);
44936 
44937 	return true;
44938 }
44939 
isRelatedToSubject(Subject * s)44940 bool ConstraintTeachersMaxGapsPerWeekForRealDays::isRelatedToSubject(Subject* s)
44941 {
44942 	Q_UNUSED(s);
44943 
44944 	return false;
44945 }
44946 
isRelatedToActivityTag(ActivityTag * s)44947 bool ConstraintTeachersMaxGapsPerWeekForRealDays::isRelatedToActivityTag(ActivityTag* s)
44948 {
44949 	Q_UNUSED(s);
44950 
44951 	return false;
44952 }
44953 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)44954 bool ConstraintTeachersMaxGapsPerWeekForRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
44955 {
44956 	Q_UNUSED(r);
44957 	Q_UNUSED(s);
44958 
44959 	return false;
44960 }
44961 
hasWrongDayOrHour(Rules & r)44962 bool ConstraintTeachersMaxGapsPerWeekForRealDays::hasWrongDayOrHour(Rules& r)
44963 {
44964 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
44965 		return true;
44966 
44967 	return false;
44968 }
44969 
canRepairWrongDayOrHour(Rules & r)44970 bool ConstraintTeachersMaxGapsPerWeekForRealDays::canRepairWrongDayOrHour(Rules& r)
44971 {
44972 	assert(hasWrongDayOrHour(r));
44973 
44974 	return true;
44975 }
44976 
repairWrongDayOrHour(Rules & r)44977 bool ConstraintTeachersMaxGapsPerWeekForRealDays::repairWrongDayOrHour(Rules& r)
44978 {
44979 	assert(hasWrongDayOrHour(r));
44980 
44981 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
44982 		maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
44983 
44984 	return true;
44985 }
44986 
44987 ////////////////////////////////////////////////////////////////////////////////////////////
44988 ////////////////////////////////////////////////////////////////////////////////////////////
44989 
ConstraintTeacherMaxGapsPerWeekForRealDays()44990 ConstraintTeacherMaxGapsPerWeekForRealDays::ConstraintTeacherMaxGapsPerWeekForRealDays()
44991 	: TimeConstraint()
44992 {
44993 	this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS;
44994 	this->maxGaps = -1;
44995 }
44996 
ConstraintTeacherMaxGapsPerWeekForRealDays(double wp,QString tn,int mg)44997 ConstraintTeacherMaxGapsPerWeekForRealDays::ConstraintTeacherMaxGapsPerWeekForRealDays(double wp, QString tn, int mg)
44998 	: TimeConstraint(wp)
44999 {
45000 	this->type = CONSTRAINT_TEACHER_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS;
45001 	this->teacherName=tn;
45002 	this->maxGaps=mg;
45003 }
45004 
computeInternalStructure(QWidget * parent,Rules & r)45005 bool ConstraintTeacherMaxGapsPerWeekForRealDays::computeInternalStructure(QWidget* parent, Rules& r)
45006 {
45007 	Q_UNUSED(parent);
45008 
45009 	//this->teacherIndex=r.searchTeacher(this->teacherName);
45010 	teacherIndex=r.teachersHash.value(teacherName, -1);
45011 	assert(this->teacherIndex>=0);
45012 	return true;
45013 }
45014 
hasInactiveActivities(Rules & r)45015 bool ConstraintTeacherMaxGapsPerWeekForRealDays::hasInactiveActivities(Rules& r)
45016 {
45017 	Q_UNUSED(r);
45018 	return false;
45019 }
45020 
getXmlDescription(Rules & r)45021 QString ConstraintTeacherMaxGapsPerWeekForRealDays::getXmlDescription(Rules& r){
45022 	Q_UNUSED(r);
45023 
45024 	QString s="<ConstraintTeacherMaxGapsPerWeekForRealDays>\n";
45025 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
45026 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
45027 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
45028 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
45029 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
45030 	s+="</ConstraintTeacherMaxGapsPerWeekForRealDays>\n";
45031 	return s;
45032 }
45033 
getDescription(Rules & r)45034 QString ConstraintTeacherMaxGapsPerWeekForRealDays::getDescription(Rules& r){
45035 	Q_UNUSED(r);
45036 
45037 	QString begin=QString("");
45038 	if(!active)
45039 		begin="X - ";
45040 
45041 	QString end=QString("");
45042 	if(!comments.isEmpty())
45043 		end=", "+tr("C: %1", "Comments").arg(comments);
45044 
45045 	QString s;
45046 	s+="! ";
45047 	s+=tr("Teacher max gaps per week for real days");s+=", ";
45048 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
45049 	s+=tr("T:%1", "Teacher").arg(this->teacherName); s+=", ";
45050 	s+=tr("MG:%1", "Max gaps (per week for real days)").arg(this->maxGaps);
45051 
45052 	return begin+s+end;
45053 }
45054 
getDetailedDescription(Rules & r)45055 QString ConstraintTeacherMaxGapsPerWeekForRealDays::getDetailedDescription(Rules& r){
45056 	Q_UNUSED(r);
45057 
45058 	QString s=tr("Time constraint"); s+="\n";
45059 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
45060 	s+=tr("A teacher must respect the maximum number of gaps per week for real days"); s+="\n";
45061 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
45062 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage)); s+="\n";
45063 	s+=tr("Teacher=%1").arg(this->teacherName); s+="\n";
45064 	s+=tr("Maximum gaps per week for real days=%1").arg(this->maxGaps); s+="\n";
45065 
45066 	if(!active){
45067 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
45068 		s+="\n";
45069 	}
45070 	if(!comments.isEmpty()){
45071 		s+=tr("Comments=%1").arg(comments);
45072 		s+="\n";
45073 	}
45074 
45075 	return s;
45076 }
45077 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)45078 double ConstraintTeacherMaxGapsPerWeekForRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
45079 {
45080 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
45081 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
45082 		c.teachersMatrixReady=true;
45083 		c.subgroupsMatrixReady=true;
45084 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
45085 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
45086 
45087 		c.changedForMatrixCalculation=false;
45088 	}
45089 
45090 	int tg;
45091 	int i, j, k;
45092 
45093 	i=this->teacherIndex;
45094 
45095 	int real_d, double_h;
45096 
45097 	int totalExtraGaps=0;
45098 
45099 	tg=0;
45100 	for(real_d=0; real_d<r.nDaysPerWeek/2; real_d++){
45101 		for(double_h=0; double_h<2*r.nHoursPerDay; double_h++){
45102 			if(double_h<r.nHoursPerDay)
45103 				j=2*real_d;
45104 			else
45105 				j=2*real_d+1;
45106 			k=double_h%r.nHoursPerDay;
45107 			if(teachersMatrix[i][j][k]>0){
45108 				assert(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]);
45109 				break;
45110 			}
45111 		}
45112 
45113 		int cnt=0;
45114 		for(; double_h<2*r.nHoursPerDay; double_h++){
45115 			if(double_h<r.nHoursPerDay)
45116 				j=2*real_d;
45117 			else
45118 				j=2*real_d+1;
45119 			k=double_h%r.nHoursPerDay;
45120 			if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
45121 				if(teachersMatrix[i][j][k]>0){
45122 					tg+=cnt;
45123 					cnt=0;
45124 				}
45125 				else
45126 					cnt++;
45127 			}
45128 		}
45129 	}
45130 	if(tg>this->maxGaps){
45131 		//assert(this->weightPercentage<100); partial solutions might break this rule
45132 		if(conflictsString!=nullptr){
45133 			QString s=tr("Time constraint teacher max gaps per week for real days broken for teacher: %1, conflicts factor increase=%2")
45134 				.arg(r.internalTeachersList[i]->name)
45135 				.arg(CustomFETString::numberPlusTwoDigitsPrecision((tg-maxGaps)*weightPercentage/100));
45136 
45137 			*conflictsString+= s+"\n";
45138 
45139 			dl.append(s);
45140 			cl.append((tg-maxGaps)*weightPercentage/100);
45141 
45142 			totalExtraGaps+=tg-maxGaps;
45143 		}
45144 	}
45145 
45146 	if(c.nPlacedActivities==r.nInternalActivities)
45147 		if(weightPercentage==100)
45148 			assert(totalExtraGaps==0); //for partial solutions this rule might be broken
45149 	return weightPercentage/100 * totalExtraGaps;
45150 }
45151 
isRelatedToActivity(Rules & r,Activity * a)45152 bool ConstraintTeacherMaxGapsPerWeekForRealDays::isRelatedToActivity(Rules& r, Activity* a)
45153 {
45154 	Q_UNUSED(r);
45155 	Q_UNUSED(a);
45156 
45157 	return false;
45158 }
45159 
isRelatedToTeacher(Teacher * t)45160 bool ConstraintTeacherMaxGapsPerWeekForRealDays::isRelatedToTeacher(Teacher* t)
45161 {
45162 	if(this->teacherName==t->name)
45163 		return true;
45164 	return false;
45165 }
45166 
isRelatedToSubject(Subject * s)45167 bool ConstraintTeacherMaxGapsPerWeekForRealDays::isRelatedToSubject(Subject* s)
45168 {
45169 	Q_UNUSED(s);
45170 
45171 	return false;
45172 }
45173 
isRelatedToActivityTag(ActivityTag * s)45174 bool ConstraintTeacherMaxGapsPerWeekForRealDays::isRelatedToActivityTag(ActivityTag* s)
45175 {
45176 	Q_UNUSED(s);
45177 
45178 	return false;
45179 }
45180 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)45181 bool ConstraintTeacherMaxGapsPerWeekForRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
45182 {
45183 	Q_UNUSED(r);
45184 	Q_UNUSED(s);
45185 
45186 	return false;
45187 }
45188 
hasWrongDayOrHour(Rules & r)45189 bool ConstraintTeacherMaxGapsPerWeekForRealDays::hasWrongDayOrHour(Rules& r)
45190 {
45191 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
45192 		return true;
45193 
45194 	return false;
45195 }
45196 
canRepairWrongDayOrHour(Rules & r)45197 bool ConstraintTeacherMaxGapsPerWeekForRealDays::canRepairWrongDayOrHour(Rules& r)
45198 {
45199 	assert(hasWrongDayOrHour(r));
45200 
45201 	return true;
45202 }
45203 
repairWrongDayOrHour(Rules & r)45204 bool ConstraintTeacherMaxGapsPerWeekForRealDays::repairWrongDayOrHour(Rules& r)
45205 {
45206 	assert(hasWrongDayOrHour(r));
45207 
45208 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
45209 		maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
45210 
45211 	return true;
45212 }
45213 
45214 ////////////////////////////////////////////////////////////////////////////////////////////
45215 ////////////////////////////////////////////////////////////////////////////////////////////
45216 
ConstraintStudentsMaxGapsPerWeekForRealDays()45217 ConstraintStudentsMaxGapsPerWeekForRealDays::ConstraintStudentsMaxGapsPerWeekForRealDays()
45218 	: TimeConstraint()
45219 {
45220 	this->type = CONSTRAINT_STUDENTS_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS;
45221 }
45222 
ConstraintStudentsMaxGapsPerWeekForRealDays(double wp,int mg)45223 ConstraintStudentsMaxGapsPerWeekForRealDays::ConstraintStudentsMaxGapsPerWeekForRealDays(double wp, int mg)
45224 	: TimeConstraint(wp)
45225 {
45226 	this->type = CONSTRAINT_STUDENTS_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS;
45227 	this->maxGaps=mg;
45228 }
45229 
computeInternalStructure(QWidget * parent,Rules & r)45230 bool ConstraintStudentsMaxGapsPerWeekForRealDays::computeInternalStructure(QWidget* parent, Rules& r)
45231 {
45232 	Q_UNUSED(parent);
45233 	Q_UNUSED(r);
45234 
45235 	return true;
45236 }
45237 
hasInactiveActivities(Rules & r)45238 bool ConstraintStudentsMaxGapsPerWeekForRealDays::hasInactiveActivities(Rules& r)
45239 {
45240 	Q_UNUSED(r);
45241 	return false;
45242 }
45243 
getXmlDescription(Rules & r)45244 QString ConstraintStudentsMaxGapsPerWeekForRealDays::getXmlDescription(Rules& r)
45245 {
45246 	Q_UNUSED(r);
45247 
45248 	QString s="<ConstraintStudentsMaxGapsPerWeekForRealDays>\n";
45249 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
45250 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
45251 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
45252 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
45253 	s+="</ConstraintStudentsMaxGapsPerWeekForRealDays>\n";
45254 	return s;
45255 }
45256 
getDescription(Rules & r)45257 QString ConstraintStudentsMaxGapsPerWeekForRealDays::getDescription(Rules& r)
45258 {
45259 	Q_UNUSED(r);
45260 
45261 	QString begin=QString("");
45262 	if(!active)
45263 		begin="X - ";
45264 
45265 	QString end=QString("");
45266 	if(!comments.isEmpty())
45267 		end=", "+tr("C: %1", "Comments").arg(comments);
45268 
45269 	QString s;
45270 	s+="! ";
45271 	s+=tr("Students max gaps per week for real days");s+=", ";
45272 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
45273 	s+=tr("MG:%1", "Max gaps (per week for real days)").arg(this->maxGaps);
45274 
45275 	return begin+s+end;
45276 }
45277 
getDetailedDescription(Rules & r)45278 QString ConstraintStudentsMaxGapsPerWeekForRealDays::getDetailedDescription(Rules& r)
45279 {
45280 	Q_UNUSED(r);
45281 
45282 	QString s=tr("Time constraint");s+="\n";
45283 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
45284 	s+=tr("All students must respect the maximum number of gaps per week for real days");s+="\n";
45285 	s+=tr("(breaks and students set not available not counted)");s+="\n";
45286 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
45287 	s+=tr("Maximum gaps per week for real days=%1").arg(this->maxGaps);s+="\n";
45288 
45289 	if(!active){
45290 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
45291 		s+="\n";
45292 	}
45293 	if(!comments.isEmpty()){
45294 		s+=tr("Comments=%1").arg(comments);
45295 		s+="\n";
45296 	}
45297 
45298 	return s;
45299 }
45300 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)45301 double ConstraintStudentsMaxGapsPerWeekForRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
45302 {
45303 	//returns a number equal to the number of gaps of the subgroups (in hours)
45304 
45305 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
45306 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
45307 		c.teachersMatrixReady=true;
45308 		c.subgroupsMatrixReady=true;
45309 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
45310 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
45311 
45312 		c.changedForMatrixCalculation=false;
45313 	}
45314 
45315 	int nGaps;
45316 	int tmp;
45317 	int i;
45318 
45319 	int tIllegalGaps=0;
45320 
45321 	for(i=0; i<r.nInternalSubgroups; i++){
45322 		nGaps=0;
45323 		for(int real_d=0; real_d<r.nDaysPerWeek/2; real_d++){
45324 			int double_h;
45325 
45326 			int k;
45327 			tmp=0;
45328 			for(double_h=0; double_h<2*r.nHoursPerDay; double_h++){
45329 				int j;
45330 				if(double_h<r.nHoursPerDay)
45331 					j=2*real_d;
45332 				else
45333 					j=2*real_d+1;
45334 				k=double_h%r.nHoursPerDay;
45335 				if(subgroupsMatrix[i][j][k]>0){
45336 					assert(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]);
45337 					break;
45338 				}
45339 			}
45340 			for(; double_h<2*r.nHoursPerDay; double_h++){
45341 				int j;
45342 				if(double_h<r.nHoursPerDay)
45343 					j=2*real_d;
45344 				else
45345 					j=2*real_d+1;
45346 				k=double_h%r.nHoursPerDay;
45347 				if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
45348 					if(subgroupsMatrix[i][j][k]>0){
45349 						nGaps+=tmp;
45350 						tmp=0;
45351 					}
45352 					else
45353 						tmp++;
45354 				}
45355 			}
45356 		}
45357 
45358 		int illegalGaps=nGaps-this->maxGaps;
45359 		if(illegalGaps<0)
45360 			illegalGaps=0;
45361 
45362 		if(illegalGaps>0 && conflictsString!=nullptr){
45363 			QString s=tr("Time constraint students max gaps per week for real days broken for subgroup: %1, it has %2 extra gaps, conflicts increase=%3")
45364 			 .arg(r.internalSubgroupsList[i]->name)
45365 			 .arg(illegalGaps)
45366 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(illegalGaps*weightPercentage/100));
45367 
45368 			dl.append(s);
45369 			cl.append(illegalGaps*weightPercentage/100);
45370 
45371 			*conflictsString+= s+"\n";
45372 
45373 			tIllegalGaps+=illegalGaps;
45374 		}
45375 	}
45376 
45377 	if(c.nPlacedActivities==r.nInternalActivities)
45378 		if(weightPercentage==100)    //for partial solutions it might be broken
45379 			assert(tIllegalGaps==0);
45380 	return weightPercentage/100 * tIllegalGaps;
45381 }
45382 
isRelatedToActivity(Rules & r,Activity * a)45383 bool ConstraintStudentsMaxGapsPerWeekForRealDays::isRelatedToActivity(Rules& r, Activity* a)
45384 {
45385 	Q_UNUSED(r);
45386 	Q_UNUSED(a);
45387 
45388 	return false;
45389 }
45390 
isRelatedToTeacher(Teacher * t)45391 bool ConstraintStudentsMaxGapsPerWeekForRealDays::isRelatedToTeacher(Teacher* t)
45392 {
45393 	Q_UNUSED(t);
45394 
45395 	return false;
45396 }
45397 
isRelatedToSubject(Subject * s)45398 bool ConstraintStudentsMaxGapsPerWeekForRealDays::isRelatedToSubject(Subject* s)
45399 {
45400 	Q_UNUSED(s);
45401 
45402 	return false;
45403 }
45404 
isRelatedToActivityTag(ActivityTag * s)45405 bool ConstraintStudentsMaxGapsPerWeekForRealDays::isRelatedToActivityTag(ActivityTag* s)
45406 {
45407 	Q_UNUSED(s);
45408 
45409 	return false;
45410 }
45411 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)45412 bool ConstraintStudentsMaxGapsPerWeekForRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
45413 {
45414 	Q_UNUSED(r);
45415 	Q_UNUSED(s);
45416 
45417 	return true;
45418 }
45419 
hasWrongDayOrHour(Rules & r)45420 bool ConstraintStudentsMaxGapsPerWeekForRealDays::hasWrongDayOrHour(Rules& r)
45421 {
45422 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
45423 		return true;
45424 
45425 	return false;
45426 }
45427 
canRepairWrongDayOrHour(Rules & r)45428 bool ConstraintStudentsMaxGapsPerWeekForRealDays::canRepairWrongDayOrHour(Rules& r)
45429 {
45430 	assert(hasWrongDayOrHour(r));
45431 
45432 	return true;
45433 }
45434 
repairWrongDayOrHour(Rules & r)45435 bool ConstraintStudentsMaxGapsPerWeekForRealDays::repairWrongDayOrHour(Rules& r)
45436 {
45437 	assert(hasWrongDayOrHour(r));
45438 
45439 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
45440 		maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
45441 
45442 	return true;
45443 }
45444 
45445 ////////////////////////////////////////////////////////////////////////////////////////////
45446 ////////////////////////////////////////////////////////////////////////////////////////////
45447 
ConstraintStudentsSetMaxGapsPerWeekForRealDays()45448 ConstraintStudentsSetMaxGapsPerWeekForRealDays::ConstraintStudentsSetMaxGapsPerWeekForRealDays()
45449 	: TimeConstraint()
45450 {
45451 	this->type = CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS;
45452 }
45453 
ConstraintStudentsSetMaxGapsPerWeekForRealDays(double wp,int mg,const QString & st)45454 ConstraintStudentsSetMaxGapsPerWeekForRealDays::ConstraintStudentsSetMaxGapsPerWeekForRealDays(double wp, int mg, const QString& st )
45455 	: TimeConstraint(wp)
45456 {
45457 	this->type = CONSTRAINT_STUDENTS_SET_MAX_GAPS_PER_WEEK_FOR_REAL_DAYS;
45458 	this->maxGaps=mg;
45459 	this->students = st;
45460 }
45461 
computeInternalStructure(QWidget * parent,Rules & r)45462 bool ConstraintStudentsSetMaxGapsPerWeekForRealDays::computeInternalStructure(QWidget* parent, Rules& r){
45463 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
45464 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
45465 
45466 	if(ss==nullptr){
45467 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
45468 		 tr("Constraint students set max gaps per week for real days is wrong because it refers to inexistent students set."
45469 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
45470 
45471 		return false;
45472 	}
45473 
45474 	assert(ss!=nullptr);
45475 
45476 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
45477 	/*this->iSubgroupsList.clear();
45478 	if(ss->type==STUDENTS_SUBGROUP){
45479 		int tmp;
45480 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
45481 		assert(tmp>=0);
45482 		assert(tmp<r.nInternalSubgroups);
45483 		if(!this->iSubgroupsList.contains(tmp))
45484 			this->iSubgroupsList.append(tmp);
45485 	}
45486 	else if(ss->type==STUDENTS_GROUP){
45487 		StudentsGroup* stg=(StudentsGroup*)ss;
45488 		for(int i=0; i<stg->subgroupsList.size(); i++){
45489 			StudentsSubgroup* sts=stg->subgroupsList[i];
45490 			int tmp;
45491 			tmp=sts->indexInInternalSubgroupsList;
45492 			assert(tmp>=0);
45493 			assert(tmp<r.nInternalSubgroups);
45494 			if(!this->iSubgroupsList.contains(tmp))
45495 				this->iSubgroupsList.append(tmp);
45496 		}
45497 	}
45498 	else if(ss->type==STUDENTS_YEAR){
45499 		StudentsYear* sty=(StudentsYear*)ss;
45500 		for(int i=0; i<sty->groupsList.size(); i++){
45501 			StudentsGroup* stg=sty->groupsList[i];
45502 			for(int j=0; j<stg->subgroupsList.size(); j++){
45503 				StudentsSubgroup* sts=stg->subgroupsList[j];
45504 				int tmp;
45505 				tmp=sts->indexInInternalSubgroupsList;
45506 				assert(tmp>=0);
45507 				assert(tmp<r.nInternalSubgroups);
45508 				if(!this->iSubgroupsList.contains(tmp))
45509 					this->iSubgroupsList.append(tmp);
45510 			}
45511 		}
45512 	}
45513 	else
45514 		assert(0);*/
45515 
45516 	return true;
45517 }
45518 
hasInactiveActivities(Rules & r)45519 bool ConstraintStudentsSetMaxGapsPerWeekForRealDays::hasInactiveActivities(Rules& r)
45520 {
45521 	Q_UNUSED(r);
45522 	return false;
45523 }
45524 
getXmlDescription(Rules & r)45525 QString ConstraintStudentsSetMaxGapsPerWeekForRealDays::getXmlDescription(Rules& r){
45526 	Q_UNUSED(r);
45527 
45528 	QString s="<ConstraintStudentsSetMaxGapsPerWeekForRealDays>\n";
45529 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
45530 	s+="	<Max_Gaps>"+CustomFETString::number(this->maxGaps)+"</Max_Gaps>\n";
45531 	s+="	<Students>"; s+=protect(this->students); s+="</Students>\n";
45532 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
45533 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
45534 	s+="</ConstraintStudentsSetMaxGapsPerWeekForRealDays>\n";
45535 	return s;
45536 }
45537 
getDescription(Rules & r)45538 QString ConstraintStudentsSetMaxGapsPerWeekForRealDays::getDescription(Rules& r){
45539 	Q_UNUSED(r);
45540 
45541 	QString begin=QString("");
45542 	if(!active)
45543 		begin="X - ";
45544 
45545 	QString end=QString("");
45546 	if(!comments.isEmpty())
45547 		end=", "+tr("C: %1", "Comments").arg(comments);
45548 
45549 	QString s;
45550 	s+="! ";
45551 	s+=tr("Students set max gaps per week for real days"); s+=", ";
45552 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage)); s+=", ";
45553 	s+=tr("MG:%1", "Max gaps (per week for real days)").arg(this->maxGaps);s+=", ";
45554 	s+=tr("St:%1", "Students").arg(this->students);
45555 
45556 	return begin+s+end;
45557 }
45558 
getDetailedDescription(Rules & r)45559 QString ConstraintStudentsSetMaxGapsPerWeekForRealDays::getDetailedDescription(Rules& r){
45560 	Q_UNUSED(r);
45561 
45562 	QString s=tr("Time constraint");s+="\n";
45563 	s+=tr("(not perfect)", "It refers to a not perfect constraint"); s+="\n";
45564 	s+=tr("A students set must respect the maximum number of gaps per week for real days");s+="\n";
45565 	s+=tr("(breaks and students set not available not counted)");s+="\n";
45566 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
45567 	s+=tr("Maximum gaps per week for real days=%1").arg(this->maxGaps);s+="\n";
45568 	s+=tr("Students=%1").arg(this->students); s+="\n";
45569 
45570 	if(!active){
45571 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
45572 		s+="\n";
45573 	}
45574 	if(!comments.isEmpty()){
45575 		s+=tr("Comments=%1").arg(comments);
45576 		s+="\n";
45577 	}
45578 
45579 	return s;
45580 }
45581 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)45582 double ConstraintStudentsSetMaxGapsPerWeekForRealDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
45583 {
45584 	//OLD COMMENT
45585 	//returns a number equal to the number of gaps of the subgroups (in hours)
45586 
45587 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
45588 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
45589 		c.teachersMatrixReady=true;
45590 		c.subgroupsMatrixReady=true;
45591 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
45592 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
45593 
45594 		c.changedForMatrixCalculation=false;
45595 	}
45596 
45597 	int nGaps;
45598 	int tmp;
45599 
45600 	int tIllegalGaps=0;
45601 
45602 	for(int sg=0; sg<this->iSubgroupsList.count(); sg++){
45603 		int i=this->iSubgroupsList.at(sg);
45604 		nGaps=0;
45605 		for(int real_d=0; real_d<r.nDaysPerWeek/2; real_d++){
45606 			tmp=0;
45607 
45608 			int double_h;
45609 
45610 			for(double_h=0; double_h<2*r.nHoursPerDay; double_h++){
45611 				int j;
45612 				if(double_h<r.nHoursPerDay)
45613 					j=2*real_d;
45614 				else
45615 					j=2*real_d+1;
45616 				int k=double_h%r.nHoursPerDay;
45617 				if(subgroupsMatrix[i][j][k]>0){
45618 					assert(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]);
45619 					break;
45620 				}
45621 			}
45622 			for(; double_h<2*r.nHoursPerDay; double_h++){
45623 				int j;
45624 				if(double_h<r.nHoursPerDay)
45625 					j=2*real_d;
45626 				else
45627 					j=2*real_d+1;
45628 				int k=double_h%r.nHoursPerDay;
45629 				if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
45630 					if(subgroupsMatrix[i][j][k]>0){
45631 						nGaps+=tmp;
45632 						tmp=0;
45633 					}
45634 					else
45635 						tmp++;
45636 				}
45637 			}
45638 		}
45639 
45640 		int illegalGaps=nGaps-this->maxGaps;
45641 		if(illegalGaps<0)
45642 			illegalGaps=0;
45643 
45644 		if(illegalGaps>0 && conflictsString!=nullptr){
45645 			QString s=tr("Time constraint students set max gaps per week for real days broken for subgroup: %1, extra gaps=%2, conflicts increase=%3")
45646 			 .arg(r.internalSubgroupsList[i]->name)
45647 			 .arg(illegalGaps)
45648 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(weightPercentage/100*illegalGaps));
45649 
45650 			dl.append(s);
45651 			cl.append(weightPercentage/100*illegalGaps);
45652 
45653 			*conflictsString+= s+"\n";
45654 
45655 			tIllegalGaps+=illegalGaps;
45656 		}
45657 	}
45658 
45659 	if(c.nPlacedActivities==r.nInternalActivities)
45660 		if(weightPercentage==100)     //for partial solutions it might be broken
45661 			assert(tIllegalGaps==0);
45662 	return weightPercentage/100 * tIllegalGaps;
45663 }
45664 
isRelatedToActivity(Rules & r,Activity * a)45665 bool ConstraintStudentsSetMaxGapsPerWeekForRealDays::isRelatedToActivity(Rules& r, Activity* a)
45666 {
45667 	Q_UNUSED(r);
45668 	Q_UNUSED(a);
45669 
45670 	return false;
45671 }
45672 
isRelatedToTeacher(Teacher * t)45673 bool ConstraintStudentsSetMaxGapsPerWeekForRealDays::isRelatedToTeacher(Teacher* t)
45674 {
45675 	Q_UNUSED(t);
45676 
45677 	return false;
45678 }
45679 
isRelatedToSubject(Subject * s)45680 bool ConstraintStudentsSetMaxGapsPerWeekForRealDays::isRelatedToSubject(Subject* s)
45681 {
45682 	Q_UNUSED(s);
45683 
45684 	return false;
45685 }
45686 
isRelatedToActivityTag(ActivityTag * s)45687 bool ConstraintStudentsSetMaxGapsPerWeekForRealDays::isRelatedToActivityTag(ActivityTag* s)
45688 {
45689 	Q_UNUSED(s);
45690 
45691 	return false;
45692 }
45693 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)45694 bool ConstraintStudentsSetMaxGapsPerWeekForRealDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
45695 {
45696 	return r.setsShareStudents(this->students, s->name);
45697 }
45698 
hasWrongDayOrHour(Rules & r)45699 bool ConstraintStudentsSetMaxGapsPerWeekForRealDays::hasWrongDayOrHour(Rules& r)
45700 {
45701 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
45702 		return true;
45703 
45704 	return false;
45705 }
45706 
canRepairWrongDayOrHour(Rules & r)45707 bool ConstraintStudentsSetMaxGapsPerWeekForRealDays::canRepairWrongDayOrHour(Rules& r)
45708 {
45709 	assert(hasWrongDayOrHour(r));
45710 
45711 	return true;
45712 }
45713 
repairWrongDayOrHour(Rules & r)45714 bool ConstraintStudentsSetMaxGapsPerWeekForRealDays::repairWrongDayOrHour(Rules& r)
45715 {
45716 	assert(hasWrongDayOrHour(r));
45717 
45718 	if(maxGaps>r.nDaysPerWeek*r.nHoursPerDay)
45719 		maxGaps=r.nDaysPerWeek*r.nHoursPerDay;
45720 
45721 	return true;
45722 }
45723 
45724 //2021-08-12
45725 ////////////////////////////////////////////////////////////////////////////////////////////
45726 ////////////////////////////////////////////////////////////////////////////////////////////
45727 
ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour()45728 ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour()
45729 	: TimeConstraint()
45730 {
45731 	this->type = CONSTRAINT_TEACHERS_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
45732 }
45733 
ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour(double wp,int mBSH)45734 ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour(double wp, int mBSH)
45735 	: TimeConstraint(wp)
45736 {
45737 	this->type = CONSTRAINT_TEACHERS_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
45738 	this->maxBeginningsAtSecondHour=mBSH;
45739 }
45740 
computeInternalStructure(QWidget * parent,Rules & r)45741 bool ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
45742 {
45743 	Q_UNUSED(parent);
45744 	Q_UNUSED(r);
45745 
45746 	return true;
45747 }
45748 
hasInactiveActivities(Rules & r)45749 bool ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
45750 {
45751 	Q_UNUSED(r);
45752 	return false;
45753 }
45754 
getXmlDescription(Rules & r)45755 QString ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
45756 {
45757 	Q_UNUSED(r);
45758 
45759 	QString s="<ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour>\n";
45760 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
45761 	s+="	<Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
45762 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
45763 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
45764 	s+="</ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour>\n";
45765 	return s;
45766 }
45767 
getDescription(Rules & r)45768 QString ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
45769 {
45770 	Q_UNUSED(r);
45771 
45772 	QString begin=QString("");
45773 	if(!active)
45774 		begin="X - ";
45775 
45776 	QString end=QString("");
45777 	if(!comments.isEmpty())
45778 		end=", "+tr("C: %1", "Comments").arg(comments);
45779 
45780 	QString s;
45781 	s+=tr("Teachers must begin mornings early, respecting maximum %1 arrivals at second hour")
45782 	 .arg(this->maxBeginningsAtSecondHour);
45783 	s+=", ";
45784 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
45785 
45786 	return begin+s+end;
45787 }
45788 
getDetailedDescription(Rules & r)45789 QString ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
45790 {
45791 	Q_UNUSED(r);
45792 
45793 	QString s=tr("Time constraint");s+="\n";
45794 	s+=tr("All teachers must begin the mornings early, respecting maximum %1 later arrivals, at second hour")
45795 	 .arg(this->maxBeginningsAtSecondHour);s+="\n";
45796 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
45797 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
45798 
45799 	if(!active){
45800 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
45801 		s+="\n";
45802 	}
45803 	if(!comments.isEmpty()){
45804 		s+=tr("Comments=%1").arg(comments);
45805 		s+="\n";
45806 	}
45807 
45808 	return s;
45809 }
45810 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)45811 double ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
45812 {
45813 	//considers the condition that the hours of teachers begin as early as possible the mornings
45814 
45815 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
45816 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
45817 		c.teachersMatrixReady=true;
45818 		c.subgroupsMatrixReady=true;
45819 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
45820 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
45821 
45822 		c.changedForMatrixCalculation=false;
45823 	}
45824 
45825 	int conflTotal=0;
45826 
45827 	for(int i=0; i<r.nInternalTeachers; i++){
45828 		int nGapsFirstHour=0;
45829 		for(int j=0; j<r.nDaysPerWeek; j++){
45830 			if(j%2==1)
45831 				continue;
45832 
45833 			int k;
45834 			for(k=0; k<r.nHoursPerDay; k++)
45835 				if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k])
45836 					break;
45837 
45838 			bool firstHourOccupied=false;
45839 			if(k<r.nHoursPerDay)
45840 				if(teachersMatrix[i][j][k]>0)
45841 					firstHourOccupied=true;
45842 
45843 			bool dayOccupied=firstHourOccupied;
45844 
45845 			bool illegalGap=false;
45846 
45847 			if(!dayOccupied){
45848 				for(k++; k<r.nHoursPerDay; k++){
45849 					if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
45850 						if(teachersMatrix[i][j][k]>0){
45851 							dayOccupied=true;
45852 							break;
45853 						}
45854 						else{
45855 							illegalGap=true;
45856 						}
45857 					}
45858 				}
45859 			}
45860 
45861 			if(dayOccupied && illegalGap){
45862 				if(conflictsString!=nullptr){
45863 					QString s=tr("Constraint teachers mornings early max %1 beginnings at second hour broken for teacher %2, on day %3,"
45864 					 " because the teacher has an illegal gap, increases conflicts total by %4")
45865 					 .arg(this->maxBeginningsAtSecondHour)
45866 					 .arg(r.internalTeachersList[i]->name)
45867 					 .arg(r.daysOfTheWeek[j])
45868 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
45869 
45870 					dl.append(s);
45871 					cl.append(1*weightPercentage/100);
45872 
45873 					*conflictsString+= s+"\n";
45874 
45875 					conflTotal+=1;
45876 				}
45877 
45878 				if(c.nPlacedActivities==r.nInternalActivities){
45879 					assert(0);
45880 				}
45881 			}
45882 
45883 			if(dayOccupied && !firstHourOccupied)
45884 				nGapsFirstHour++;
45885 		}
45886 
45887 		if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
45888 			if(conflictsString!=nullptr){
45889 				QString s=tr("Constraint teachers mornings early max %1 beginnings at second hour broken for teacher %2,"
45890 				 " because the teacher has too many arrivals at second hour, increases conflicts total by %3")
45891 				 .arg(this->maxBeginningsAtSecondHour)
45892 				 .arg(r.internalTeachersList[i]->name)
45893 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
45894 
45895 				dl.append(s);
45896 				cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
45897 
45898 				*conflictsString+= s+"\n";
45899 
45900 				conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
45901 			}
45902 
45903 			if(c.nPlacedActivities==r.nInternalActivities){
45904 				assert(0);
45905 			}
45906 		}
45907 	}
45908 
45909 	if(c.nPlacedActivities==r.nInternalActivities)
45910 		if(weightPercentage==100)    //might be broken for partial solutions
45911 			assert(conflTotal==0);
45912 	return weightPercentage/100 * conflTotal;
45913 }
45914 
isRelatedToActivity(Rules & r,Activity * a)45915 bool ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
45916 {
45917 	Q_UNUSED(r);
45918 	Q_UNUSED(a);
45919 
45920 	return false;
45921 }
45922 
isRelatedToTeacher(Teacher * t)45923 bool ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
45924 {
45925 	Q_UNUSED(t);
45926 
45927 	return true;
45928 }
45929 
isRelatedToSubject(Subject * s)45930 bool ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
45931 {
45932 	Q_UNUSED(s);
45933 
45934 	return false;
45935 }
45936 
isRelatedToActivityTag(ActivityTag * s)45937 bool ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
45938 {
45939 	Q_UNUSED(s);
45940 
45941 	return false;
45942 }
45943 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)45944 bool ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
45945 {
45946 	Q_UNUSED(r);
45947 	Q_UNUSED(s);
45948 
45949 	return false;
45950 }
45951 
hasWrongDayOrHour(Rules & r)45952 bool ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
45953 {
45954 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
45955 		return true;
45956 
45957 	return false;
45958 }
45959 
canRepairWrongDayOrHour(Rules & r)45960 bool ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
45961 {
45962 	assert(hasWrongDayOrHour(r));
45963 
45964 	return true;
45965 }
45966 
repairWrongDayOrHour(Rules & r)45967 bool ConstraintTeachersMorningsEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
45968 {
45969 	assert(hasWrongDayOrHour(r));
45970 
45971 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
45972 		maxBeginningsAtSecondHour=r.nDaysPerWeek/2;
45973 
45974 	return true;
45975 }
45976 
45977 ////////////////////////////////////////////////////////////////////////////////////////////
45978 ////////////////////////////////////////////////////////////////////////////////////////////
45979 
ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour()45980 ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour()
45981 	: TimeConstraint()
45982 {
45983 	this->type = CONSTRAINT_TEACHER_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
45984 }
45985 
ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour(double wp,int mBSH,const QString & teacher)45986 ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour(double wp, int mBSH, const QString& teacher)
45987 	: TimeConstraint(wp)
45988 {
45989 	this->type = CONSTRAINT_TEACHER_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
45990 	this->teacherName=teacher;
45991 	this->maxBeginningsAtSecondHour=mBSH;
45992 }
45993 
computeInternalStructure(QWidget * parent,Rules & r)45994 bool ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
45995 {
45996 	Q_UNUSED(parent);
45997 
45998 	teacherIndex=r.teachersHash.value(teacherName, -1);
45999 	assert(this->teacherIndex>=0);
46000 
46001 	return true;
46002 }
46003 
hasInactiveActivities(Rules & r)46004 bool ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
46005 {
46006 	Q_UNUSED(r);
46007 	return false;
46008 }
46009 
getXmlDescription(Rules & r)46010 QString ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
46011 {
46012 	Q_UNUSED(r);
46013 
46014 	QString s="<ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour>\n";
46015 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
46016 	s+="	<Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
46017 	s+="	<Teacher>"+protect(this->teacherName)+"</Teacher>\n";
46018 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
46019 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
46020 	s+="</ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour>\n";
46021 	return s;
46022 }
46023 
getDescription(Rules & r)46024 QString ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
46025 {
46026 	Q_UNUSED(r);
46027 
46028 	QString begin=QString("");
46029 	if(!active)
46030 		begin="X - ";
46031 
46032 	QString end=QString("");
46033 	if(!comments.isEmpty())
46034 		end=", "+tr("C: %1", "Comments").arg(comments);
46035 
46036 	QString s;
46037 
46038 	s+=tr("Teacher must begin mornings early, respecting maximum %1 arrivals at second hour")
46039 	 .arg(this->maxBeginningsAtSecondHour); s+=", ";
46040 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
46041 	s+=tr("T:%1", "Teacher").arg(this->teacherName);
46042 
46043 	return begin+s+end;
46044 }
46045 
getDetailedDescription(Rules & r)46046 QString ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
46047 {
46048 	Q_UNUSED(r);
46049 
46050 	QString s=tr("Time constraint");s+="\n";
46051 
46052 	s+=tr("A teacher must begin his mornings early, respecting a maximum number of later arrivals, at second hour"); s+="\n";
46053 	s+=tr("(breaks and teacher not available not counted)");s+="\n";
46054 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
46055 	s+=tr("Teacher=%1").arg(this->teacherName); s+="\n";
46056 	s+=tr("Maximum number of arrivals at the second hour=%1").arg(this->maxBeginningsAtSecondHour);s+="\n";
46057 
46058 	if(!active){
46059 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
46060 		s+="\n";
46061 	}
46062 	if(!comments.isEmpty()){
46063 		s+=tr("Comments=%1").arg(comments);
46064 		s+="\n";
46065 	}
46066 
46067 	return s;
46068 }
46069 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)46070 double ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
46071 {
46072 	//considers the condition that the hours of subgroups begin as early as possible
46073 
46074 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
46075 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
46076 		c.teachersMatrixReady=true;
46077 		c.subgroupsMatrixReady=true;
46078 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
46079 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
46080 
46081 		c.changedForMatrixCalculation=false;
46082 	}
46083 
46084 	int conflTotal=0;
46085 
46086 	if(1){
46087 		int i=teacherIndex;
46088 
46089 		int nGapsFirstHour=0;
46090 		for(int j=0; j<r.nDaysPerWeek; j++){
46091 			if(j%2==1)
46092 				continue;
46093 
46094 			int k;
46095 			for(k=0; k<r.nHoursPerDay; k++)
46096 				if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k])
46097 					break;
46098 
46099 			bool firstHourOccupied=false;
46100 			if(k<r.nHoursPerDay)
46101 				if(teachersMatrix[i][j][k]>0)
46102 					firstHourOccupied=true;
46103 
46104 			bool dayOccupied=firstHourOccupied;
46105 
46106 			bool illegalGap=false;
46107 
46108 			if(!dayOccupied){
46109 				for(k++; k<r.nHoursPerDay; k++){
46110 					if(!breakDayHour[j][k] && !teacherNotAvailableDayHour[i][j][k]){
46111 						if(teachersMatrix[i][j][k]>0){
46112 							dayOccupied=true;
46113 							break;
46114 						}
46115 						else{
46116 							illegalGap=true;
46117 						}
46118 					}
46119 				}
46120 			}
46121 
46122 			if(dayOccupied && illegalGap){
46123 				if(conflictsString!=nullptr){
46124 					QString s=tr("Constraint teacher mornings early max %1 beginnings at second hour broken for teacher %2, on day %3,"
46125 					 " because the teacher has an illegal gap, increases conflicts total by %4")
46126 					 .arg(this->maxBeginningsAtSecondHour)
46127 					 .arg(r.internalTeachersList[i]->name)
46128 					 .arg(r.daysOfTheWeek[j])
46129 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
46130 
46131 					dl.append(s);
46132 					cl.append(1*weightPercentage/100);
46133 
46134 					*conflictsString+= s+"\n";
46135 
46136 					conflTotal+=1;
46137 				}
46138 
46139 				if(c.nPlacedActivities==r.nInternalActivities)
46140 					assert(0);
46141 			}
46142 
46143 			if(dayOccupied && !firstHourOccupied)
46144 				nGapsFirstHour++;
46145 		}
46146 
46147 		if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
46148 			if(conflictsString!=nullptr){
46149 				QString s=tr("Constraint teacher mornings early max %1 beginnings at second hour broken for teacher %2,"
46150 				 " because the teacher has too many arrivals at second hour, increases conflicts total by %3")
46151 				 .arg(this->maxBeginningsAtSecondHour)
46152 				 .arg(r.internalTeachersList[i]->name)
46153 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
46154 
46155 				dl.append(s);
46156 				cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
46157 
46158 				*conflictsString+= s+"\n";
46159 
46160 				conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
46161 			}
46162 
46163 			if(c.nPlacedActivities==r.nInternalActivities)
46164 				assert(0);
46165 		}
46166 	}
46167 
46168 	if(c.nPlacedActivities==r.nInternalActivities)
46169 		if(weightPercentage==100)    //might be broken for partial solutions
46170 			assert(conflTotal==0);
46171 	return weightPercentage/100 * conflTotal;
46172 }
46173 
isRelatedToActivity(Rules & r,Activity * a)46174 bool ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
46175 {
46176 	Q_UNUSED(r);
46177 	Q_UNUSED(a);
46178 
46179 	return false;
46180 }
46181 
isRelatedToTeacher(Teacher * t)46182 bool ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
46183 {
46184 	return this->teacherName==t->name;
46185 }
46186 
isRelatedToSubject(Subject * s)46187 bool ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
46188 {
46189 	Q_UNUSED(s);
46190 
46191 	return false;
46192 }
46193 
isRelatedToActivityTag(ActivityTag * s)46194 bool ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
46195 {
46196 	Q_UNUSED(s);
46197 
46198 	return false;
46199 }
46200 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)46201 bool ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
46202 {
46203 	Q_UNUSED(r);
46204 	Q_UNUSED(s);
46205 
46206 	return false;
46207 }
46208 
hasWrongDayOrHour(Rules & r)46209 bool ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
46210 {
46211 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
46212 		return true;
46213 
46214 	return false;
46215 }
46216 
canRepairWrongDayOrHour(Rules & r)46217 bool ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
46218 {
46219 	assert(hasWrongDayOrHour(r));
46220 
46221 	return true;
46222 }
46223 
repairWrongDayOrHour(Rules & r)46224 bool ConstraintTeacherMorningsEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
46225 {
46226 	assert(hasWrongDayOrHour(r));
46227 
46228 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
46229 		maxBeginningsAtSecondHour=r.nDaysPerWeek/2;
46230 
46231 	return true;
46232 }
46233 
46234 ////////////////////////////////////////////////////////////////////////////////////////////
46235 ////////////////////////////////////////////////////////////////////////////////////////////
46236 
ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour()46237 ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour()
46238 	: TimeConstraint()
46239 {
46240 	this->type = CONSTRAINT_STUDENTS_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
46241 }
46242 
ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour(double wp,int mBSH)46243 ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour(double wp, int mBSH)
46244 	: TimeConstraint(wp)
46245 {
46246 	this->type = CONSTRAINT_STUDENTS_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
46247 	this->maxBeginningsAtSecondHour=mBSH;
46248 }
46249 
computeInternalStructure(QWidget * parent,Rules & r)46250 bool ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
46251 {
46252 	Q_UNUSED(parent);
46253 	Q_UNUSED(r);
46254 
46255 	return true;
46256 }
46257 
hasInactiveActivities(Rules & r)46258 bool ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
46259 {
46260 	Q_UNUSED(r);
46261 	return false;
46262 }
46263 
getXmlDescription(Rules & r)46264 QString ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
46265 {
46266 	Q_UNUSED(r);
46267 
46268 	QString s="<ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour>\n";
46269 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
46270 	s+="	<Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
46271 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
46272 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
46273 	s+="</ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour>\n";
46274 	return s;
46275 }
46276 
getDescription(Rules & r)46277 QString ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
46278 {
46279 	Q_UNUSED(r);
46280 
46281 	QString begin=QString("");
46282 	if(!active)
46283 		begin="X - ";
46284 
46285 	QString end=QString("");
46286 	if(!comments.isEmpty())
46287 		end=", "+tr("C: %1", "Comments").arg(comments);
46288 
46289 	QString s;
46290 	s+=tr("Students must begin mornings early, respecting maximum %1 arrivals at second hour")
46291 	 .arg(this->maxBeginningsAtSecondHour);
46292 	s+=", ";
46293 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));
46294 
46295 	return begin+s+end;
46296 }
46297 
getDetailedDescription(Rules & r)46298 QString ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
46299 {
46300 	Q_UNUSED(r);
46301 
46302 	QString s=tr("Time constraint");s+="\n";
46303 	s+=tr("All students must begin the mornings early, respecting maximum %1 later arrivals, at second hour")
46304 	 .arg(this->maxBeginningsAtSecondHour);s+="\n";
46305 	s+=tr("(breaks and students set not available not counted)");s+="\n";
46306 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
46307 
46308 	if(!active){
46309 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
46310 		s+="\n";
46311 	}
46312 	if(!comments.isEmpty()){
46313 		s+=tr("Comments=%1").arg(comments);
46314 		s+="\n";
46315 	}
46316 
46317 	return s;
46318 }
46319 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)46320 double ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
46321 {
46322 	//considers the condition that the hours of subgroups begin as early as possible
46323 
46324 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
46325 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
46326 		c.teachersMatrixReady=true;
46327 		c.subgroupsMatrixReady=true;
46328 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
46329 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
46330 
46331 		c.changedForMatrixCalculation=false;
46332 	}
46333 
46334 	int conflTotal=0;
46335 
46336 	for(int i=0; i<r.nInternalSubgroups; i++){
46337 		int nGapsFirstHour=0;
46338 		for(int j=0; j<r.nDaysPerWeek; j++){
46339 			if(j%2==1)
46340 				continue;
46341 
46342 			int k;
46343 			for(k=0; k<r.nHoursPerDay; k++)
46344 				if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k])
46345 					break;
46346 
46347 			bool firstHourOccupied=false;
46348 			if(k<r.nHoursPerDay)
46349 				if(subgroupsMatrix[i][j][k]>0)
46350 					firstHourOccupied=true;
46351 
46352 			bool dayOccupied=firstHourOccupied;
46353 
46354 			bool illegalGap=false;
46355 
46356 			if(!dayOccupied){
46357 				for(k++; k<r.nHoursPerDay; k++){
46358 					if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
46359 						if(subgroupsMatrix[i][j][k]>0){
46360 							dayOccupied=true;
46361 							break;
46362 						}
46363 						else{
46364 							illegalGap=true;
46365 						}
46366 					}
46367 				}
46368 			}
46369 
46370 			if(dayOccupied && illegalGap){
46371 				if(conflictsString!=nullptr){
46372 					QString s=tr("Constraint students mornings early max %1 beginnings at second hour broken for subgroup %2, on day %3,"
46373 					 " because students have an illegal gap, increases conflicts total by %4")
46374 					 .arg(this->maxBeginningsAtSecondHour)
46375 					 .arg(r.internalSubgroupsList[i]->name)
46376 					 .arg(r.daysOfTheWeek[j])
46377 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
46378 
46379 					dl.append(s);
46380 					cl.append(1*weightPercentage/100);
46381 
46382 					*conflictsString+= s+"\n";
46383 
46384 					conflTotal+=1;
46385 				}
46386 
46387 				if(c.nPlacedActivities==r.nInternalActivities){
46388 					assert(0);
46389 				}
46390 			}
46391 
46392 			if(dayOccupied && !firstHourOccupied)
46393 				nGapsFirstHour++;
46394 		}
46395 
46396 		if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
46397 			if(conflictsString!=nullptr){
46398 				QString s=tr("Constraint students mornings early max %1 beginnings at second hour broken for subgroup %2,"
46399 				 " because students have too many arrivals at second hour, increases conflicts total by %3")
46400 				 .arg(this->maxBeginningsAtSecondHour)
46401 				 .arg(r.internalSubgroupsList[i]->name)
46402 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
46403 
46404 				dl.append(s);
46405 				cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
46406 
46407 				*conflictsString+= s+"\n";
46408 
46409 				conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
46410 			}
46411 
46412 			if(c.nPlacedActivities==r.nInternalActivities){
46413 				assert(0);
46414 			}
46415 		}
46416 	}
46417 
46418 	if(c.nPlacedActivities==r.nInternalActivities)
46419 		if(weightPercentage==100)    //might be broken for partial solutions
46420 			assert(conflTotal==0);
46421 	return weightPercentage/100 * conflTotal;
46422 }
46423 
isRelatedToActivity(Rules & r,Activity * a)46424 bool ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
46425 {
46426 	Q_UNUSED(r);
46427 	Q_UNUSED(a);
46428 
46429 	return false;
46430 }
46431 
isRelatedToTeacher(Teacher * t)46432 bool ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
46433 {
46434 	Q_UNUSED(t);
46435 
46436 	return false;
46437 }
46438 
isRelatedToSubject(Subject * s)46439 bool ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
46440 {
46441 	Q_UNUSED(s);
46442 
46443 	return false;
46444 }
46445 
isRelatedToActivityTag(ActivityTag * s)46446 bool ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
46447 {
46448 	Q_UNUSED(s);
46449 
46450 	return false;
46451 }
46452 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)46453 bool ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
46454 {
46455 	Q_UNUSED(r);
46456 	Q_UNUSED(s);
46457 
46458 	return true;
46459 }
46460 
hasWrongDayOrHour(Rules & r)46461 bool ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
46462 {
46463 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
46464 		return true;
46465 
46466 	return false;
46467 }
46468 
canRepairWrongDayOrHour(Rules & r)46469 bool ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
46470 {
46471 	assert(hasWrongDayOrHour(r));
46472 
46473 	return true;
46474 }
46475 
repairWrongDayOrHour(Rules & r)46476 bool ConstraintStudentsMorningsEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
46477 {
46478 	assert(hasWrongDayOrHour(r));
46479 
46480 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
46481 		maxBeginningsAtSecondHour=r.nDaysPerWeek/2;
46482 
46483 	return true;
46484 }
46485 
46486 ////////////////////////////////////////////////////////////////////////////////////////////
46487 ////////////////////////////////////////////////////////////////////////////////////////////
46488 
ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour()46489 ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour()
46490 	: TimeConstraint()
46491 {
46492 	this->type = CONSTRAINT_STUDENTS_SET_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
46493 }
46494 
ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour(double wp,int mBSH,const QString & students)46495 ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour(double wp, int mBSH, const QString& students)
46496 	: TimeConstraint(wp)
46497 {
46498 	this->type = CONSTRAINT_STUDENTS_SET_MORNINGS_EARLY_MAX_BEGINNINGS_AT_SECOND_HOUR;
46499 	this->students=students;
46500 	this->maxBeginningsAtSecondHour=mBSH;
46501 }
46502 
computeInternalStructure(QWidget * parent,Rules & r)46503 bool ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::computeInternalStructure(QWidget* parent, Rules& r)
46504 {
46505 	//StudentsSet* ss=r.searchAugmentedStudentsSet(this->students);
46506 	StudentsSet* ss=r.studentsHash.value(students, nullptr);
46507 
46508 	if(ss==nullptr){
46509 		TimeConstraintIrreconcilableMessage::warning(parent, tr("FET warning"),
46510 		 tr("Constraint students set mornings early is wrong because it refers to inexistent students set."
46511 		 " Please correct it (removing it might be a solution). Please report potential bug. Constraint is:\n%1").arg(this->getDetailedDescription(r)));
46512 
46513 		return false;
46514 	}
46515 
46516 	assert(ss!=nullptr);
46517 
46518 	populateInternalSubgroupsList(r, ss, this->iSubgroupsList);
46519 	/*this->iSubgroupsList.clear();
46520 	if(ss->type==STUDENTS_SUBGROUP){
46521 		int tmp;
46522 		tmp=((StudentsSubgroup*)ss)->indexInInternalSubgroupsList;
46523 		assert(tmp>=0);
46524 		assert(tmp<r.nInternalSubgroups);
46525 		if(!this->iSubgroupsList.contains(tmp))
46526 			this->iSubgroupsList.append(tmp);
46527 	}
46528 	else if(ss->type==STUDENTS_GROUP){
46529 		StudentsGroup* stg=(StudentsGroup*)ss;
46530 		for(int i=0; i<stg->subgroupsList.size(); i++){
46531 			StudentsSubgroup* sts=stg->subgroupsList[i];
46532 			int tmp;
46533 			tmp=sts->indexInInternalSubgroupsList;
46534 			assert(tmp>=0);
46535 			assert(tmp<r.nInternalSubgroups);
46536 			if(!this->iSubgroupsList.contains(tmp))
46537 				this->iSubgroupsList.append(tmp);
46538 		}
46539 	}
46540 	else if(ss->type==STUDENTS_YEAR){
46541 		StudentsYear* sty=(StudentsYear*)ss;
46542 		for(int i=0; i<sty->groupsList.size(); i++){
46543 			StudentsGroup* stg=sty->groupsList[i];
46544 			for(int j=0; j<stg->subgroupsList.size(); j++){
46545 				StudentsSubgroup* sts=stg->subgroupsList[j];
46546 				int tmp;
46547 				tmp=sts->indexInInternalSubgroupsList;
46548 				assert(tmp>=0);
46549 				assert(tmp<r.nInternalSubgroups);
46550 				if(!this->iSubgroupsList.contains(tmp))
46551 					this->iSubgroupsList.append(tmp);
46552 			}
46553 		}
46554 	}
46555 	else
46556 		assert(0);*/
46557 	return true;
46558 }
46559 
hasInactiveActivities(Rules & r)46560 bool ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::hasInactiveActivities(Rules& r)
46561 {
46562 	Q_UNUSED(r);
46563 	return false;
46564 }
46565 
getXmlDescription(Rules & r)46566 QString ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::getXmlDescription(Rules& r)
46567 {
46568 	Q_UNUSED(r);
46569 
46570 	QString s="<ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour>\n";
46571 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
46572 	s+="	<Max_Beginnings_At_Second_Hour>"+CustomFETString::number(this->maxBeginningsAtSecondHour)+"</Max_Beginnings_At_Second_Hour>\n";
46573 	s+="	<Students>"+protect(this->students)+"</Students>\n";
46574 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
46575 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
46576 	s+="</ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour>\n";
46577 	return s;
46578 }
46579 
getDescription(Rules & r)46580 QString ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::getDescription(Rules& r)
46581 {
46582 	Q_UNUSED(r);
46583 
46584 	QString begin=QString("");
46585 	if(!active)
46586 		begin="X - ";
46587 
46588 	QString end=QString("");
46589 	if(!comments.isEmpty())
46590 		end=", "+tr("C: %1", "Comments").arg(comments);
46591 
46592 	QString s;
46593 
46594 	s+=tr("Students set must begin the mornings early, respecting maximum %1 arrivals at second hour")
46595 	 .arg(this->maxBeginningsAtSecondHour); s+=", ";
46596 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
46597 	s+=tr("St:%1", "Students set").arg(this->students);
46598 
46599 	return begin+s+end;
46600 }
46601 
getDetailedDescription(Rules & r)46602 QString ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::getDetailedDescription(Rules& r)
46603 {
46604 	Q_UNUSED(r);
46605 
46606 	QString s=tr("Time constraint");s+="\n";
46607 
46608 	s+=tr("A students set must begin the mornings early, respecting a maximum number of later arrivals, at second hour"); s+="\n";
46609 	s+=tr("(breaks and students set not available not counted)");s+="\n";
46610 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
46611 	s+=tr("Students set=%1").arg(this->students); s+="\n";
46612 	s+=tr("Maximum number of arrivals at the second hour=%1").arg(this->maxBeginningsAtSecondHour);s+="\n";
46613 
46614 	if(!active){
46615 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
46616 		s+="\n";
46617 	}
46618 	if(!comments.isEmpty()){
46619 		s+=tr("Comments=%1").arg(comments);
46620 		s+="\n";
46621 	}
46622 
46623 	return s;
46624 }
46625 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)46626 double ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>&dl, FakeString* conflictsString)
46627 {
46628 	//considers the condition that the hours of subgroups begin as early as possible
46629 
46630 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
46631 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
46632 		c.teachersMatrixReady=true;
46633 		c.subgroupsMatrixReady=true;
46634 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
46635 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
46636 
46637 		c.changedForMatrixCalculation=false;
46638 	}
46639 
46640 	int conflTotal=0;
46641 
46642 	for(int i : qAsConst(this->iSubgroupsList)){
46643 		int nGapsFirstHour=0;
46644 		for(int j=0; j<r.nDaysPerWeek; j++){
46645 			if(j%2==1)
46646 				continue;
46647 
46648 			int k;
46649 			for(k=0; k<r.nHoursPerDay; k++)
46650 				if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k])
46651 					break;
46652 
46653 			bool firstHourOccupied=false;
46654 			if(k<r.nHoursPerDay)
46655 				if(subgroupsMatrix[i][j][k]>0)
46656 					firstHourOccupied=true;
46657 
46658 			bool dayOccupied=firstHourOccupied;
46659 
46660 			bool illegalGap=false;
46661 
46662 			if(!dayOccupied){
46663 				for(k++; k<r.nHoursPerDay; k++){
46664 					if(!breakDayHour[j][k] && !subgroupNotAvailableDayHour[i][j][k]){
46665 						if(subgroupsMatrix[i][j][k]>0){
46666 							dayOccupied=true;
46667 							break;
46668 						}
46669 						else{
46670 							illegalGap=true;
46671 						}
46672 					}
46673 				}
46674 			}
46675 
46676 			if(dayOccupied && illegalGap){
46677 				if(conflictsString!=nullptr){
46678 					QString s=tr("Constraint students set mornings early max %1 beginnings at second hour broken for subgroup %2, on day %3,"
46679 					 " because students have an illegal gap, increases conflicts total by %4")
46680 					 .arg(this->maxBeginningsAtSecondHour)
46681 					 .arg(r.internalSubgroupsList[i]->name)
46682 					 .arg(r.daysOfTheWeek[j])
46683 					 .arg(CustomFETString::numberPlusTwoDigitsPrecision(1*weightPercentage/100));
46684 
46685 					dl.append(s);
46686 					cl.append(1*weightPercentage/100);
46687 
46688 					*conflictsString+= s+"\n";
46689 
46690 					conflTotal+=1;
46691 				}
46692 
46693 				if(c.nPlacedActivities==r.nInternalActivities)
46694 					assert(0);
46695 			}
46696 
46697 			if(dayOccupied && !firstHourOccupied)
46698 				nGapsFirstHour++;
46699 		}
46700 
46701 		if(nGapsFirstHour>this->maxBeginningsAtSecondHour){
46702 			if(conflictsString!=nullptr){
46703 				QString s=tr("Constraint students set mornings early max %1 beginnings at second hour broken for subgroup %2,"
46704 				 " because students have too many arrivals at second hour, increases conflicts total by %3")
46705 				 .arg(this->maxBeginningsAtSecondHour)
46706 				 .arg(r.internalSubgroupsList[i]->name)
46707 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100));
46708 
46709 				dl.append(s);
46710 				cl.append((nGapsFirstHour-this->maxBeginningsAtSecondHour)*weightPercentage/100);
46711 
46712 				*conflictsString+= s+"\n";
46713 
46714 				conflTotal+=(nGapsFirstHour-this->maxBeginningsAtSecondHour);
46715 			}
46716 
46717 			if(c.nPlacedActivities==r.nInternalActivities)
46718 				assert(0);
46719 		}
46720 	}
46721 
46722 	if(c.nPlacedActivities==r.nInternalActivities)
46723 		if(weightPercentage==100)    //might be broken for partial solutions
46724 			assert(conflTotal==0);
46725 	return weightPercentage/100 * conflTotal;
46726 }
46727 
isRelatedToActivity(Rules & r,Activity * a)46728 bool ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToActivity(Rules& r, Activity* a)
46729 {
46730 	Q_UNUSED(r);
46731 	Q_UNUSED(a);
46732 
46733 	return false;
46734 }
46735 
isRelatedToTeacher(Teacher * t)46736 bool ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToTeacher(Teacher* t)
46737 {
46738 	Q_UNUSED(t);
46739 
46740 	return false;
46741 }
46742 
isRelatedToSubject(Subject * s)46743 bool ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToSubject(Subject* s)
46744 {
46745 	Q_UNUSED(s);
46746 
46747 	return false;
46748 }
46749 
isRelatedToActivityTag(ActivityTag * s)46750 bool ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToActivityTag(ActivityTag* s)
46751 {
46752 	Q_UNUSED(s);
46753 
46754 	return false;
46755 }
46756 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)46757 bool ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
46758 {
46759 	return r.setsShareStudents(this->students, s->name);
46760 }
46761 
hasWrongDayOrHour(Rules & r)46762 bool ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::hasWrongDayOrHour(Rules& r)
46763 {
46764 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
46765 		return true;
46766 
46767 	return false;
46768 }
46769 
canRepairWrongDayOrHour(Rules & r)46770 bool ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::canRepairWrongDayOrHour(Rules& r)
46771 {
46772 	assert(hasWrongDayOrHour(r));
46773 
46774 	return true;
46775 }
46776 
repairWrongDayOrHour(Rules & r)46777 bool ConstraintStudentsSetMorningsEarlyMaxBeginningsAtSecondHour::repairWrongDayOrHour(Rules& r)
46778 {
46779 	assert(hasWrongDayOrHour(r));
46780 
46781 	if(maxBeginningsAtSecondHour>r.nDaysPerWeek/2)
46782 		maxBeginningsAtSecondHour=r.nDaysPerWeek/2;
46783 
46784 	return true;
46785 }
46786 
46787 ///////////////////////////////////////////////////////////////////////////////////////////
46788 ///////////////////////////////////////////////////////////////////////////////////////////
46789 
ConstraintTeacherMaxThreeConsecutiveDays()46790 ConstraintTeacherMaxThreeConsecutiveDays::ConstraintTeacherMaxThreeConsecutiveDays()
46791 	: TimeConstraint()
46792 {
46793 	this->type=CONSTRAINT_TEACHER_MAX_THREE_CONSECUTIVE_DAYS;
46794 }
46795 
ConstraintTeacherMaxThreeConsecutiveDays(double wp,bool ae,const QString & tn)46796 ConstraintTeacherMaxThreeConsecutiveDays::ConstraintTeacherMaxThreeConsecutiveDays(double wp, bool ae, const QString& tn)
46797 	 : TimeConstraint(wp)
46798 {
46799 	this->teacherName = tn;
46800 	this->allowAMAMException=ae;
46801 	this->type=CONSTRAINT_TEACHER_MAX_THREE_CONSECUTIVE_DAYS;
46802 }
46803 
computeInternalStructure(QWidget * parent,Rules & r)46804 bool ConstraintTeacherMaxThreeConsecutiveDays::computeInternalStructure(QWidget* parent, Rules& r)
46805 {
46806 	Q_UNUSED(parent);
46807 
46808 	//this->teacher_ID=r.searchTeacher(this->teacherName);
46809 	teacher_ID=r.teachersHash.value(teacherName, -1);
46810 	assert(this->teacher_ID>=0);
46811 	return true;
46812 }
46813 
hasInactiveActivities(Rules & r)46814 bool ConstraintTeacherMaxThreeConsecutiveDays::hasInactiveActivities(Rules& r)
46815 {
46816 	Q_UNUSED(r);
46817 	return false;
46818 }
46819 
getXmlDescription(Rules & r)46820 QString ConstraintTeacherMaxThreeConsecutiveDays::getXmlDescription(Rules& r)
46821 {
46822 	Q_UNUSED(r);
46823 
46824 	QString s="<ConstraintTeacherMaxThreeConsecutiveDays>\n";
46825 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
46826 	s+="	<Teacher_Name>"+protect(this->teacherName)+"</Teacher_Name>\n";
46827 	s+="	<Allow_Afternoon_Morning_Afternoon_Morning_Exception>"+trueFalse(this->allowAMAMException)
46828 	 +"</Allow_Afternoon_Morning_Afternoon_Morning_Exception>\n";
46829 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
46830 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
46831 	s+="</ConstraintTeacherMaxThreeConsecutiveDays>\n";
46832 	return s;
46833 }
46834 
getDescription(Rules & r)46835 QString ConstraintTeacherMaxThreeConsecutiveDays::getDescription(Rules& r)
46836 {
46837 	Q_UNUSED(r);
46838 
46839 	QString begin=QString("");
46840 	if(!active)
46841 		begin="X - ";
46842 
46843 	QString end=QString("");
46844 	if(!comments.isEmpty())
46845 		end=", "+tr("C: %1", "Comments").arg(comments);
46846 
46847 	QString s=tr("Teacher max three consecutive days");s+=", ";
46848 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
46849 	s+=tr("T:%1", "Teacher").arg(this->teacherName);s+=", ";
46850 	s+=tr("A-AMAM-E:%1", "Allow afternoon-morning-afternoon-morning exception").arg(yesNoTranslated(this->allowAMAMException));
46851 
46852 	return begin+s+end;
46853 }
46854 
getDetailedDescription(Rules & r)46855 QString ConstraintTeacherMaxThreeConsecutiveDays::getDetailedDescription(Rules& r)
46856 {
46857 	Q_UNUSED(r);
46858 
46859 	QString s=tr("Time constraint");s+="\n";
46860 	s+=tr("A teacher must respect a maximum of three consecutive working days");s+="\n";
46861 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
46862 	s+=tr("Teacher=%1").arg(this->teacherName);s+="\n";
46863 	s+=tr("Allow afternoon-morning-afternoon-morning exception=%1").arg(yesNoTranslated(this->allowAMAMException));s+="\n";
46864 
46865 	if(!active){
46866 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
46867 		s+="\n";
46868 	}
46869 	if(!comments.isEmpty()){
46870 		s+=tr("Comments=%1").arg(comments);
46871 		s+="\n";
46872 	}
46873 
46874 	return s;
46875 }
46876 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)46877 double ConstraintTeacherMaxThreeConsecutiveDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
46878 {
46879 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
46880 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
46881 		c.teachersMatrixReady=true;
46882 		c.subgroupsMatrixReady=true;
46883 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
46884 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
46885 
46886 		c.changedForMatrixCalculation=false;
46887 	}
46888 
46889 	int nbroken=0;
46890 
46891 	Matrix1D<bool> tm;
46892 	tm.resize(r.nDaysPerWeek);
46893 	//Teacher* tch=r.internalTeachersList[this->teacher_ID];
46894 	for(int d=0; d<r.nDaysPerWeek; d++){
46895 		tm[d]=false;
46896 		for(int h=0; h<r.nHoursPerDay; h++)
46897 			if(teachersMatrix[this->teacher_ID][d][h]>0){
46898 				tm[d]=true;
46899 				break;
46900 			}
46901 	}
46902 
46903 	for(int d=0; d<r.nDaysPerWeek; d++){
46904 		if(d%2==1 && this->allowAMAMException)
46905 			continue;
46906 		if(d+3>=r.nDaysPerWeek)
46907 			continue;
46908 		if(tm[d] && tm[d+1] && tm[d+2] && tm[d+3]){
46909 			nbroken++;
46910 		}
46911 	}
46912 
46913 	if(conflictsString!=nullptr){
46914 		if(nbroken>0){
46915 			QString s= tr("Time constraint teacher max three consecutive days broken for teacher: %1.")
46916 			 .arg(r.internalTeachersList[this->teacher_ID]->name);
46917 			s += QString(" ")+tr("This increases the conflicts total by %1")
46918 			 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
46919 
46920 			dl.append(s);
46921 			cl.append(nbroken*weightPercentage/100);
46922 
46923 			*conflictsString += s+"\n";
46924 		}
46925 	}
46926 
46927 	if(weightPercentage==100)
46928 		assert(nbroken==0);
46929 	return weightPercentage/100 * nbroken;
46930 }
46931 
isRelatedToActivity(Rules & r,Activity * a)46932 bool ConstraintTeacherMaxThreeConsecutiveDays::isRelatedToActivity(Rules& r, Activity* a)
46933 {
46934 	Q_UNUSED(r);
46935 	Q_UNUSED(a);
46936 
46937 	return false;
46938 }
46939 
isRelatedToTeacher(Teacher * t)46940 bool ConstraintTeacherMaxThreeConsecutiveDays::isRelatedToTeacher(Teacher* t)
46941 {
46942 	if(this->teacherName==t->name)
46943 		return true;
46944 	return false;
46945 }
46946 
isRelatedToSubject(Subject * s)46947 bool ConstraintTeacherMaxThreeConsecutiveDays::isRelatedToSubject(Subject* s)
46948 {
46949 	Q_UNUSED(s);
46950 
46951 	return false;
46952 }
46953 
isRelatedToActivityTag(ActivityTag * s)46954 bool ConstraintTeacherMaxThreeConsecutiveDays::isRelatedToActivityTag(ActivityTag* s)
46955 {
46956 	Q_UNUSED(s);
46957 
46958 	return false;
46959 }
46960 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)46961 bool ConstraintTeacherMaxThreeConsecutiveDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
46962 {
46963 	Q_UNUSED(r);
46964 	Q_UNUSED(s);
46965 
46966 	return false;
46967 }
46968 
hasWrongDayOrHour(Rules & r)46969 bool ConstraintTeacherMaxThreeConsecutiveDays::hasWrongDayOrHour(Rules& r)
46970 {
46971 	Q_UNUSED(r);
46972 	return false;
46973 }
46974 
canRepairWrongDayOrHour(Rules & r)46975 bool ConstraintTeacherMaxThreeConsecutiveDays::canRepairWrongDayOrHour(Rules& r)
46976 {
46977 	Q_UNUSED(r);
46978 	assert(0);
46979 
46980 	return true;
46981 }
46982 
repairWrongDayOrHour(Rules & r)46983 bool ConstraintTeacherMaxThreeConsecutiveDays::repairWrongDayOrHour(Rules& r)
46984 {
46985 	Q_UNUSED(r);
46986 	assert(0); //should check hasWrongDayOrHour, firstly
46987 
46988 	return true;
46989 }
46990 
46991 ///////////////////////////////////////////////////////////////////////////////////////////
46992 ///////////////////////////////////////////////////////////////////////////////////////////
46993 
ConstraintTeachersMaxThreeConsecutiveDays()46994 ConstraintTeachersMaxThreeConsecutiveDays::ConstraintTeachersMaxThreeConsecutiveDays()
46995 	: TimeConstraint()
46996 {
46997 	this->type=CONSTRAINT_TEACHERS_MAX_THREE_CONSECUTIVE_DAYS;
46998 }
46999 
ConstraintTeachersMaxThreeConsecutiveDays(double wp,bool ae)47000 ConstraintTeachersMaxThreeConsecutiveDays::ConstraintTeachersMaxThreeConsecutiveDays(double wp, bool ae)
47001 	 : TimeConstraint(wp)
47002 {
47003 	this->allowAMAMException=ae;
47004 	this->type=CONSTRAINT_TEACHERS_MAX_THREE_CONSECUTIVE_DAYS;
47005 }
47006 
computeInternalStructure(QWidget * parent,Rules & r)47007 bool ConstraintTeachersMaxThreeConsecutiveDays::computeInternalStructure(QWidget* parent, Rules& r)
47008 {
47009 	Q_UNUSED(parent);
47010 	Q_UNUSED(r);
47011 
47012 	return true;
47013 }
47014 
hasInactiveActivities(Rules & r)47015 bool ConstraintTeachersMaxThreeConsecutiveDays::hasInactiveActivities(Rules& r)
47016 {
47017 	Q_UNUSED(r);
47018 	return false;
47019 }
47020 
getXmlDescription(Rules & r)47021 QString ConstraintTeachersMaxThreeConsecutiveDays::getXmlDescription(Rules& r)
47022 {
47023 	Q_UNUSED(r);
47024 
47025 	QString s="<ConstraintTeachersMaxThreeConsecutiveDays>\n";
47026 	s+="	<Weight_Percentage>"+CustomFETString::number(this->weightPercentage)+"</Weight_Percentage>\n";
47027 	s+="	<Allow_Afternoon_Morning_Afternoon_Morning_Exception>"+trueFalse(this->allowAMAMException)
47028 	 +"</Allow_Afternoon_Morning_Afternoon_Morning_Exception>\n";
47029 	s+="	<Active>"+trueFalse(active)+"</Active>\n";
47030 	s+="	<Comments>"+protect(comments)+"</Comments>\n";
47031 	s+="</ConstraintTeachersMaxThreeConsecutiveDays>\n";
47032 	return s;
47033 }
47034 
getDescription(Rules & r)47035 QString ConstraintTeachersMaxThreeConsecutiveDays::getDescription(Rules& r)
47036 {
47037 	Q_UNUSED(r);
47038 
47039 	QString begin=QString("");
47040 	if(!active)
47041 		begin="X - ";
47042 
47043 	QString end=QString("");
47044 	if(!comments.isEmpty())
47045 		end=", "+tr("C: %1", "Comments").arg(comments);
47046 
47047 	QString s=tr("Teachers max three consecutive days");s+=", ";
47048 	s+=tr("WP:%1%", "Weight percentage").arg(CustomFETString::number(this->weightPercentage));s+=", ";
47049 	s+=tr("A-AMAM-E:%1", "Allow afternoon-morning-afternoon-morning exception").arg(yesNoTranslated(this->allowAMAMException));
47050 
47051 	return begin+s+end;
47052 }
47053 
getDetailedDescription(Rules & r)47054 QString ConstraintTeachersMaxThreeConsecutiveDays::getDetailedDescription(Rules& r)
47055 {
47056 	Q_UNUSED(r);
47057 
47058 	QString s=tr("Time constraint");s+="\n";
47059 	s+=tr("All teachers must respect a maximum of three consecutive working days");s+="\n";
47060 	s+=tr("Weight (percentage)=%1%").arg(CustomFETString::number(this->weightPercentage));s+="\n";
47061 	s+=tr("Allow afternoon-morning-afternoon-morning exception=%1").arg(yesNoTranslated(this->allowAMAMException));s+="\n";
47062 
47063 	if(!active){
47064 		s+=tr("Active=%1", "Refers to a constraint").arg(yesNoTranslated(active));
47065 		s+="\n";
47066 	}
47067 	if(!comments.isEmpty()){
47068 		s+=tr("Comments=%1").arg(comments);
47069 		s+="\n";
47070 	}
47071 
47072 	return s;
47073 }
47074 
fitness(Solution & c,Rules & r,QList<double> & cl,QList<QString> & dl,FakeString * conflictsString)47075 double ConstraintTeachersMaxThreeConsecutiveDays::fitness(Solution& c, Rules& r, QList<double>& cl, QList<QString>& dl, FakeString* conflictsString)
47076 {
47077 	//if the matrices subgroupsMatrix and teachersMatrix are already calculated, do not calculate them again!
47078 	if(!c.teachersMatrixReady || !c.subgroupsMatrixReady){
47079 		c.teachersMatrixReady=true;
47080 		c.subgroupsMatrixReady=true;
47081 		subgroups_conflicts = c.getSubgroupsMatrix(r, subgroupsMatrix);
47082 		teachers_conflicts = c.getTeachersMatrix(r, teachersMatrix);
47083 
47084 		c.changedForMatrixCalculation=false;
47085 	}
47086 
47087 	int nbroken=0;
47088 
47089 	Matrix1D<bool> tm;
47090 	tm.resize(r.nDaysPerWeek);
47091 	for(int t=0; t<r.nInternalTeachers; t++){
47092 		//Teacher* tch=r.internalTeachersList[this->teacher_ID];
47093 		for(int d=0; d<r.nDaysPerWeek; d++){
47094 			tm[d]=false;
47095 			for(int h=0; h<r.nHoursPerDay; h++)
47096 				if(teachersMatrix[t][d][h]>0){
47097 					tm[d]=true;
47098 					break;
47099 				}
47100 		}
47101 
47102 		int partialnbroken=0;
47103 
47104 		for(int d=0; d<r.nDaysPerWeek; d++){
47105 			if(d%2==1 && this->allowAMAMException)
47106 				continue;
47107 			if(d+3>=r.nDaysPerWeek)
47108 				continue;
47109 			if(tm[d] && tm[d+1] && tm[d+2] && tm[d+3]){
47110 				nbroken++;
47111 				partialnbroken++;
47112 			}
47113 		}
47114 
47115 		if(conflictsString!=nullptr){
47116 			if(partialnbroken>0){
47117 				QString s= tr("Time constraint teacher max three consecutive days broken for teacher: %1.")
47118 				 .arg(r.internalTeachersList[t]->name);
47119 				s += QString(" ")+tr("This increases the conflicts total by %1")
47120 				 .arg(CustomFETString::numberPlusTwoDigitsPrecision(nbroken*weightPercentage/100));
47121 
47122 				dl.append(s);
47123 				cl.append(partialnbroken*weightPercentage/100);
47124 
47125 				*conflictsString += s+"\n";
47126 			}
47127 		}
47128 	}
47129 
47130 	if(weightPercentage==100)
47131 		assert(nbroken==0);
47132 	return weightPercentage/100 * nbroken;
47133 }
47134 
isRelatedToActivity(Rules & r,Activity * a)47135 bool ConstraintTeachersMaxThreeConsecutiveDays::isRelatedToActivity(Rules& r, Activity* a)
47136 {
47137 	Q_UNUSED(r);
47138 	Q_UNUSED(a);
47139 
47140 	return false;
47141 }
47142 
isRelatedToTeacher(Teacher * t)47143 bool ConstraintTeachersMaxThreeConsecutiveDays::isRelatedToTeacher(Teacher* t)
47144 {
47145 	Q_UNUSED(t);
47146 
47147 	return false;
47148 }
47149 
isRelatedToSubject(Subject * s)47150 bool ConstraintTeachersMaxThreeConsecutiveDays::isRelatedToSubject(Subject* s)
47151 {
47152 	Q_UNUSED(s);
47153 
47154 	return false;
47155 }
47156 
isRelatedToActivityTag(ActivityTag * s)47157 bool ConstraintTeachersMaxThreeConsecutiveDays::isRelatedToActivityTag(ActivityTag* s)
47158 {
47159 	Q_UNUSED(s);
47160 
47161 	return false;
47162 }
47163 
isRelatedToStudentsSet(Rules & r,StudentsSet * s)47164 bool ConstraintTeachersMaxThreeConsecutiveDays::isRelatedToStudentsSet(Rules& r, StudentsSet* s)
47165 {
47166 	Q_UNUSED(r);
47167 	Q_UNUSED(s);
47168 
47169 	return false;
47170 }
47171 
hasWrongDayOrHour(Rules & r)47172 bool ConstraintTeachersMaxThreeConsecutiveDays::hasWrongDayOrHour(Rules& r)
47173 {
47174 	Q_UNUSED(r);
47175 	return false;
47176 }
47177 
canRepairWrongDayOrHour(Rules & r)47178 bool ConstraintTeachersMaxThreeConsecutiveDays::canRepairWrongDayOrHour(Rules& r)
47179 {
47180 	Q_UNUSED(r);
47181 	assert(0);
47182 
47183 	return true;
47184 }
47185 
repairWrongDayOrHour(Rules & r)47186 bool ConstraintTeachersMaxThreeConsecutiveDays::repairWrongDayOrHour(Rules& r)
47187 {
47188 	Q_UNUSED(r);
47189 	assert(0); //should check hasWrongDayOrHour, firstly
47190 
47191 	return true;
47192 }
47193