1 /*
2 File generate.cpp
3 */
4 
5 /***************************************************************************
6                           generate.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 /*Note 2018-07-28: The code for students max span per day, students early max beginnings at second hour, students/teachers max gaps per day/week,
23 students/teachers min/max hours daily can and should be theoretically corrected. But it is very risky. Many examples and variants should be tested.
24 See the directory doc/algorithm/2018-07-28-should-improve-theoretically for a better generate file, but which behaves much worse on at least a file,
25 examples/Romania/Pedagogic-High-School-Tg-Mures/2007-2008_sem1-d-test-students-max-span-per-day.fet (because of the new code in
26 students max span per day).*/
27 
28 /*
29 2020-12-05:
30 Old comment below, because Qt 6 combined QVector and QList into QList and the resulting class is working at least as fast as a Qt 5 QList,
31 in many cases ~10% faster or even more.
32 
33 Note: Since FET-5.44.0 the random number generator changed, and the seed has 2x3=6 components instead of only 2. But probably
34 the problem below still remains.
35 Note: TODO item #398
36 (In fact this is an anti-TODO):
37 
38 Qt documentation recommends the use of QVector instead of QList. I tried on many files and indeed on many of them it improves the speed with even 10%.
39 But for some files, among which some from the Economics Faculty of Timisoara, it is much slower, even with 20% slower.
40 
41 The file examples/Romania/Faculty-Econ-Timisoara-difficult/2007-2008-sem-2/Econ-Timisoara.fet :
42 	starting with random seed X=1234, Y=1234 (ending with random seed X=2061125487, Y=844641195 for FET-5.42.0 official)
43 		it takes 18 minutes 51 seconds with QList
44 		it takes 23 minutes 53 seconds with QVector
45 The file examples/Romania/Faculty-Econ-Timisoara-difficult/2009-2010-sem-1/Econ-v0.8.fet :
46 	starting with random seed X=1234, Y=1234 (ending with random seed X=48863282, Y=367996316 for FET-5.42.0 official)
47 		it takes 11 minutes 52 seconds with QList
48 		it takes 13 minutes 17 seconds with QVector
49 
50 So at least for now FET will use QList.*/
51 
52 //The sorting with the compare function as a class member using lambdas was inspired by this page:
53 //https://stackoverflow.com/questions/37767847/stdsort-function-with-custom-compare-function-results-error-reference-to-non
54 
55 //Note about using constBegin() and constEnd() when converting from QSet to QList and viceversa: In Qt >= 5.14.0 and < 6.0.0 it is necessary
56 //to use 'const' for the global variables which are accessed in more than one thread concurrently. It seems that in Qt 6 this does not matter,
57 //but it is better to use it. We are using the 'const' variant for the begin() and end() iterators in the whole generate.cpp file, for safety.
58 
59 #include <ctime>
60 
61 #include <Qt>
62 #include <QtGlobal>
63 #include <QtAlgorithms>
64 
65 #include <iostream>
66 #include <algorithm>
67 #include <cstdlib>
68 
69 using namespace std;
70 
71 #include "timetable_defs.h"
72 #include "timetable.h"
73 #include "generate.h"
74 #include "rules.h"
75 
76 #include "generate_pre.h"
77 
78 #include "matrix.h"
79 
80 #include <QList>
81 #include <QSet>
82 #include <QHash>
83 //#include <QQueue>
84 
85 //#include <condition_variable>
86 
87 //extern QMutex myMutex; //timetablegenerateform.cpp
88 
89 /*
90 #ifndef FET_COMMAND_LINE
91 extern QSemaphore semaphorePlacedActivity;
92 extern QSemaphore finishedSemaphore;
93 #else
94 QSemaphore semaphorePlacedActivity;
95 QSemaphore finishedSemaphore;
96 #endif
97 */
98 
99 //extern MRG32k3a rng;
100 
101 extern Timetable gt;
102 
103 const int MAX_LEVEL=14;
104 
105 const int LEVEL_STOP_CONFLICTS_CALCULATION=MAX_LEVEL;
106 
107 const int INF=2000000000;
108 
109 const int MAX_RETRIES_FOR_AN_ACTIVITY_AT_LEVEL_0=400000;
110 
compareConflictsIncreasing(int a,int b)111 bool Generate::compareConflictsIncreasing(int a, int b)
112 {
113 	return nConflictingActivitiesBipartiteMatching[a]<nConflictingActivitiesBipartiteMatching[b];
114 }
115 
compareConflictsIncreasingAtLevel0(int a,int b)116 bool Generate::compareConflictsIncreasingAtLevel0(int a, int b)
117 {
118 	if(nConflictingActivitiesBipartiteMatching[a]==0 || nConflictingActivitiesBipartiteMatching[b]==0)
119 		return nConflictingActivitiesBipartiteMatching[a]<nConflictingActivitiesBipartiteMatching[b];
120 
121 	assert(conflictingActivitiesBipartiteMatching[a].count()>0);
122 	assert(conflictingActivitiesBipartiteMatching[b].count()>0);
123 
124 	int minWrongA=INF;
125 	int nWrongA=0;
126 	int minIndexActA=gt.rules.nInternalActivities;
127 	for(int ai2 : qAsConst(conflictingActivitiesBipartiteMatching[a])){
128 		minWrongA=min(minWrongA, triedRemovals(ai2, tmpGlobalSolutionCompareLevel0->times[ai2]));
129 		nWrongA+=triedRemovals(ai2, tmpGlobalSolutionCompareLevel0->times[ai2]);
130 		minIndexActA=min(minIndexActA, invPermutation[ai2]);
131 	}
132 
133 	int minWrongB=INF;
134 	int nWrongB=0;
135 	int minIndexActB=gt.rules.nInternalActivities;
136 	for(int ai2 : qAsConst(conflictingActivitiesBipartiteMatching[b])){
137 		minWrongB=min(minWrongB, triedRemovals(ai2, tmpGlobalSolutionCompareLevel0->times[ai2]));
138 		nWrongB+=triedRemovals(ai2, tmpGlobalSolutionCompareLevel0->times[ai2]);
139 		minIndexActB=min(minIndexActB, invPermutation[ai2]);
140 	}
141 
142 	if(minWrongA!=minWrongB)
143 		return minWrongA<minWrongB;
144 
145 	if(nWrongA!=nWrongB)
146 		return nWrongA<nWrongB;
147 
148 	if(nConflictingActivitiesBipartiteMatching[a]!=nConflictingActivitiesBipartiteMatching[b])
149 		return nConflictingActivitiesBipartiteMatching[a]<nConflictingActivitiesBipartiteMatching[b];
150 
151 	return minIndexActA<minIndexActB;
152 }
153 
154 ///////////////////////////////////////////////////////////begin Hopcroft-Karp
155 
156 /*bool breadthFirstSearch()
157 {
158 	QQueue<int> q;
159 
160 	for(int i=0; i<nRealRooms; i++){
161 		int u=randomPermutation[i];
162 
163 		//if(globalLevel>0)
164 		//	cout<<"i=="<<i<<", u=="<<u<<", nCA[u]=="<<nConflictingActivities[u]<<" ";
165 
166 		if(pairNode[u]==NIL_NODE){
167 			dist[u]=0;
168 			q.enqueue(u);
169 		}
170 		else{
171 			dist[u]=INF_DIST;
172 		}
173 	}
174 
175 	//if(globalLevel>0)
176 	//	cout<<endl<<endl;
177 
178 	dist[NIL_NODE]=INF_DIST;
179 	while(!q.isEmpty()){
180 		int u=q.dequeue();
181 		if(dist[u]<dist[NIL_NODE]){
182 			for(int v : qAsConst(adj[u])){
183 				if(dist[pairNode[v]]==INF_DIST){
184 					dist[pairNode[v]]=dist[u]+1;
185 					q.enqueue(pairNode[v]);
186 				}
187 			}
188 		}
189 	}
190 
191 	return dist[NIL_NODE]!=INF_DIST;
192 }
193 
194 bool depthFirstSearch(int u)
195 {
196 	if(u!=NIL_NODE){
197 		for(int v : qAsConst(adj[u])){
198 			if(dist[pairNode[v]]==dist[u]+1){
199 				if(depthFirstSearch(pairNode[v])==true){
200 					pairNode[v]=u;
201 					pairNode[u]=v;
202 					return true;
203 				}
204 			}
205 		}
206 		dist[u]=INF_DIST;
207 		return false;
208 	}
209 
210 	return true;
211 }
212 
213 int hopcroftKarp()
214 {
215 	for(int i=0; i<nRealRooms+nSets; i++)
216 		pairNode[i]=NIL_NODE;
217 
218 	int matching=0;
219 
220 	while(breadthFirstSearch()==true){
221 		for(int i=0; i<nRealRooms; i++){
222 			int u=randomPermutation[i];
223 			if(pairNode[u]==NIL_NODE){
224 				if(depthFirstSearch(u)==true){
225 					matching++;
226 				}
227 			}
228 		}
229 	}
230 
231 	return matching;
232 }*/
233 
234 ///////////////////////////////////////////////////////////end Hopcroft-Karp
235 
236 ////////////////////////begin maximum bipartite matching by depth first search
237 
depthFirstSearch(int u)238 bool Generate::depthFirstSearch(int u)
239 {
240 	if(u!=NIL_NODE){
241 		if(visited[u])
242 			return false;
243 
244 		visited[u]=true;
245 		for(int v : qAsConst(adj[u])){
246 			if(depthFirstSearch(pairNode[v])==true){
247 				pairNode[v]=u;
248 				pairNode[u]=v; //useless assignment
249 				return true;
250 			}
251 		}
252 		return false;
253 	}
254 
255 	return true;
256 }
257 
maximumBipartiteMatching()258 int Generate::maximumBipartiteMatching()
259 {
260 	for(int i=0; i<nRealRooms+nSets; i++) //i could start from nRealRooms, if we also don't check pairNode[u] below
261 		pairNode[i]=NIL_NODE;
262 
263 	int matching=0;
264 
265 	for(int i=0; i<nRealRooms; i++){
266 		int u=semiRandomPermutation[i];
267 		if(pairNode[u]==NIL_NODE){ //useless test (see comment above)
268 			for(int j=0; j<nRealRooms; j++)
269 				visited[j]=false;
270 			if(depthFirstSearch(u)==true)
271 				matching++;
272 		}
273 		else{
274 			assert(0);
275 		}
276 		if(matching>=nSets)
277 			break;
278 	}
279 
280 	return matching;
281 }
282 
283 ////////////////////////end maximum bipartite matching by depth first search
284 
285 //For mornings-afternoons
teacherNoGapsPerAfternoon(int tch)286 inline bool teacherNoGapsPerAfternoon(int tch)
287 {
288 	return teacherConstrainedToZeroGapsPerAfternoon[tch];
289 }
290 
addAiToNewTimetable(int ai,const Activity * act,int d,int h)291 inline void Generate::addAiToNewTimetable(int ai, const Activity* act, int d, int h)
292 {
293 	for(int tch : qAsConst(mustComputeTimetableTeachers[ai])){
294 		for(int dur=0; dur<act->duration; dur++){
295 			oldTeachersTimetable(tch,d,h+dur)=newTeachersTimetable(tch,d,h+dur);
296 			newTeachersTimetable(tch,d,h+dur)=ai;
297 		}
298 		oldTeachersDayNHours(tch,d)=newTeachersDayNHours(tch,d);
299 		oldTeachersDayNGaps(tch,d)=newTeachersDayNGaps(tch,d);
300 	}
301 
302 	for(int sbg : qAsConst(mustComputeTimetableSubgroups[ai])){
303 		for(int dur=0; dur<act->duration; dur++){
304 			oldSubgroupsTimetable(sbg,d,h+dur)=newSubgroupsTimetable(sbg,d,h+dur);
305 			newSubgroupsTimetable(sbg,d,h+dur)=ai;
306 		}
307 		oldSubgroupsDayNHours(sbg,d)=newSubgroupsDayNHours(sbg,d);
308 		oldSubgroupsDayNGaps(sbg,d)=newSubgroupsDayNGaps(sbg,d);
309 		oldSubgroupsDayNFirstGaps(sbg,d)=newSubgroupsDayNFirstGaps(sbg,d);
310 	}
311 
312 	if(gt.rules.mode==MORNINGS_AFTERNOONS){
313 		if(haveTeachersAfternoonsEarly && (d%2)==1)
314 			for(int tch : qAsConst(mustComputeTimetableTeachers[ai]))
315 				oldTeachersDayNFirstGaps(tch,d)=newTeachersDayNFirstGaps(tch,d);
316 		else if(haveTeachersMorningsEarly && (d%2)==0)
317 			for(int tch : qAsConst(mustComputeTimetableTeachers[ai]))
318 				oldTeachersDayNFirstGaps(tch,d)=newTeachersDayNFirstGaps(tch,d);
319 
320 		if(haveTeachersMaxGapsPerRealDay){
321 			for(int tch : qAsConst(mustComputeTimetableTeachers[ai])){
322 				oldTeachersRealDayNHours(tch,d/2)=newTeachersRealDayNHours(tch,d/2);
323 				oldTeachersRealDayNGaps(tch,d/2)=newTeachersRealDayNGaps(tch,d/2);
324 			}
325 		}
326 
327 		if(haveStudentsMaxGapsPerRealDay){
328 			for(int sbg : qAsConst(mustComputeTimetableSubgroups[ai])){
329 				oldSubgroupsRealDayNHours(sbg,d/2)=newSubgroupsRealDayNHours(sbg,d/2);
330 				oldSubgroupsRealDayNGaps(sbg,d/2)=newSubgroupsRealDayNGaps(sbg,d/2);
331 				oldSubgroupsRealDayNFirstGaps(sbg,d/2)=newSubgroupsRealDayNFirstGaps(sbg,d/2);
332 			}
333 		}
334 	}
335 }
336 
removeAiFromNewTimetable(int ai,const Activity * act,int d,int h)337 inline void Generate::removeAiFromNewTimetable(int ai, const Activity* act, int d, int h)
338 {
339 	for(int tch : qAsConst(mustComputeTimetableTeachers[ai])){
340 		for(int dur=0; dur<act->duration; dur++){
341 			assert(newTeachersTimetable(tch,d,h+dur)==ai);
342 			newTeachersTimetable(tch,d,h+dur)=oldTeachersTimetable(tch,d,h+dur);
343 		}
344 		newTeachersDayNHours(tch,d)=oldTeachersDayNHours(tch,d);
345 		newTeachersDayNGaps(tch,d)=oldTeachersDayNGaps(tch,d);
346 	}
347 
348 	for(int sbg : qAsConst(mustComputeTimetableSubgroups[ai])){
349 		for(int dur=0; dur<act->duration; dur++){
350 			assert(newSubgroupsTimetable(sbg,d,h+dur)==ai);
351 			newSubgroupsTimetable(sbg,d,h+dur)=oldSubgroupsTimetable(sbg,d,h+dur);
352 		}
353 		newSubgroupsDayNHours(sbg,d)=oldSubgroupsDayNHours(sbg,d);
354 		newSubgroupsDayNGaps(sbg,d)=oldSubgroupsDayNGaps(sbg,d);
355 		newSubgroupsDayNFirstGaps(sbg,d)=oldSubgroupsDayNFirstGaps(sbg,d);
356 	}
357 
358 	if(gt.rules.mode==MORNINGS_AFTERNOONS){
359 		if(haveTeachersAfternoonsEarly && (d%2)==1)
360 			for(int tch : qAsConst(mustComputeTimetableTeachers[ai]))
361 				newTeachersDayNFirstGaps(tch,d)=oldTeachersDayNFirstGaps(tch,d);
362 		else if(haveTeachersMorningsEarly && (d%2)==0)
363 			for(int tch : qAsConst(mustComputeTimetableTeachers[ai]))
364 				newTeachersDayNFirstGaps(tch,d)=oldTeachersDayNFirstGaps(tch,d);
365 
366 		if(haveTeachersMaxGapsPerRealDay){
367 			for(int tch : qAsConst(mustComputeTimetableTeachers[ai])){
368 				newTeachersRealDayNHours(tch,d/2)=oldTeachersRealDayNHours(tch,d/2);
369 				newTeachersRealDayNGaps(tch,d/2)=oldTeachersRealDayNGaps(tch,d/2);
370 			}
371 		}
372 
373 		if(haveStudentsMaxGapsPerRealDay){
374 			for(int sbg : qAsConst(mustComputeTimetableSubgroups[ai])){
375 				newSubgroupsRealDayNHours(sbg,d/2)=oldSubgroupsRealDayNHours(sbg,d/2);
376 				newSubgroupsRealDayNGaps(sbg,d/2)=oldSubgroupsRealDayNGaps(sbg,d/2);
377 				newSubgroupsRealDayNFirstGaps(sbg,d/2)=oldSubgroupsRealDayNFirstGaps(sbg,d/2);
378 			}
379 		}
380 	}
381 }
382 
removeAi2FromTchTimetable(int ai2)383 inline void Generate::removeAi2FromTchTimetable(int ai2)
384 {
385 	Activity* act2=&gt.rules.internalActivitiesList[ai2];
386 	int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
387 	int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
388 
389 	for(int dur2=0; dur2<act2->duration; dur2++){
390 		assert(tchTimetable(d2,h2+dur2)==ai2);
391 		if(tchTimetable(d2,h2+dur2)==ai2)
392 			tchTimetable(d2,h2+dur2)=-1;
393 	}
394 }
395 
removeAi2FromSbgTimetable(int ai2)396 inline void Generate::removeAi2FromSbgTimetable(int ai2)
397 {
398 	Activity* act2=&gt.rules.internalActivitiesList[ai2];
399 	int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
400 	int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
401 
402 	for(int dur2=0; dur2<act2->duration; dur2++){
403 		assert(sbgTimetable(d2,h2+dur2)==ai2);
404 		if(sbgTimetable(d2,h2+dur2)==ai2)
405 			sbgTimetable(d2,h2+dur2)=-1;
406 	}
407 }
408 
getTchTimetable(int tch,const QList<int> & conflActivities)409 inline void Generate::getTchTimetable(int tch, const QList<int>& conflActivities)
410 {
411 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
412 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
413 			int ai2=newTeachersTimetable(tch,d2,h2);
414 			if(ai2>=0 && !conflActivities.contains(ai2))
415 				tchTimetable(d2,h2)=ai2;
416 			else
417 				tchTimetable(d2,h2)=-1;
418 		}
419 
420 	/*for(int dur=0; dur<act->duration; dur++){
421 		assert(tchTimetable(d,h+dur)==-1);
422 		tchTimetable(d,h+dur)=ai;
423 	}*/
424 }
425 
getSbgTimetable(int sbg,const QList<int> & conflActivities)426 inline void Generate::getSbgTimetable(int sbg, const QList<int>& conflActivities)
427 {
428 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
429 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
430 			int ai2=newSubgroupsTimetable(sbg,d2,h2);
431 			if(ai2>=0 && !conflActivities.contains(ai2))
432 				sbgTimetable(d2,h2)=ai2;
433 			else
434 				sbgTimetable(d2,h2)=-1;
435 		}
436 
437 	/*for(int dur=0; dur<act->duration; dur++){
438 		assert(sbgTimetable(d,h+dur)==-1);
439 		sbgTimetable(d,h+dur)=ai;
440 	}*/
441 }
442 
updateTchNHoursGaps(int tch,int d)443 inline void Generate::updateTchNHoursGaps(int tch, int d)
444 {
445 	if(gt.rules.mode!=MORNINGS_AFTERNOONS){
446 		int hours=0, gaps=0;
447 
448 		int h;
449 		for(h=0; h<gt.rules.nHoursPerDay; h++)
450 			if(tchTimetable(d,h)>=0)
451 				break;
452 		int ng=0;
453 		for(; h<gt.rules.nHoursPerDay; h++){
454 			if(tchTimetable(d,h)>=0){
455 				hours++;
456 				gaps+=ng;
457 				ng=0;
458 			}
459 			else if(!breakDayHour(d,h) && !teacherNotAvailableDayHour(tch,d,h))
460 				ng++;
461 		}
462 		tchDayNGaps[d]=gaps;
463 		tchDayNHours[d]=hours;
464 	}
465 	else{
466 		int hours=0, gaps=0, nfirstgaps=0;
467 
468 		int h;
469 		for(h=0; h<gt.rules.nHoursPerDay; h++){
470 			if(tchTimetable(d,h)>=0)
471 				break;
472 			else if(!breakDayHour(d,h) && !teacherNotAvailableDayHour(tch,d,h))
473 				nfirstgaps++;
474 		}
475 		int ng=0;
476 		for(; h<gt.rules.nHoursPerDay; h++){
477 			if(tchTimetable(d,h)>=0){
478 				hours++;
479 				gaps+=ng;
480 				ng=0;
481 			}
482 			else if(!breakDayHour(d,h) && !teacherNotAvailableDayHour(tch,d,h))
483 				ng++;
484 		}
485 		tchDayNGaps[d]=gaps;
486 		tchDayNHours[d]=hours;
487 
488 		if(haveTeachersAfternoonsEarly && (d%2)==1){
489 			if(hours>0){
490 				tchDayNFirstGaps[d]=nfirstgaps;
491 			}
492 			else{
493 				tchDayNFirstGaps[d]=0;
494 			}
495 		}
496 		else if(haveTeachersMorningsEarly && (d%2)==0){
497 			if(hours>0){
498 				tchDayNFirstGaps[d]=nfirstgaps;
499 			}
500 			else{
501 				tchDayNFirstGaps[d]=0;
502 			}
503 		}
504 	}
505 }
506 
updateTchNHoursGapsRealDay(int tch,int real_d)507 inline void Generate::updateTchNHoursGapsRealDay(int tch, int real_d)
508 {
509 	assert(gt.rules.mode==MORNINGS_AFTERNOONS);
510 
511 	int hours=0, gaps=0;
512 
513 	int h;
514 	int d;
515 	int double_h;
516 	for(double_h=0; double_h<2*gt.rules.nHoursPerDay; double_h++){
517 		if(double_h<gt.rules.nHoursPerDay)
518 			d=real_d*2;
519 		else
520 			d=real_d*2+1;
521 		h=double_h%gt.rules.nHoursPerDay;
522 		if(tchTimetable(d,h)>=0)
523 			break;
524 	}
525 	int ng=0;
526 	for(; double_h<2*gt.rules.nHoursPerDay; double_h++){
527 		if(double_h<gt.rules.nHoursPerDay)
528 			d=real_d*2;
529 		else
530 			d=real_d*2+1;
531 		h=double_h%gt.rules.nHoursPerDay;
532 		if(tchTimetable(d,h)>=0){
533 			hours++;
534 			gaps+=ng;
535 			ng=0;
536 		}
537 		else if(!breakDayHour(d,h) && !teacherNotAvailableDayHour(tch,d,h))
538 			ng++;
539 	}
540 	tchRealDayNGaps[real_d]=gaps;
541 	tchRealDayNHours[real_d]=hours;
542 }
543 
updateSbgNHoursGaps(int sbg,int d)544 inline void Generate::updateSbgNHoursGaps(int sbg, int d)
545 {
546 	int hours=0, gaps=0, nfirstgaps=0;
547 
548 	int h;
549 	for(h=0; h<gt.rules.nHoursPerDay; h++){
550 		if(sbgTimetable(d,h)>=0)
551 			break;
552 		else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
553 			nfirstgaps++;
554 	}
555 	int ng=0;
556 	for(; h<gt.rules.nHoursPerDay; h++){
557 		if(sbgTimetable(d,h)>=0){
558 			hours++;
559 			gaps+=ng;
560 			ng=0;
561 		}
562 		else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
563 			ng++;
564 	}
565 	sbgDayNGaps[d]=gaps;
566 	sbgDayNHours[d]=hours;
567 	if(sbgDayNHours[d]>0)
568 		sbgDayNFirstGaps[d]=nfirstgaps;
569 	else
570 		sbgDayNFirstGaps[d]=0;
571 }
572 
updateSbgNHoursGapsRealDay(int sbg,int real_d)573 inline void Generate::updateSbgNHoursGapsRealDay(int sbg, int real_d)
574 {
575 	assert(gt.rules.mode==MORNINGS_AFTERNOONS);
576 
577 	int hours=0, gaps=0, nfirstgaps=0;
578 	int hours_first_half=0;
579 	int nfirstgaps_first_half=0;
580 
581 	int h;
582 	int d;
583 	int double_h;
584 	for(double_h=0; double_h<2*gt.rules.nHoursPerDay; double_h++){
585 		if(double_h<gt.rules.nHoursPerDay)
586 			d=real_d*2;
587 		else
588 			d=real_d*2+1;
589 		h=double_h%gt.rules.nHoursPerDay;
590 		if(sbgTimetable(d,h)>=0)
591 			break;
592 		else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h)){
593 			nfirstgaps++;
594 			if(d%2==0)
595 				nfirstgaps_first_half++;
596 		}
597 	}
598 	int ng=0;
599 	for(; double_h<2*gt.rules.nHoursPerDay; double_h++){
600 		if(double_h<gt.rules.nHoursPerDay)
601 			d=real_d*2;
602 		else
603 			d=real_d*2+1;
604 		h=double_h%gt.rules.nHoursPerDay;
605 		if(sbgTimetable(d,h)>=0){
606 			hours++;
607 			if(d%2==0)
608 				hours_first_half++;
609 			gaps+=ng;
610 			ng=0;
611 		}
612 		else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
613 			ng++;
614 	}
615 	sbgRealDayNGaps[real_d]=gaps;
616 	sbgRealDayNHours[real_d]=hours;
617 	if(hours_first_half>0)
618 		sbgRealDayNFirstGaps[real_d]=nfirstgaps;
619 	else if(hours>0)
620 		sbgRealDayNFirstGaps[real_d]=nfirstgaps-nfirstgaps_first_half;
621 	else
622 		sbgRealDayNFirstGaps[real_d]=0;
623 }
624 
updateTeachersNHoursGaps(int ai,int d)625 inline void Generate::updateTeachersNHoursGaps(int ai, int d)
626 {
627 	if(gt.rules.mode!=MORNINGS_AFTERNOONS){
628 		for(int tch : qAsConst(mustComputeTimetableTeachers[ai])){
629 			int hours=0, gaps=0;
630 
631 			int h;
632 			for(h=0; h<gt.rules.nHoursPerDay; h++)
633 				if(newTeachersTimetable(tch,d,h)>=0)
634 					break;
635 			int ng=0;
636 			for(; h<gt.rules.nHoursPerDay; h++){
637 				if(newTeachersTimetable(tch,d,h)>=0){
638 					hours++;
639 					gaps+=ng;
640 					ng=0;
641 				}
642 				else if(!breakDayHour(d,h) && !teacherNotAvailableDayHour(tch,d,h))
643 					ng++;
644 			}
645 			newTeachersDayNGaps(tch,d)=gaps;
646 			newTeachersDayNHours(tch,d)=hours;
647 		}
648 	}
649 	else{
650 		for(int tch : qAsConst(mustComputeTimetableTeachers[ai])){
651 			int hours=0, gaps=0, nfirstgaps=0;
652 
653 			int h;
654 			for(h=0; h<gt.rules.nHoursPerDay; h++){
655 				if(newTeachersTimetable(tch,d,h)>=0)
656 					break;
657 				else if(!breakDayHour(d,h) && !teacherNotAvailableDayHour(tch,d,h))
658 					nfirstgaps++;
659 			}
660 			int ng=0;
661 			for(; h<gt.rules.nHoursPerDay; h++){
662 				if(newTeachersTimetable(tch,d,h)>=0){
663 					hours++;
664 					gaps+=ng;
665 					ng=0;
666 				}
667 				else if(!breakDayHour(d,h) && !teacherNotAvailableDayHour(tch,d,h))
668 					ng++;
669 			}
670 			newTeachersDayNGaps(tch,d)=gaps;
671 			newTeachersDayNHours(tch,d)=hours;
672 
673 			if(haveTeachersAfternoonsEarly && (d%2)==1){
674 				if(hours>0){
675 					newTeachersDayNFirstGaps(tch,d)=nfirstgaps;
676 				}
677 				else{
678 					newTeachersDayNFirstGaps(tch,d)=0;
679 				}
680 			}
681 			else if(haveTeachersMorningsEarly && (d%2)==0){
682 				if(hours>0){
683 					newTeachersDayNFirstGaps(tch,d)=nfirstgaps;
684 				}
685 				else{
686 					newTeachersDayNFirstGaps(tch,d)=0;
687 				}
688 			}
689 		}
690 	}
691 }
692 
updateTeachersNHoursGapsRealDay(int ai,int real_d)693 inline void Generate::updateTeachersNHoursGapsRealDay(int ai, int real_d)
694 {
695 	assert(gt.rules.mode==MORNINGS_AFTERNOONS);
696 
697 	for(int tch : qAsConst(mustComputeTimetableTeachers[ai])){
698 		int hours=0, gaps=0;
699 
700 		int h;
701 		int d;
702 		int double_h;
703 		for(double_h=0; double_h<2*gt.rules.nHoursPerDay; double_h++){
704 			if(double_h<gt.rules.nHoursPerDay)
705 				d=real_d*2;
706 			else
707 				d=real_d*2+1;
708 			h=double_h%gt.rules.nHoursPerDay;
709 			if(newTeachersTimetable(tch,d,h)>=0)
710 				break;
711 		}
712 		int ng=0;
713 		for(; double_h<2*gt.rules.nHoursPerDay; double_h++){
714 			if(double_h<gt.rules.nHoursPerDay)
715 				d=real_d*2;
716 			else
717 				d=real_d*2+1;
718 			h=double_h%gt.rules.nHoursPerDay;
719 			if(newTeachersTimetable(tch,d,h)>=0){
720 				hours++;
721 				gaps+=ng;
722 				ng=0;
723 			}
724 			else if(!breakDayHour(d,h) && !teacherNotAvailableDayHour(tch,d,h))
725 				ng++;
726 		}
727 		newTeachersRealDayNGaps(tch,real_d)=gaps;
728 		newTeachersRealDayNHours(tch,real_d)=hours;
729 	}
730 }
731 
updateSubgroupsNHoursGaps(int ai,int d)732 inline void Generate::updateSubgroupsNHoursGaps(int ai, int d)
733 {
734 	for(int sbg : qAsConst(mustComputeTimetableSubgroups[ai])){
735 		int hours=0, gaps=0, nfirstgaps=0;
736 
737 		int h;
738 		for(h=0; h<gt.rules.nHoursPerDay; h++){
739 			if(newSubgroupsTimetable(sbg,d,h)>=0)
740 				break;
741 			else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
742 				nfirstgaps++;
743 		}
744 		int ng=0;
745 		for(; h<gt.rules.nHoursPerDay; h++){
746 			if(newSubgroupsTimetable(sbg,d,h)>=0){
747 				hours++;
748 				gaps+=ng;
749 				ng=0;
750 			}
751 			else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
752 				ng++;
753 		}
754 		newSubgroupsDayNGaps(sbg,d)=gaps;
755 		newSubgroupsDayNHours(sbg,d)=hours;
756 		if(hours>0)
757 			newSubgroupsDayNFirstGaps(sbg,d)=nfirstgaps;
758 		else
759 			newSubgroupsDayNFirstGaps(sbg,d)=0;
760 	}
761 }
762 
updateSubgroupsNHoursGapsRealDay(int ai,int real_d)763 inline void Generate::updateSubgroupsNHoursGapsRealDay(int ai, int real_d)
764 {
765 	assert(gt.rules.mode==MORNINGS_AFTERNOONS);
766 
767 	for(int sbg : qAsConst(mustComputeTimetableSubgroups[ai])){
768 		int hours=0, gaps=0, nfirstgaps=0;
769 		int hours_first_half=0;
770 		int nfirstgaps_first_half=0;
771 
772 		int h;
773 		int d;
774 		int double_h;
775 		for(double_h=0; double_h<2*gt.rules.nHoursPerDay; double_h++){
776 			if(double_h<gt.rules.nHoursPerDay)
777 				d=real_d*2;
778 			else
779 				d=real_d*2+1;
780 			h=double_h%gt.rules.nHoursPerDay;
781 			if(newSubgroupsTimetable(sbg,d,h)>=0)
782 				break;
783 			else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h)){
784 				nfirstgaps++;
785 				if(d%2==0)
786 					nfirstgaps_first_half++;
787 			}
788 		}
789 		int ng=0;
790 		for(; double_h<2*gt.rules.nHoursPerDay; double_h++){
791 			if(double_h<gt.rules.nHoursPerDay)
792 				d=real_d*2;
793 			else
794 				d=real_d*2+1;
795 			h=double_h%gt.rules.nHoursPerDay;
796 			if(newSubgroupsTimetable(sbg,d,h)>=0){
797 				hours++;
798 				if(d%2==0)
799 					hours_first_half++;
800 				gaps+=ng;
801 				ng=0;
802 			}
803 			else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
804 				ng++;
805 		}
806 		newSubgroupsRealDayNGaps(sbg,real_d)=gaps;
807 		newSubgroupsRealDayNHours(sbg,real_d)=hours;
808 		if(hours_first_half>0)
809 			newSubgroupsRealDayNFirstGaps(sbg,real_d)=nfirstgaps;
810 		else if(hours>0)
811 			newSubgroupsRealDayNFirstGaps(sbg,real_d)=nfirstgaps-nfirstgaps_first_half;
812 		else
813 			newSubgroupsRealDayNFirstGaps(sbg,real_d)=0;
814 	}
815 }
816 
teacherGetNHoursGaps(int tch)817 inline void Generate::teacherGetNHoursGaps(int tch)
818 {
819 	if(!mustComputeTimetableTeacher[tch])
820 		return;
821 
822 	if(gt.rules.mode!=MORNINGS_AFTERNOONS){
823 		for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
824 			newTeachersDayNHours(tch,d2)=0;
825 			newTeachersDayNGaps(tch,d2)=0;
826 			//useless code commented on 2021-08-11 (we are not in the MORNINGS_AFTERNOONS mode).
827 			//if(haveTeachersAfternoonsEarly && d2%2==1)
828 			//	newTeachersDayNFirstGaps(tch,d2)=0;
829 		}
830 		for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
831 			bool countGaps=false;
832 			int ng=0;
833 			for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
834 				if(newTeachersTimetable(tch,d2,h2)>=0){
835 					newTeachersDayNHours(tch,d2)++;
836 					if(countGaps)
837 						newTeachersDayNGaps(tch,d2)+=ng;
838 					else
839 						countGaps=true;
840 					ng=0;
841 				}
842 				else if(!breakDayHour(d2,h2) && !teacherNotAvailableDayHour(tch,d2,h2))
843 					ng++;
844 			}
845 		}
846 	}
847 	else{
848 		for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
849 			newTeachersDayNHours(tch,d2)=0;
850 			newTeachersDayNGaps(tch,d2)=0;
851 			if(haveTeachersAfternoonsEarly && d2%2==1)
852 				newTeachersDayNFirstGaps(tch,d2)=0;
853 			else if(haveTeachersMorningsEarly && d2%2==0)
854 				newTeachersDayNFirstGaps(tch,d2)=0;
855 		}
856 		for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
857 			bool countGaps=false;
858 			int ng=0;
859 			int h2;
860 			int nfirstgaps=0;
861 
862 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
863 				if(newTeachersTimetable(tch,d2,h2)>=0)
864 					break;
865 				else if(!breakDayHour(d2,h2) && !teacherNotAvailableDayHour(tch,d2,h2))
866 					nfirstgaps++;
867 			}
868 			for(; h2<gt.rules.nHoursPerDay; h2++){
869 				if(newTeachersTimetable(tch,d2,h2)>=0){
870 					newTeachersDayNHours(tch,d2)++;
871 					if(countGaps)
872 						newTeachersDayNGaps(tch,d2)+=ng;
873 					else
874 						countGaps=true;
875 					ng=0;
876 				}
877 				else if(!breakDayHour(d2,h2) && !teacherNotAvailableDayHour(tch,d2,h2))
878 					ng++;
879 			}
880 
881 			if(haveTeachersAfternoonsEarly && d2%2==1 && newTeachersDayNHours(tch,d2)>0)
882 				newTeachersDayNFirstGaps(tch,d2)=nfirstgaps;
883 			if(haveTeachersMorningsEarly && d2%2==0 && newTeachersDayNHours(tch,d2)>0)
884 				newTeachersDayNFirstGaps(tch,d2)=nfirstgaps;
885 		}
886 
887 		if(haveTeachersMaxGapsPerRealDay){
888 			for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
889 				newTeachersRealDayNHours(tch,d2)=0;
890 				newTeachersRealDayNGaps(tch,d2)=0;
891 			}
892 			for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
893 				bool countGaps=false;
894 				int ng=0;
895 				for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
896 					if(newTeachersTimetable(tch,2*d2,h2)>=0){
897 						newTeachersRealDayNHours(tch,d2)++;
898 						if(countGaps)
899 							newTeachersRealDayNGaps(tch,d2)+=ng;
900 						else
901 							countGaps=true;
902 						ng=0;
903 					}
904 					else if(!breakDayHour(2*d2,h2) && !teacherNotAvailableDayHour(tch,2*d2,h2))
905 						ng++;
906 				}
907 				for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
908 					if(newTeachersTimetable(tch,2*d2+1,h2)>=0){
909 						newTeachersRealDayNHours(tch,d2)++;
910 						if(countGaps)
911 							newTeachersRealDayNGaps(tch,d2)+=ng;
912 						else
913 							countGaps=true;
914 						ng=0;
915 					}
916 					else if(!breakDayHour(2*d2+1,h2) && !teacherNotAvailableDayHour(tch,2*d2+1,h2))
917 						ng++;
918 				}
919 			}
920 		}
921 	}
922 }
923 
tchGetNHoursGaps(int tch)924 inline void Generate::tchGetNHoursGaps(int tch)
925 {
926 	if(gt.rules.mode!=MORNINGS_AFTERNOONS){
927 		for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
928 			tchDayNHours[d2]=0;
929 			tchDayNGaps[d2]=0;
930 		}
931 		for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
932 			bool countGaps=false;
933 			int ng=0;
934 			for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
935 				if(tchTimetable(d2,h2)>=0){
936 					tchDayNHours[d2]++;
937 					if(countGaps)
938 						tchDayNGaps[d2]+=ng;
939 					else
940 						countGaps=true;
941 					ng=0;
942 				}
943 				else if(!breakDayHour(d2,h2) && !teacherNotAvailableDayHour(tch,d2,h2))
944 					ng++;
945 			}
946 		}
947 	}
948 	else{
949 		for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
950 			tchDayNHours[d2]=0;
951 			tchDayNGaps[d2]=0;
952 
953 			if(haveTeachersAfternoonsEarly && d2%2==1)
954 				tchDayNFirstGaps[d2]=0;
955 			else if(haveTeachersMorningsEarly && d2%2==0)
956 				tchDayNFirstGaps[d2]=0;
957 		}
958 		for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
959 			bool countGaps=false;
960 			int ng=0;
961 			int nfirstgaps=0;
962 			int h2;
963 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
964 				if(tchTimetable(d2,h2)>=0)
965 					break;
966 				else if(!breakDayHour(d2,h2) && !teacherNotAvailableDayHour(tch,d2,h2))
967 					nfirstgaps++;
968 			}
969 			for(; h2<gt.rules.nHoursPerDay; h2++){
970 				if(tchTimetable(d2,h2)>=0){
971 					tchDayNHours[d2]++;
972 					if(countGaps)
973 						tchDayNGaps[d2]+=ng;
974 					else
975 						countGaps=true;
976 					ng=0;
977 				}
978 				else if(!breakDayHour(d2,h2) && !teacherNotAvailableDayHour(tch,d2,h2))
979 					ng++;
980 			}
981 
982 			if(haveTeachersAfternoonsEarly && d2%2==1 && tchDayNHours[d2]>0)
983 				tchDayNFirstGaps[d2]=nfirstgaps;
984 			else if(haveTeachersMorningsEarly && d2%2==0 && tchDayNHours[d2]>0)
985 				tchDayNFirstGaps[d2]=nfirstgaps;
986 		}
987 	}
988 }
989 
tchGetNHoursGapsRealDays(int tch)990 inline void Generate::tchGetNHoursGapsRealDays(int tch)
991 {
992 	assert(gt.rules.mode==MORNINGS_AFTERNOONS);
993 
994 	for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
995 		tchRealDayNHours[d2]=0;
996 		tchRealDayNGaps[d2]=0;
997 	}
998 	for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
999 		bool countGaps=false;
1000 		int ng=0;
1001 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
1002 			if(tchTimetable(2*d2,h2)>=0){
1003 				tchRealDayNHours[d2]++;
1004 				if(countGaps)
1005 					tchRealDayNGaps[d2]+=ng;
1006 				else
1007 					countGaps=true;
1008 				ng=0;
1009 			}
1010 			else if(!breakDayHour(2*d2,h2) && !teacherNotAvailableDayHour(tch,2*d2,h2))
1011 				ng++;
1012 		}
1013 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
1014 			if(tchTimetable(2*d2+1,h2)>=0){
1015 				tchRealDayNHours[d2]++;
1016 				if(countGaps)
1017 					tchRealDayNGaps[d2]+=ng;
1018 				else
1019 					countGaps=true;
1020 				ng=0;
1021 			}
1022 			else if(!breakDayHour(2*d2+1,h2) && !teacherNotAvailableDayHour(tch,2*d2+1,h2))
1023 				ng++;
1024 		}
1025 	}
1026 }
1027 
teacherRemoveAnActivityFromBeginOrEnd(int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)1028 inline bool Generate::teacherRemoveAnActivityFromBeginOrEnd(int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
1029 {
1030 	//Teacher: remove an activity from the beginning or from the end of any day
1031 	QList<int> possibleDays;
1032 	QList<bool> atBeginning;
1033 	QList<int> acts;
1034 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
1035 		if(tchDayNHours[d2]>0){
1036 			int actIndexBegin=-1, actIndexEnd=-1;
1037 			int h2;
1038 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
1039 				if(tchTimetable(d2,h2)>=0){
1040 					actIndexBegin=tchTimetable(d2,h2);
1041 					break;
1042 				}
1043 			}
1044 			if(actIndexBegin>=0)
1045 				if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
1046 					actIndexBegin=-1;
1047 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
1048 				if(tchTimetable(d2,h2)>=0){
1049 					actIndexEnd=tchTimetable(d2,h2);
1050 					break;
1051 				}
1052 			}
1053 			if(actIndexEnd>=0)
1054 				if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai || actIndexEnd==actIndexBegin)
1055 					actIndexEnd=-1;
1056 
1057 			if(actIndexBegin>=0){
1058 				assert(!acts.contains(actIndexBegin));
1059 				possibleDays.append(d2);
1060 				atBeginning.append(true);
1061 				acts.append(actIndexBegin);
1062 			}
1063 			if(actIndexEnd>=0){
1064 				assert(!acts.contains(actIndexEnd));
1065 				possibleDays.append(d2);
1066 				atBeginning.append(false);
1067 				acts.append(actIndexEnd);
1068 			}
1069 		}
1070 	}
1071 
1072 	bool possibleBeginOrEnd=true;
1073 	if(possibleDays.count()==0)
1074 		possibleBeginOrEnd=false;
1075 
1076 	if(possibleBeginOrEnd){
1077 		int t;
1078 
1079 		if(level==0){
1080 			int optMinWrong=INF;
1081 
1082 			QList<int> tl;
1083 
1084 			for(int q=0; q<acts.count(); q++){
1085 				int ai2=acts.at(q);
1086 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
1087 				 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
1088 				}
1089 			}
1090 
1091 			for(int q=0; q<acts.count(); q++){
1092 				int ai2=acts.at(q);
1093 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
1094 					tl.append(q);
1095 			}
1096 
1097 			assert(tl.count()>=1);
1098 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
1099 
1100 			assert(mpos>=0 && mpos<acts.count());
1101 			t=mpos;
1102 		}
1103 		else{
1104 			t=rng.intMRG32k3a(possibleDays.count());
1105 		}
1106 
1107 		assert(t>=0 && t<possibleDays.count());
1108 
1109 		int d2=possibleDays.at(t);
1110 		bool begin=atBeginning.at(t);
1111 		int ai2=acts.at(t);
1112 
1113 		removedActivity=ai2;
1114 
1115 		if(begin){
1116 			int h2;
1117 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
1118 				if(tchTimetable(d2,h2)>=0)
1119 					break;
1120 			assert(h2<gt.rules.nHoursPerDay);
1121 
1122 			assert(tchTimetable(d2,h2)==ai2);
1123 
1124 			assert(!conflActivities.contains(ai2));
1125 			conflActivities.append(ai2);
1126 			nConflActivities++;
1127 			assert(nConflActivities==conflActivities.count());
1128 		}
1129 		else{
1130 			int h2;
1131 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
1132 				if(tchTimetable(d2,h2)>=0)
1133 					break;
1134 			assert(h2>=0);
1135 
1136 			assert(tchTimetable(d2,h2)==ai2);
1137 
1138 			assert(!conflActivities.contains(ai2));
1139 			conflActivities.append(ai2);
1140 			nConflActivities++;
1141 			assert(nConflActivities==conflActivities.count());
1142 		}
1143 
1144 		return true;
1145 	}
1146 	else
1147 		return false;
1148 }
1149 
teacherRemoveAnActivityFromEnd(int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)1150 inline bool Generate::teacherRemoveAnActivityFromEnd(int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
1151 {
1152 	//Teacher: remove an activity from the end of any day
1153 	QList<int> possibleDays;
1154 	QList<int> acts;
1155 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
1156 		if(tchDayNHours[d2]>0){
1157 			int actIndexEnd=-1;
1158 			int h2;
1159 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
1160 				if(tchTimetable(d2,h2)>=0){
1161 					actIndexEnd=tchTimetable(d2,h2);
1162 					break;
1163 				}
1164 			}
1165 			if(actIndexEnd>=0)
1166 				if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai)
1167 					actIndexEnd=-1;
1168 
1169 			if(actIndexEnd>=0){
1170 				assert(!acts.contains(actIndexEnd));
1171 				possibleDays.append(d2);
1172 				acts.append(actIndexEnd);
1173 			}
1174 		}
1175 	}
1176 
1177 	bool possibleEnd=true;
1178 	if(possibleDays.count()==0)
1179 		possibleEnd=false;
1180 
1181 	if(possibleEnd){
1182 		int t;
1183 
1184 		if(level==0){
1185 			int optMinWrong=INF;
1186 
1187 			QList<int> tl;
1188 
1189 			for(int q=0; q<acts.count(); q++){
1190 				int ai2=acts.at(q);
1191 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
1192 				 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
1193 				}
1194 			}
1195 
1196 			for(int q=0; q<acts.count(); q++){
1197 				int ai2=acts.at(q);
1198 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
1199 					tl.append(q);
1200 			}
1201 
1202 			assert(tl.count()>=1);
1203 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
1204 
1205 			assert(mpos>=0 && mpos<acts.count());
1206 			t=mpos;
1207 		}
1208 		else{
1209 			t=rng.intMRG32k3a(possibleDays.count());
1210 		}
1211 
1212 		assert(t>=0 && t<possibleDays.count());
1213 
1214 		int d2=possibleDays.at(t);
1215 		int ai2=acts.at(t);
1216 
1217 		removedActivity=ai2;
1218 
1219 		int h2;
1220 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
1221 			if(tchTimetable(d2,h2)>=0)
1222 				break;
1223 		assert(h2>=0);
1224 
1225 		assert(tchTimetable(d2,h2)==ai2);
1226 
1227 		assert(!conflActivities.contains(ai2));
1228 		conflActivities.append(ai2);
1229 		nConflActivities++;
1230 		assert(nConflActivities==conflActivities.count());
1231 
1232 		return true;
1233 	}
1234 	else
1235 		return false;
1236 }
1237 
teacherRemoveAnActivityFromBeginMorningOrEndAfternoon(int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)1238 inline bool Generate::teacherRemoveAnActivityFromBeginMorningOrEndAfternoon(int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
1239 {
1240 	QList<int> possibleDays;
1241 	QList<bool> atBeginning;
1242 	QList<int> acts;
1243 	for(int real_d=0; real_d<gt.rules.nDaysPerWeek/2; real_d++){
1244 		int d2_morning=real_d*2;
1245 		int d2_afternoon=real_d*2+1;
1246 
1247 		if(tchDayNHours[d2_morning]>0){
1248 			int actIndexBegin=-1;
1249 			int h2;
1250 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
1251 				if(tchTimetable(d2_morning,h2)>=0){
1252 					actIndexBegin=tchTimetable(d2_morning,h2);
1253 					break;
1254 				}
1255 			}
1256 			if(actIndexBegin>=0)
1257 				if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
1258 					actIndexBegin=-1;
1259 
1260 			if(actIndexBegin>=0){
1261 				assert(!acts.contains(actIndexBegin));
1262 				possibleDays.append(d2_morning);
1263 				atBeginning.append(true);
1264 				acts.append(actIndexBegin);
1265 			}
1266 		}
1267 
1268 		if(tchDayNHours[d2_afternoon]>0){
1269 			int actIndexEnd=-1;
1270 			int h2;
1271 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
1272 				if(tchTimetable(d2_afternoon,h2)>=0){
1273 					actIndexEnd=tchTimetable(d2_afternoon,h2);
1274 					break;
1275 				}
1276 			}
1277 			if(actIndexEnd>=0)
1278 				if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai /* || actIndexEnd==actIndexBegin */)
1279 					actIndexEnd=-1;
1280 
1281 			if(actIndexEnd>=0){
1282 				assert(!acts.contains(actIndexEnd));
1283 				possibleDays.append(d2_afternoon);
1284 				atBeginning.append(false);
1285 				acts.append(actIndexEnd);
1286 			}
1287 		}
1288 	}
1289 
1290 	bool possibleBeginOrEnd=true;
1291 	if(possibleDays.count()==0)
1292 		possibleBeginOrEnd=false;
1293 
1294 	if(possibleBeginOrEnd){
1295 		int t;
1296 
1297 		if(level==0){
1298 			int optMinWrong=INF;
1299 
1300 			QList<int> tl;
1301 
1302 			for(int q=0; q<acts.count(); q++){
1303 				int ai2=acts.at(q);
1304 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
1305 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
1306 				}
1307 			}
1308 
1309 			for(int q=0; q<acts.count(); q++){
1310 				int ai2=acts.at(q);
1311 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
1312 					tl.append(q);
1313 			}
1314 
1315 			assert(tl.count()>=1);
1316 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
1317 
1318 			assert(mpos>=0 && mpos<acts.count());
1319 			t=mpos;
1320 		}
1321 		else{
1322 			t=rng.intMRG32k3a(possibleDays.count());
1323 		}
1324 
1325 		assert(t>=0 && t<possibleDays.count());
1326 
1327 		int d2=possibleDays.at(t);
1328 		bool begin=atBeginning.at(t);
1329 		int ai2=acts.at(t);
1330 
1331 		removedActivity=ai2;
1332 
1333 		if(begin){
1334 			int h2;
1335 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
1336 				if(tchTimetable(d2,h2)>=0)
1337 					break;
1338 			assert(h2<gt.rules.nHoursPerDay);
1339 
1340 			assert(tchTimetable(d2,h2)==ai2);
1341 
1342 			assert(!conflActivities.contains(ai2));
1343 			conflActivities.append(ai2);
1344 			nConflActivities++;
1345 			assert(nConflActivities==conflActivities.count());
1346 		}
1347 		else{
1348 			int h2;
1349 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
1350 				if(tchTimetable(d2,h2)>=0)
1351 					break;
1352 			assert(h2>=0);
1353 
1354 			assert(tchTimetable(d2,h2)==ai2);
1355 
1356 			assert(!conflActivities.contains(ai2));
1357 			conflActivities.append(ai2);
1358 			nConflActivities++;
1359 			assert(nConflActivities==conflActivities.count());
1360 		}
1361 
1362 		return true;
1363 	}
1364 	else
1365 		return false;
1366 }
1367 
teacherRemoveAnActivityFromBeginOrEndCertainRealDay(int d2,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)1368 inline bool Generate::teacherRemoveAnActivityFromBeginOrEndCertainRealDay(int d2, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
1369 {
1370 	QList<int> possibleDays;
1371 	QList<bool> atBeginning;
1372 	QList<int> acts;
1373 	int real_d=d2;
1374 
1375 	int d2_morning=real_d*2;
1376 	int d2_afternoon=real_d*2+1;
1377 
1378 	int actIndexBegin=-1;
1379 	if(tchDayNHours[d2_morning]>0){
1380 		int h2;
1381 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
1382 			if(tchTimetable(d2_morning,h2)>=0){
1383 				actIndexBegin=tchTimetable(d2_morning,h2);
1384 				break;
1385 			}
1386 		}
1387 		if(actIndexBegin>=0)
1388 			if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
1389 				actIndexBegin=-1;
1390 
1391 		if(actIndexBegin>=0){
1392 			assert(!acts.contains(actIndexBegin));
1393 			possibleDays.append(d2_morning);
1394 			atBeginning.append(true);
1395 			acts.append(actIndexBegin);
1396 		}
1397 	}
1398 	else if(tchDayNHours[d2_afternoon]>0){
1399 		int h2;
1400 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
1401 			if(tchTimetable(d2_afternoon,h2)>=0){
1402 				actIndexBegin=tchTimetable(d2_afternoon,h2);
1403 				break;
1404 			}
1405 		}
1406 		if(actIndexBegin>=0)
1407 			if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
1408 				actIndexBegin=-1;
1409 
1410 		if(actIndexBegin>=0){
1411 			assert(!acts.contains(actIndexBegin));
1412 			possibleDays.append(d2_afternoon);
1413 			atBeginning.append(true);
1414 			acts.append(actIndexBegin);
1415 		}
1416 	}
1417 
1418 	if(tchDayNHours[d2_afternoon]>0){
1419 		int actIndexEnd=-1;
1420 		int h2;
1421 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
1422 			if(tchTimetable(d2_afternoon,h2)>=0){
1423 				actIndexEnd=tchTimetable(d2_afternoon,h2);
1424 				break;
1425 			}
1426 		}
1427 		if(actIndexEnd>=0)
1428 			if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai || actIndexEnd==actIndexBegin)
1429 				actIndexEnd=-1;
1430 
1431 		if(actIndexEnd>=0){
1432 			assert(!acts.contains(actIndexEnd));
1433 			possibleDays.append(d2_afternoon);
1434 			atBeginning.append(false);
1435 			acts.append(actIndexEnd);
1436 		}
1437 	}
1438 	else if(tchDayNHours[d2_morning]>0){
1439 		int actIndexEnd=-1;
1440 		int h2;
1441 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
1442 			if(tchTimetable(d2_morning, h2)>=0){
1443 				actIndexEnd=tchTimetable(d2_morning,h2);
1444 				break;
1445 			}
1446 		}
1447 		if(actIndexEnd>=0)
1448 			if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai || actIndexEnd==actIndexBegin)
1449 				actIndexEnd=-1;
1450 
1451 		if(actIndexEnd>=0){
1452 			assert(!acts.contains(actIndexEnd));
1453 			possibleDays.append(d2_morning);
1454 			atBeginning.append(false);
1455 			acts.append(actIndexEnd);
1456 		}
1457 	}
1458 
1459 	bool possibleBeginOrEnd=true;
1460 	if(possibleDays.count()==0)
1461 		possibleBeginOrEnd=false;
1462 
1463 	if(possibleBeginOrEnd){
1464 		int t;
1465 
1466 		if(level==0){
1467 			int optMinWrong=INF;
1468 
1469 			QList<int> tl;
1470 
1471 			for(int q=0; q<acts.count(); q++){
1472 				int ai2=acts.at(q);
1473 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
1474 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
1475 				}
1476 			}
1477 
1478 			for(int q=0; q<acts.count(); q++){
1479 				int ai2=acts.at(q);
1480 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
1481 					tl.append(q);
1482 			}
1483 
1484 			assert(tl.count()>=1);
1485 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
1486 
1487 			assert(mpos>=0 && mpos<acts.count());
1488 			t=mpos;
1489 		}
1490 		else{
1491 			t=rng.intMRG32k3a(possibleDays.count());
1492 		}
1493 
1494 		assert(t>=0 && t<possibleDays.count());
1495 
1496 		int d2=possibleDays.at(t);
1497 		bool begin=atBeginning.at(t);
1498 		int ai2=acts.at(t);
1499 
1500 		removedActivity=ai2;
1501 
1502 		if(begin){
1503 			int h2;
1504 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
1505 				if(tchTimetable(d2,h2)>=0)
1506 					break;
1507 			assert(h2<gt.rules.nHoursPerDay);
1508 
1509 			assert(tchTimetable(d2,h2)==ai2);
1510 
1511 			assert(!conflActivities.contains(ai2));
1512 			conflActivities.append(ai2);
1513 			nConflActivities++;
1514 			assert(nConflActivities==conflActivities.count());
1515 		}
1516 		else{
1517 			int h2;
1518 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
1519 				if(tchTimetable(d2,h2)>=0)
1520 					break;
1521 			assert(h2>=0);
1522 
1523 			assert(tchTimetable(d2,h2)==ai2);
1524 
1525 			assert(!conflActivities.contains(ai2));
1526 			conflActivities.append(ai2);
1527 			nConflActivities++;
1528 			assert(nConflActivities==conflActivities.count());
1529 		}
1530 
1531 		return true;
1532 	}
1533 	else
1534 		return false;
1535 }
1536 
teacherRemoveAnActivityFromBeginOrEndCertainDay(int d2,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)1537 inline bool Generate::teacherRemoveAnActivityFromBeginOrEndCertainDay(int d2, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
1538 {
1539 	//Teacher: remove an activity from the beginning or from the end of a certain day
1540 	int actIndexBegin=-1, actIndexEnd=-1;
1541 
1542 	if(tchDayNHours[d2]>0){
1543 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
1544 			if(tchTimetable(d2,h2)>=0){
1545 				actIndexBegin=tchTimetable(d2,h2);
1546 				break;
1547 			}
1548 		}
1549 		if(actIndexBegin>=0)
1550 			if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
1551 				actIndexBegin=-1;
1552 		for(int h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
1553 			if(tchTimetable(d2,h2)>=0){
1554 				actIndexEnd=tchTimetable(d2,h2);
1555 				break;
1556 			}
1557 		}
1558 		if(actIndexEnd>=0)
1559 			if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai || actIndexEnd==actIndexBegin)
1560 				actIndexEnd=-1;
1561 	}
1562 
1563 	if(actIndexEnd>=0 || actIndexBegin>=0){
1564 		int ai2=-1;
1565 		if(level==0){
1566 			int optMinWrong=INF;
1567 
1568 			if(actIndexBegin>=0){
1569 				if(optMinWrong>triedRemovals(actIndexBegin,c.times[actIndexBegin])){
1570 					optMinWrong=triedRemovals(actIndexBegin,c.times[actIndexBegin]);
1571 				}
1572 				ai2=actIndexBegin;
1573 			}
1574 
1575 			if(actIndexEnd>=0){
1576 				if(optMinWrong>triedRemovals(actIndexEnd,c.times[actIndexEnd])){
1577 					optMinWrong=triedRemovals(actIndexEnd,c.times[actIndexEnd]);
1578 				}
1579 				ai2=actIndexEnd;
1580 			}
1581 
1582 			if(actIndexBegin>=0 && actIndexEnd>=0 && optMinWrong==triedRemovals(actIndexEnd,c.times[actIndexEnd]) &&
1583 			  optMinWrong==triedRemovals(actIndexBegin,c.times[actIndexBegin])){
1584 				if(rng.intMRG32k3a(2)==0)
1585 					ai2=actIndexBegin;
1586 				else
1587 					ai2=actIndexEnd;
1588 			}
1589 		}
1590 		else{
1591 			if(actIndexBegin>=0 && actIndexEnd<0)
1592 				ai2=actIndexBegin;
1593 			else if(actIndexEnd>=0 && actIndexBegin<0)
1594 				ai2=actIndexEnd;
1595 			else{
1596 				if(rng.intMRG32k3a(2)==0)
1597 					ai2=actIndexBegin;
1598 				else
1599 					ai2=actIndexEnd;
1600 			}
1601 		}
1602 		assert(ai2>=0);
1603 
1604 		removedActivity=ai2;
1605 
1606 		assert(!conflActivities.contains(ai2));
1607 		conflActivities.append(ai2);
1608 		nConflActivities++;
1609 		assert(nConflActivities==conflActivities.count());
1610 
1611 		return true;
1612 	}
1613 	else
1614 		return false;
1615 }
1616 
teacherRemoveAnActivityFromBeginOrEndCertainTwoDays(int d2,int d4,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)1617 inline bool Generate::teacherRemoveAnActivityFromBeginOrEndCertainTwoDays(int d2, int d4, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
1618 {
1619 	int actIndexBegin2=-1, actIndexEnd2=-1;
1620 	int actIndexBegin4=-1, actIndexEnd4=-1;
1621 
1622 	if(tchDayNHours[d2]>0){
1623 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
1624 			if(tchTimetable(d2,h2)>=0){
1625 				actIndexBegin2=tchTimetable(d2,h2);
1626 				break;
1627 			}
1628 		}
1629 		if(actIndexBegin2>=0)
1630 			if(fixedTimeActivity[actIndexBegin2] || swappedActivities[actIndexBegin2] || actIndexBegin2==ai)
1631 				actIndexBegin2=-1;
1632 		for(int h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
1633 			if(tchTimetable(d2,h2)>=0){
1634 				actIndexEnd2=tchTimetable(d2,h2);
1635 				break;
1636 			}
1637 		}
1638 		if(actIndexEnd2>=0)
1639 			if(fixedTimeActivity[actIndexEnd2] || swappedActivities[actIndexEnd2] || actIndexEnd2==ai || actIndexEnd2==actIndexBegin2)
1640 				actIndexEnd2=-1;
1641 	}
1642 	if(tchDayNHours[d4]>0){
1643 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
1644 			if(tchTimetable(d4,h2)>=0){
1645 				actIndexBegin4=tchTimetable(d4,h2);
1646 				break;
1647 			}
1648 		}
1649 		if(actIndexBegin4>=0)
1650 			if(fixedTimeActivity[actIndexBegin4] || swappedActivities[actIndexBegin4] || actIndexBegin4==ai)
1651 				actIndexBegin4=-1;
1652 		for(int h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
1653 			if(tchTimetable(d4,h2)>=0){
1654 				actIndexEnd4=tchTimetable(d4,h2);
1655 				break;
1656 			}
1657 		}
1658 		if(actIndexEnd4>=0)
1659 			if(fixedTimeActivity[actIndexEnd4] || swappedActivities[actIndexEnd4] || actIndexEnd4==ai || actIndexEnd4==actIndexBegin4)
1660 				actIndexEnd4=-1;
1661 	}
1662 
1663 	if(actIndexEnd2>=0 || actIndexBegin2>=0 || actIndexEnd4>=0 || actIndexBegin4>=0){
1664 		int ai2=-1;
1665 		if(level==0){
1666 			int optMinWrong=INF;
1667 
1668 			if(actIndexBegin2>=0){
1669 				if(optMinWrong>triedRemovals(actIndexBegin2,c.times[actIndexBegin2])){
1670 					optMinWrong=triedRemovals(actIndexBegin2,c.times[actIndexBegin2]);
1671 				}
1672 				ai2=actIndexBegin2;
1673 			}
1674 
1675 			if(actIndexEnd2>=0){
1676 				if(optMinWrong>triedRemovals(actIndexEnd2,c.times[actIndexEnd2])){
1677 					optMinWrong=triedRemovals(actIndexEnd2,c.times[actIndexEnd2]);
1678 				}
1679 				ai2=actIndexEnd2;
1680 			}
1681 
1682 			if(actIndexBegin4>=0){
1683 				if(optMinWrong>triedRemovals(actIndexBegin4,c.times[actIndexBegin4])){
1684 					optMinWrong=triedRemovals(actIndexBegin4,c.times[actIndexBegin4]);
1685 				}
1686 				ai2=actIndexBegin4;
1687 			}
1688 
1689 			if(actIndexEnd4>=0){
1690 				if(optMinWrong>triedRemovals(actIndexEnd4,c.times[actIndexEnd4])){
1691 					optMinWrong=triedRemovals(actIndexEnd4,c.times[actIndexEnd4]);
1692 				}
1693 				ai2=actIndexEnd4;
1694 			}
1695 
1696 			QList<int> tl;
1697 
1698 			if(actIndexBegin2>=0)
1699 				if(optMinWrong==triedRemovals(actIndexBegin2,c.times[actIndexBegin2]))
1700 					tl.append(0);
1701 			if(actIndexEnd2>=0)
1702 				if(optMinWrong==triedRemovals(actIndexEnd2,c.times[actIndexEnd2]))
1703 					tl.append(1);
1704 			if(actIndexBegin4>=0)
1705 				if(optMinWrong==triedRemovals(actIndexBegin4,c.times[actIndexBegin4]))
1706 					tl.append(2);
1707 			if(actIndexEnd4>=0)
1708 				if(optMinWrong==triedRemovals(actIndexEnd4,c.times[actIndexEnd4]))
1709 					tl.append(3);
1710 
1711 			int ii=tl.at(rng.intMRG32k3a(tl.count()));
1712 
1713 			if(ii==0)
1714 				ai2=actIndexBegin2;
1715 			else if(ii==1)
1716 				ai2=actIndexEnd2;
1717 			else if(ii==2)
1718 				ai2=actIndexBegin4;
1719 			else if(ii==3)
1720 				ai2=actIndexEnd4;
1721 
1722 			/*
1723 			if(actIndexBegin>=0 && actIndexEnd>=0 && optMinWrong==triedRemovals[actIndexEnd][c.times[actIndexEnd]] &&
1724 			  optMinWrong==triedRemovals[actIndexBegin][c.times[actIndexBegin]]){
1725 				if(rng.intMRG32k3a()%2==0)
1726 					ai2=actIndexBegin;
1727 				else
1728 					ai2=actIndexEnd;
1729 			}*/
1730 		}
1731 		else{
1732 			QList<int> tl;
1733 
1734 			if(actIndexBegin2>=0)
1735 				tl.append(0);
1736 			if(actIndexEnd2>=0)
1737 				tl.append(1);
1738 			if(actIndexBegin4>=0)
1739 				tl.append(2);
1740 			if(actIndexEnd4>=0)
1741 				tl.append(3);
1742 
1743 			int ii=tl.at(rng.intMRG32k3a(tl.count()));
1744 
1745 			if(ii==0)
1746 				ai2=actIndexBegin2;
1747 			else if(ii==1)
1748 				ai2=actIndexEnd2;
1749 			else if(ii==2)
1750 				ai2=actIndexBegin4;
1751 			else if(ii==3)
1752 				ai2=actIndexEnd4;
1753 /*
1754 
1755 			if(actIndexBegin>=0 && actIndexEnd<0)
1756 				ai2=actIndexBegin;
1757 			else if(actIndexEnd>=0 && actIndexBegin<0)
1758 				ai2=actIndexEnd;
1759 			else{
1760 				if(rng.intMRG32k3a()%2==0)
1761 					ai2=actIndexBegin;
1762 				else
1763 					ai2=actIndexEnd;
1764 			}*/
1765 		}
1766 		assert(ai2>=0);
1767 
1768 		removedActivity=ai2;
1769 
1770 		assert(!conflActivities.contains(ai2));
1771 		conflActivities.append(ai2);
1772 		nConflActivities++;
1773 		assert(nConflActivities==conflActivities.count());
1774 
1775 		return true;
1776 	}
1777 	else
1778 		return false;
1779 }
1780 
teacherRemoveAnActivityFromAnywhere(int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)1781 inline bool Generate::teacherRemoveAnActivityFromAnywhere(int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
1782 {
1783 	//Teacher: remove an activity from anywhere
1784 	QList<int> acts;
1785 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
1786 		if(tchDayNHours[d2]>0){
1787 			int actIndex=-1;
1788 			int h2;
1789 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
1790 				if(tchTimetable(d2,h2)>=0){
1791 					actIndex=tchTimetable(d2,h2);
1792 
1793 					if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex))
1794 						actIndex=-1;
1795 
1796 					if(actIndex>=0){
1797 						assert(!acts.contains(actIndex));
1798 						acts.append(actIndex);
1799 					}
1800 				}
1801 		}
1802 	}
1803 
1804 	if(acts.count()>0){
1805 		int t;
1806 
1807 		if(level==0){
1808 			int optMinWrong=INF;
1809 
1810 			QList<int> tl;
1811 
1812 			for(int q=0; q<acts.count(); q++){
1813 				int ai2=acts.at(q);
1814 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
1815 				 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
1816 				}
1817 			}
1818 
1819 			for(int q=0; q<acts.count(); q++){
1820 				int ai2=acts.at(q);
1821 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
1822 					tl.append(q);
1823 			}
1824 
1825 			assert(tl.count()>=1);
1826 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
1827 
1828 			assert(mpos>=0 && mpos<acts.count());
1829 			t=mpos;
1830 		}
1831 		else{
1832 			t=rng.intMRG32k3a(acts.count());
1833 		}
1834 
1835 		int ai2=acts.at(t);
1836 
1837 		removedActivity=ai2;
1838 
1839 		assert(!conflActivities.contains(ai2));
1840 		conflActivities.append(ai2);
1841 		nConflActivities++;
1842 		assert(nConflActivities==conflActivities.count());
1843 
1844 		return true;
1845 	}
1846 	else
1847 		return false;
1848 }
1849 
teacherRemoveAnActivityFromAnywhereCertainDay(int d2,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)1850 inline bool Generate::teacherRemoveAnActivityFromAnywhereCertainDay(int d2, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
1851 {
1852 	//Teacher: remove an activity from anywhere certain day
1853 	QList<int> acts;
1854 	if(tchDayNHours[d2]>0){
1855 		int actIndex=-1;
1856 		int h2;
1857 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
1858 			if(tchTimetable(d2,h2)>=0){
1859 				actIndex=tchTimetable(d2,h2);
1860 
1861 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex))
1862 					actIndex=-1;
1863 
1864 				if(actIndex>=0){
1865 					assert(!acts.contains(actIndex));
1866 					acts.append(actIndex);
1867 				}
1868 			}
1869 	}
1870 
1871 	if(acts.count()>0){
1872 		int t;
1873 
1874 		if(level==0){
1875 			int optMinWrong=INF;
1876 
1877 			QList<int> tl;
1878 
1879 			for(int q=0; q<acts.count(); q++){
1880 				int ai2=acts.at(q);
1881 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
1882 				 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
1883 				}
1884 			}
1885 
1886 			for(int q=0; q<acts.count(); q++){
1887 				int ai2=acts.at(q);
1888 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
1889 					tl.append(q);
1890 			}
1891 
1892 			assert(tl.count()>=1);
1893 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
1894 
1895 			assert(mpos>=0 && mpos<acts.count());
1896 			t=mpos;
1897 		}
1898 		else{
1899 			t=rng.intMRG32k3a(acts.count());
1900 		}
1901 
1902 		int ai2=acts.at(t);
1903 
1904 		removedActivity=ai2;
1905 
1906 		assert(!conflActivities.contains(ai2));
1907 		conflActivities.append(ai2);
1908 		nConflActivities++;
1909 		assert(nConflActivities==conflActivities.count());
1910 
1911 		return true;
1912 	}
1913 	else
1914 		return false;
1915 }
1916 
teacherRemoveAnActivityFromAnywhereCertainDayCertainActivityTag(int d2,int actTag,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)1917 inline bool Generate::teacherRemoveAnActivityFromAnywhereCertainDayCertainActivityTag(int d2, int actTag, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
1918 {
1919 	//Teacher: remove an activity from anywhere certain day certain activity tag
1920 	QList<int> acts;
1921 	if(tchDayNHours[d2]>0){
1922 		int actIndex=-1;
1923 		int h2;
1924 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
1925 			if(tchTimetable(d2,h2)>=0){
1926 				actIndex=tchTimetable(d2,h2);
1927 
1928 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex) || !gt.rules.internalActivitiesList[actIndex].iActivityTagsSet.contains(actTag))
1929 					actIndex=-1;
1930 
1931 				if(actIndex>=0){
1932 					assert(!acts.contains(actIndex));
1933 					acts.append(actIndex);
1934 				}
1935 			}
1936 	}
1937 
1938 	if(acts.count()>0){
1939 		int t;
1940 
1941 		if(level==0){
1942 			int optMinWrong=INF;
1943 
1944 			QList<int> tl;
1945 
1946 			for(int q=0; q<acts.count(); q++){
1947 				int ai2=acts.at(q);
1948 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
1949 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
1950 				}
1951 			}
1952 
1953 			for(int q=0; q<acts.count(); q++){
1954 				int ai2=acts.at(q);
1955 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
1956 					tl.append(q);
1957 			}
1958 
1959 			assert(tl.count()>=1);
1960 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
1961 
1962 			assert(mpos>=0 && mpos<acts.count());
1963 			t=mpos;
1964 		}
1965 		else{
1966 			t=rng.intMRG32k3a(acts.count());
1967 		}
1968 
1969 		int ai2=acts.at(t);
1970 
1971 		removedActivity=ai2;
1972 
1973 		assert(!conflActivities.contains(ai2));
1974 		conflActivities.append(ai2);
1975 		nConflActivities++;
1976 		assert(nConflActivities==conflActivities.count());
1977 
1978 		return true;
1979 	}
1980 	else
1981 		return false;
1982 }
1983 
teacherRemoveAnActivityFromAnywhereCertainDayDayPairCertainActivityTag(int d2,int dpair2,int actTag,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)1984 inline bool Generate::teacherRemoveAnActivityFromAnywhereCertainDayDayPairCertainActivityTag(int d2, int dpair2, int actTag, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
1985 {
1986 	QList<int> acts;
1987 	if(tchDayNHours[d2]>0){
1988 		int actIndex=-1;
1989 		int h2;
1990 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
1991 			if(tchTimetable(d2,h2)>=0){
1992 				actIndex=tchTimetable(d2,h2);
1993 
1994 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex) || !gt.rules.internalActivitiesList[actIndex].iActivityTagsSet.contains(actTag))
1995 					actIndex=-1;
1996 
1997 				if(actIndex>=0){
1998 					assert(!acts.contains(actIndex));
1999 					acts.append(actIndex);
2000 				}
2001 			}
2002 	}
2003 	if(tchDayNHours[dpair2]>0){
2004 		int actIndex=-1;
2005 		int h2;
2006 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
2007 			if(tchTimetable(dpair2,h2)>=0){
2008 				actIndex=tchTimetable(dpair2,h2);
2009 
2010 				assert(actIndex!=ai);
2011 
2012 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || acts.contains(actIndex) || !gt.rules.internalActivitiesList[actIndex].iActivityTagsSet.contains(actTag))
2013 					actIndex=-1;
2014 
2015 				if(actIndex>=0){
2016 					assert(!acts.contains(actIndex));
2017 					acts.append(actIndex);
2018 				}
2019 			}
2020 	}
2021 
2022 	if(acts.count()>0){
2023 		int t;
2024 
2025 		if(level==0){
2026 			int optMinWrong=INF;
2027 
2028 			QList<int> tl;
2029 
2030 			for(int q=0; q<acts.count(); q++){
2031 				int ai2=acts.at(q);
2032 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
2033 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
2034 				}
2035 			}
2036 
2037 			for(int q=0; q<acts.count(); q++){
2038 				int ai2=acts.at(q);
2039 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
2040 					tl.append(q);
2041 			}
2042 
2043 			assert(tl.count()>=1);
2044 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
2045 
2046 			assert(mpos>=0 && mpos<acts.count());
2047 			t=mpos;
2048 		}
2049 		else{
2050 			t=rng.intMRG32k3a(acts.count());
2051 		}
2052 
2053 		int ai2=acts.at(t);
2054 
2055 		removedActivity=ai2;
2056 
2057 		assert(!conflActivities.contains(ai2));
2058 		conflActivities.append(ai2);
2059 		nConflActivities++;
2060 		assert(nConflActivities==conflActivities.count());
2061 
2062 		return true;
2063 	}
2064 	else
2065 		return false;
2066 }
2067 
teacherRemoveAnActivityFromAnywhereCertainTwoDays(int d2,int d4,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)2068 inline bool Generate::teacherRemoveAnActivityFromAnywhereCertainTwoDays(int d2, int d4, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
2069 {
2070 	QList<int> acts;
2071 	if(tchDayNHours[d2]>0){
2072 		int actIndex=-1;
2073 		int h2;
2074 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
2075 			if(tchTimetable(d2,h2)>=0){
2076 				actIndex=tchTimetable(d2,h2);
2077 
2078 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex))
2079 					actIndex=-1;
2080 
2081 				if(actIndex>=0){
2082 					assert(!acts.contains(actIndex));
2083 					acts.append(actIndex);
2084 				}
2085 			}
2086 	}
2087 
2088 	if(tchDayNHours[d4]>0){
2089 		int actIndex=-1;
2090 		int h2;
2091 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
2092 			if(tchTimetable(d4,h2)>=0){
2093 				actIndex=tchTimetable(d4,h2);
2094 
2095 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex))
2096 					actIndex=-1;
2097 
2098 				if(actIndex>=0){
2099 					assert(!acts.contains(actIndex));
2100 					acts.append(actIndex);
2101 				}
2102 			}
2103 	}
2104 
2105 	if(acts.count()>0){
2106 		int t;
2107 
2108 		if(level==0){
2109 			int optMinWrong=INF;
2110 
2111 			QList<int> tl;
2112 
2113 			for(int q=0; q<acts.count(); q++){
2114 				int ai2=acts.at(q);
2115 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
2116 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
2117 				}
2118 			}
2119 
2120 			for(int q=0; q<acts.count(); q++){
2121 				int ai2=acts.at(q);
2122 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
2123 					tl.append(q);
2124 			}
2125 
2126 			assert(tl.count()>=1);
2127 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
2128 
2129 			assert(mpos>=0 && mpos<acts.count());
2130 			t=mpos;
2131 		}
2132 		else{
2133 			t=rng.intMRG32k3a(acts.count());
2134 		}
2135 
2136 		int ai2=acts.at(t);
2137 
2138 		removedActivity=ai2;
2139 
2140 		assert(!conflActivities.contains(ai2));
2141 		conflActivities.append(ai2);
2142 		nConflActivities++;
2143 		assert(nConflActivities==conflActivities.count());
2144 
2145 		return true;
2146 	}
2147 	else
2148 		return false;
2149 }
2150 
2151 //students
subgroupGetNHoursGaps(int sbg)2152 inline void Generate::subgroupGetNHoursGaps(int sbg)
2153 {
2154 	if(!mustComputeTimetableSubgroup[sbg])
2155 		return;
2156 
2157 	for(int d=0; d<gt.rules.nDaysPerWeek; d++){
2158 		int hours=0, gaps=0, nfirstgaps=0;
2159 
2160 		int h;
2161 		for(h=0; h<gt.rules.nHoursPerDay; h++){
2162 			if(newSubgroupsTimetable(sbg,d,h)>=0)
2163 				break;
2164 			else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
2165 				nfirstgaps++;
2166 		}
2167 		int ng=0;
2168 		for(; h<gt.rules.nHoursPerDay; h++){
2169 			if(newSubgroupsTimetable(sbg,d,h)>=0){
2170 				hours++;
2171 				gaps+=ng;
2172 				ng=0;
2173 			}
2174 			else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
2175 				ng++;
2176 		}
2177 		newSubgroupsDayNGaps(sbg,d)=gaps;
2178 		newSubgroupsDayNHours(sbg,d)=hours;
2179 		if(hours>0)
2180 			newSubgroupsDayNFirstGaps(sbg,d)=nfirstgaps;
2181 		else
2182 			newSubgroupsDayNFirstGaps(sbg,d)=0;
2183 	}
2184 
2185 /*	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
2186 		newSubgroupsDayNHours(sbg,d2)=0;
2187 		newSubgroupsDayNGaps(sbg,d2)=0;
2188 	}
2189 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
2190 		bool countGaps=false;
2191 		int ng=0;
2192 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
2193 			if(newSubgroupsTimetable(sbg,d2,h2)>=0){
2194 				newSubgroupsDayNHours(sbg,d2)++;
2195 				if(countGaps)
2196 					newSubgroupsDayNGaps(sbg,d2)+=ng;
2197 				else
2198 					countGaps=true;
2199 				ng=0;
2200 			}
2201 			else if(!breakDayHour(d2,h2) && !subgroupNotAvailableDayHour(sbg,d2,h2))
2202 				ng++;
2203 		}
2204 	}*/
2205 
2206 	if(gt.rules.mode==MORNINGS_AFTERNOONS && haveStudentsMaxGapsPerRealDay){
2207 		for(int real_d=0; real_d<gt.rules.nDaysPerWeek/2; real_d++){
2208 			int hours=0, gaps=0, nfirstgaps=0;
2209 			int hours_first_half=0;
2210 			int nfirstgaps_first_half=0;
2211 
2212 			int h;
2213 			int d;
2214 			int double_h;
2215 			assert(gt.rules.nHoursPerDay>0);
2216 			for(double_h=0; double_h<2*gt.rules.nHoursPerDay; double_h++){
2217 				if(double_h<gt.rules.nHoursPerDay)
2218 					d=real_d*2;
2219 				else
2220 					d=real_d*2+1;
2221 				h=double_h%gt.rules.nHoursPerDay;
2222 				if(newSubgroupsTimetable(sbg,d,h)>=0)
2223 					break;
2224 				else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h)){
2225 					nfirstgaps++;
2226 					if(d%2==0)
2227 						nfirstgaps_first_half++;
2228 				}
2229 			}
2230 			int ng=0;
2231 			for(; double_h<2*gt.rules.nHoursPerDay; double_h++){
2232 				if(double_h<gt.rules.nHoursPerDay)
2233 					d=real_d*2;
2234 				else
2235 					d=real_d*2+1;
2236 				h=double_h%gt.rules.nHoursPerDay;
2237 				if(newSubgroupsTimetable(sbg,d,h)>=0){
2238 					hours++;
2239 					if(d%2==0)
2240 						hours_first_half++;
2241 					gaps+=ng;
2242 					ng=0;
2243 				}
2244 				else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
2245 					ng++;
2246 			}
2247 			newSubgroupsRealDayNGaps(sbg,real_d)=gaps;
2248 			newSubgroupsRealDayNHours(sbg,real_d)=hours;
2249 			if(hours_first_half>0)
2250 				newSubgroupsRealDayNFirstGaps(sbg,real_d)=nfirstgaps;
2251 			else if(hours>0)
2252 				newSubgroupsRealDayNFirstGaps(sbg,real_d)=nfirstgaps-nfirstgaps_first_half;
2253 			else
2254 				newSubgroupsRealDayNFirstGaps(sbg,real_d)=0;
2255 		}
2256 	}
2257 }
2258 
sbgGetNHoursGaps(int sbg)2259 inline void Generate::sbgGetNHoursGaps(int sbg)
2260 {
2261 	for(int d=0; d<gt.rules.nDaysPerWeek; d++){
2262 		int hours=0, gaps=0, nfirstgaps=0;
2263 
2264 		int h;
2265 		for(h=0; h<gt.rules.nHoursPerDay; h++){
2266 			if(sbgTimetable(d,h)>=0)
2267 				break;
2268 			else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
2269 				nfirstgaps++;
2270 		}
2271 		int ng=0;
2272 		for(; h<gt.rules.nHoursPerDay; h++){
2273 			if(sbgTimetable(d,h)>=0){
2274 				hours++;
2275 				gaps+=ng;
2276 				ng=0;
2277 			}
2278 			else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
2279 				ng++;
2280 		}
2281 
2282 		sbgDayNGaps[d]=gaps;
2283 		sbgDayNHours[d]=hours;
2284 		if(sbgDayNHours[d]>0)
2285 			sbgDayNFirstGaps[d]=nfirstgaps;
2286 		else
2287 			sbgDayNFirstGaps[d]=0;
2288 	}
2289 
2290 	/*
2291 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
2292 		sbgDayNHours[d2]=0;
2293 		sbgDayNGaps[d2]=0;
2294 		sbgDayNFirstGaps[d2]=0;
2295 	}
2296 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
2297 		bool countGaps=false;
2298 		int ng=0;
2299 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
2300 		if(sbgTimetable(d2,h2)>=0){
2301 				sbgDayNHours[d2]++;
2302 				if(countGaps)
2303 					sbgDayNGaps[d2]+=ng;
2304 				else
2305 					countGaps=true;
2306 				ng=0;
2307 			}
2308 			else if(!breakDayHour(d2,h2) && !subgroupNotAvailableDayHour(sbg,d2,h2))
2309 				ng++;
2310 		}
2311 	}*/
2312 }
2313 
sbgGetNHoursGapsRealDays(int sbg)2314 inline void Generate::sbgGetNHoursGapsRealDays(int sbg)
2315 {
2316 	assert(gt.rules.mode==MORNINGS_AFTERNOONS);
2317 	for(int real_d=0; real_d<gt.rules.nDaysPerWeek/2; real_d++){
2318 		int hours=0, gaps=0, nfirstgaps=0;
2319 		int hours_first_half=0;
2320 		int nfirstgaps_first_half=0;
2321 
2322 		int h;
2323 		int d;
2324 		int double_h;
2325 		assert(gt.rules.nHoursPerDay>0);
2326 		for(double_h=0; double_h<2*gt.rules.nHoursPerDay; double_h++){
2327 			if(double_h<gt.rules.nHoursPerDay)
2328 				d=real_d*2;
2329 			else
2330 				d=real_d*2+1;
2331 			h=double_h%gt.rules.nHoursPerDay;
2332 			if(sbgTimetable(d,h)>=0)
2333 				break;
2334 			else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h)){
2335 				nfirstgaps++;
2336 				if(d%2==0)
2337 					nfirstgaps_first_half++;
2338 			}
2339 		}
2340 		int ng=0;
2341 		for(; double_h<2*gt.rules.nHoursPerDay; double_h++){
2342 			if(double_h<gt.rules.nHoursPerDay)
2343 				d=real_d*2;
2344 			else
2345 				d=real_d*2+1;
2346 			h=double_h%gt.rules.nHoursPerDay;
2347 			if(sbgTimetable(d,h)>=0){
2348 				hours++;
2349 				if(d%2==0)
2350 					hours_first_half++;
2351 				gaps+=ng;
2352 				ng=0;
2353 			}
2354 			else if(!breakDayHour(d,h) && !subgroupNotAvailableDayHour(sbg,d,h))
2355 				ng++;
2356 		}
2357 
2358 		sbgRealDayNGaps[real_d]=gaps;
2359 		sbgRealDayNHours[real_d]=hours;
2360 		if(hours_first_half>0)
2361 			sbgRealDayNFirstGaps[real_d]=nfirstgaps;
2362 		else if(hours>0)
2363 			sbgRealDayNFirstGaps[real_d]=nfirstgaps-nfirstgaps_first_half;
2364 		else
2365 			sbgRealDayNFirstGaps[real_d]=0;
2366 	}
2367 }
2368 
subgroupRemoveAnActivityFromBeginOrEnd(int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)2369 inline bool Generate::subgroupRemoveAnActivityFromBeginOrEnd(int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
2370 {
2371 	//Subgroup: remove an activity from the beginning or from the end of any day
2372 	QList<int> possibleDays;
2373 	QList<bool> atBeginning;
2374 	QList<int> acts;
2375 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
2376 		if(sbgDayNHours[d2]>0){
2377 			int actIndexBegin=-1, actIndexEnd=-1;
2378 			int h2;
2379 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
2380 				if(sbgTimetable(d2,h2)>=0){
2381 					actIndexBegin=sbgTimetable(d2,h2);
2382 					break;
2383 				}
2384 			}
2385 			if(actIndexBegin>=0)
2386 				if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
2387 					actIndexBegin=-1;
2388 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
2389 				if(sbgTimetable(d2,h2)>=0){
2390 					actIndexEnd=sbgTimetable(d2,h2);
2391 					break;
2392 				}
2393 			}
2394 			if(actIndexEnd>=0)
2395 				if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai || actIndexEnd==actIndexBegin)
2396 					actIndexEnd=-1;
2397 
2398 			if(actIndexBegin>=0){
2399 				assert(!acts.contains(actIndexBegin));
2400 				possibleDays.append(d2);
2401 				atBeginning.append(true);
2402 				acts.append(actIndexBegin);
2403 			}
2404 			if(actIndexEnd>=0){
2405 				assert(!acts.contains(actIndexEnd));
2406 				possibleDays.append(d2);
2407 				atBeginning.append(false);
2408 				acts.append(actIndexEnd);
2409 			}
2410 		}
2411 	}
2412 
2413 	bool possibleBeginOrEnd=true;
2414 	if(possibleDays.count()==0)
2415 		possibleBeginOrEnd=false;
2416 
2417 	if(possibleBeginOrEnd){
2418 		int t;
2419 
2420 		if(level==0){
2421 			int optMinWrong=INF;
2422 
2423 			QList<int> tl;
2424 
2425 			for(int q=0; q<acts.count(); q++){
2426 				int ai2=acts.at(q);
2427 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
2428 				 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
2429 				}
2430 			}
2431 
2432 			for(int q=0; q<acts.count(); q++){
2433 				int ai2=acts.at(q);
2434 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
2435 					tl.append(q);
2436 			}
2437 
2438 			assert(tl.count()>=1);
2439 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
2440 
2441 			assert(mpos>=0 && mpos<acts.count());
2442 			t=mpos;
2443 		}
2444 		else{
2445 			t=rng.intMRG32k3a(possibleDays.count());
2446 		}
2447 
2448 		assert(t>=0 && t<possibleDays.count());
2449 
2450 		int d2=possibleDays.at(t);
2451 		bool begin=atBeginning.at(t);
2452 		int ai2=acts.at(t);
2453 
2454 		removedActivity=ai2;
2455 
2456 		if(begin){
2457 			int h2;
2458 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
2459 				if(sbgTimetable(d2,h2)>=0)
2460 					break;
2461 			assert(h2<gt.rules.nHoursPerDay);
2462 
2463 			assert(sbgTimetable(d2,h2)==ai2);
2464 
2465 			assert(!conflActivities.contains(ai2));
2466 			conflActivities.append(ai2);
2467 			nConflActivities++;
2468 			assert(nConflActivities==conflActivities.count());
2469 		}
2470 		else{
2471 			int h2;
2472 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
2473 				if(sbgTimetable(d2,h2)>=0)
2474 					break;
2475 			assert(h2>=0);
2476 
2477 			assert(sbgTimetable(d2,h2)==ai2);
2478 
2479 			assert(!conflActivities.contains(ai2));
2480 			conflActivities.append(ai2);
2481 			nConflActivities++;
2482 			assert(nConflActivities==conflActivities.count());
2483 		}
2484 
2485 		return true;
2486 	}
2487 	else
2488 		return false;
2489 }
2490 
subgroupRemoveAnActivityFromBeginMorningOrEndAfternoon(int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)2491 inline bool Generate::subgroupRemoveAnActivityFromBeginMorningOrEndAfternoon(int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
2492 {
2493 	QList<int> possibleDays;
2494 	QList<bool> atBeginning;
2495 	QList<int> acts;
2496 	for(int real_d=0; real_d<gt.rules.nDaysPerWeek/2; real_d++){
2497 		int d2_morning=real_d*2;
2498 		int d2_afternoon=real_d*2+1;
2499 
2500 		if(sbgDayNHours[d2_morning]>0){
2501 			int actIndexBegin=-1;
2502 			int h2;
2503 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
2504 				if(sbgTimetable(d2_morning,h2)>=0){
2505 					actIndexBegin=sbgTimetable(d2_morning,h2);
2506 					break;
2507 				}
2508 			}
2509 			if(actIndexBegin>=0)
2510 				if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
2511 					actIndexBegin=-1;
2512 
2513 			if(actIndexBegin>=0){
2514 				assert(!acts.contains(actIndexBegin));
2515 				possibleDays.append(d2_morning);
2516 				atBeginning.append(true);
2517 				acts.append(actIndexBegin);
2518 			}
2519 		}
2520 
2521 		if(sbgDayNHours[d2_afternoon]>0){
2522 			int actIndexEnd=-1;
2523 			int h2;
2524 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
2525 				if(sbgTimetable(d2_afternoon,h2)>=0){
2526 					actIndexEnd=sbgTimetable(d2_afternoon,h2);
2527 					break;
2528 				}
2529 			}
2530 			if(actIndexEnd>=0)
2531 				if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai /* || actIndexEnd==actIndexBegin */)
2532 					actIndexEnd=-1;
2533 
2534 			if(actIndexEnd>=0){
2535 				assert(!acts.contains(actIndexEnd));
2536 				possibleDays.append(d2_afternoon);
2537 				atBeginning.append(false);
2538 				acts.append(actIndexEnd);
2539 			}
2540 		}
2541 	}
2542 
2543 	bool possibleBeginOrEnd=true;
2544 	if(possibleDays.count()==0)
2545 		possibleBeginOrEnd=false;
2546 
2547 	if(possibleBeginOrEnd){
2548 		int t;
2549 
2550 		if(level==0){
2551 			int optMinWrong=INF;
2552 
2553 			QList<int> tl;
2554 
2555 			for(int q=0; q<acts.count(); q++){
2556 				int ai2=acts.at(q);
2557 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
2558 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
2559 				}
2560 			}
2561 
2562 			for(int q=0; q<acts.count(); q++){
2563 				int ai2=acts.at(q);
2564 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
2565 					tl.append(q);
2566 			}
2567 
2568 			assert(tl.count()>=1);
2569 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
2570 
2571 			assert(mpos>=0 && mpos<acts.count());
2572 			t=mpos;
2573 		}
2574 		else{
2575 			t=rng.intMRG32k3a(possibleDays.count());
2576 		}
2577 
2578 		assert(t>=0 && t<possibleDays.count());
2579 
2580 		int d2=possibleDays.at(t);
2581 		bool begin=atBeginning.at(t);
2582 		int ai2=acts.at(t);
2583 
2584 		removedActivity=ai2;
2585 
2586 		if(begin){
2587 			int h2;
2588 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
2589 				if(sbgTimetable(d2,h2)>=0)
2590 					break;
2591 			assert(h2<gt.rules.nHoursPerDay);
2592 
2593 			assert(sbgTimetable(d2,h2)==ai2);
2594 
2595 			assert(!conflActivities.contains(ai2));
2596 			conflActivities.append(ai2);
2597 			nConflActivities++;
2598 			assert(nConflActivities==conflActivities.count());
2599 		}
2600 		else{
2601 			int h2;
2602 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
2603 				if(sbgTimetable(d2,h2)>=0)
2604 					break;
2605 			assert(h2>=0);
2606 
2607 			assert(sbgTimetable(d2,h2)==ai2);
2608 
2609 			assert(!conflActivities.contains(ai2));
2610 			conflActivities.append(ai2);
2611 			nConflActivities++;
2612 			assert(nConflActivities==conflActivities.count());
2613 		}
2614 
2615 		return true;
2616 	}
2617 	else
2618 		return false;
2619 }
2620 
subgroupRemoveAnActivityFromBeginOrEndCertainRealDay(int d2,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)2621 inline bool Generate::subgroupRemoveAnActivityFromBeginOrEndCertainRealDay(int d2, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
2622 {
2623 	QList<int> possibleDays;
2624 	QList<bool> atBeginning;
2625 	QList<int> acts;
2626 	int real_d=d2;
2627 
2628 	int d2_morning=real_d*2;
2629 	int d2_afternoon=real_d*2+1;
2630 
2631 	int actIndexBegin=-1;
2632 	if(sbgDayNHours[d2_morning]>0){
2633 		int h2;
2634 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
2635 			if(sbgTimetable(d2_morning,h2)>=0){
2636 				actIndexBegin=sbgTimetable(d2_morning,h2);
2637 				break;
2638 			}
2639 		}
2640 		if(actIndexBegin>=0)
2641 			if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
2642 				actIndexBegin=-1;
2643 
2644 		if(actIndexBegin>=0){
2645 			assert(!acts.contains(actIndexBegin));
2646 			possibleDays.append(d2_morning);
2647 			atBeginning.append(true);
2648 			acts.append(actIndexBegin);
2649 		}
2650 	}
2651 	else if(sbgDayNHours[d2_afternoon]>0){
2652 		int h2;
2653 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
2654 			if(sbgTimetable(d2_afternoon,h2)>=0){
2655 				actIndexBegin=sbgTimetable(d2_afternoon,h2);
2656 				break;
2657 			}
2658 		}
2659 		if(actIndexBegin>=0)
2660 			if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
2661 				actIndexBegin=-1;
2662 
2663 		if(actIndexBegin>=0){
2664 			assert(!acts.contains(actIndexBegin));
2665 			possibleDays.append(d2_afternoon);
2666 			atBeginning.append(true);
2667 			acts.append(actIndexBegin);
2668 		}
2669 	}
2670 
2671 	if(sbgDayNHours[d2_afternoon]>0){
2672 		int actIndexEnd=-1;
2673 		int h2;
2674 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
2675 			if(sbgTimetable(d2_afternoon,h2)>=0){
2676 				actIndexEnd=sbgTimetable(d2_afternoon,h2);
2677 				break;
2678 			}
2679 		}
2680 		if(actIndexEnd>=0)
2681 			if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai || actIndexEnd==actIndexBegin)
2682 				actIndexEnd=-1;
2683 
2684 		if(actIndexEnd>=0){
2685 			assert(!acts.contains(actIndexEnd));
2686 			possibleDays.append(d2_afternoon);
2687 			atBeginning.append(false);
2688 			acts.append(actIndexEnd);
2689 		}
2690 	}
2691 	else if(sbgDayNHours[d2_morning]>0){
2692 		int actIndexEnd=-1;
2693 		int h2;
2694 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
2695 			if(sbgTimetable(d2_morning,h2)>=0){
2696 				actIndexEnd=sbgTimetable(d2_morning,h2);
2697 				break;
2698 			}
2699 		}
2700 		if(actIndexEnd>=0)
2701 			if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai || actIndexEnd==actIndexBegin)
2702 				actIndexEnd=-1;
2703 
2704 		if(actIndexEnd>=0){
2705 			assert(!acts.contains(actIndexEnd));
2706 			possibleDays.append(d2_morning);
2707 			atBeginning.append(false);
2708 			acts.append(actIndexEnd);
2709 		}
2710 	}
2711 
2712 	bool possibleBeginOrEnd=true;
2713 	if(possibleDays.count()==0)
2714 		possibleBeginOrEnd=false;
2715 
2716 	if(possibleBeginOrEnd){
2717 		int t;
2718 
2719 		if(level==0){
2720 			int optMinWrong=INF;
2721 
2722 			QList<int> tl;
2723 
2724 			for(int q=0; q<acts.count(); q++){
2725 				int ai2=acts.at(q);
2726 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
2727 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
2728 				}
2729 			}
2730 
2731 			for(int q=0; q<acts.count(); q++){
2732 				int ai2=acts.at(q);
2733 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
2734 					tl.append(q);
2735 			}
2736 
2737 			assert(tl.count()>=1);
2738 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
2739 
2740 			assert(mpos>=0 && mpos<acts.count());
2741 			t=mpos;
2742 		}
2743 		else{
2744 			t=rng.intMRG32k3a(possibleDays.count());
2745 		}
2746 
2747 		assert(t>=0 && t<possibleDays.count());
2748 
2749 		int d2=possibleDays.at(t);
2750 		bool begin=atBeginning.at(t);
2751 		int ai2=acts.at(t);
2752 
2753 		removedActivity=ai2;
2754 
2755 		if(begin){
2756 			int h2;
2757 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
2758 				if(sbgTimetable(d2,h2)>=0)
2759 					break;
2760 			assert(h2<gt.rules.nHoursPerDay);
2761 
2762 			assert(sbgTimetable(d2,h2)==ai2);
2763 
2764 			assert(!conflActivities.contains(ai2));
2765 			conflActivities.append(ai2);
2766 			nConflActivities++;
2767 			assert(nConflActivities==conflActivities.count());
2768 		}
2769 		else{
2770 			int h2;
2771 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
2772 				if(sbgTimetable(d2,h2)>=0)
2773 					break;
2774 			assert(h2>=0);
2775 
2776 			assert(sbgTimetable(d2,h2)==ai2);
2777 
2778 			assert(!conflActivities.contains(ai2));
2779 			conflActivities.append(ai2);
2780 			nConflActivities++;
2781 			assert(nConflActivities==conflActivities.count());
2782 		}
2783 
2784 		return true;
2785 	}
2786 	else
2787 		return false;
2788 }
2789 
subgroupRemoveAnActivityFromEndCertainRealDay(int d2,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)2790 inline bool Generate::subgroupRemoveAnActivityFromEndCertainRealDay(int d2, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
2791 {
2792 	QList<int> possibleDays;
2793 	QList<bool> atBeginning;
2794 	QList<int> acts;
2795 	int real_d=d2;
2796 
2797 	int d2_morning=real_d*2;
2798 	int d2_afternoon=real_d*2+1;
2799 
2800 	/*if(sbgDayNHours[d2_morning]>0){
2801 		int actIndexBegin=-1;
2802 		int h2;
2803 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
2804 			if(sbgTimetable(d2_morning,h2)>=0){
2805 				actIndexBegin=sbgTimetable(d2_morning,h2);
2806 				break;
2807 			}
2808 		}
2809 		if(actIndexBegin>=0)
2810 			if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
2811 				actIndexBegin=-1;
2812 
2813 		if(actIndexBegin>=0){
2814 			assert(!acts.contains(actIndexBegin));
2815 			possibleDays.append(d2_morning);
2816 			atBeginning.append(true);
2817 			acts.append(actIndexBegin);
2818 		}
2819 	}*/
2820 
2821 	if(sbgDayNHours[d2_afternoon]>0){
2822 		int actIndexEnd=-1;
2823 		int h2;
2824 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
2825 			if(sbgTimetable(d2_afternoon,h2)>=0){
2826 				actIndexEnd=sbgTimetable(d2_afternoon,h2);
2827 				break;
2828 			}
2829 		}
2830 		if(actIndexEnd>=0)
2831 			if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai /* || actIndexEnd==actIndexBegin */)
2832 				actIndexEnd=-1;
2833 
2834 		if(actIndexEnd>=0){
2835 			assert(!acts.contains(actIndexEnd));
2836 			possibleDays.append(d2_afternoon);
2837 			atBeginning.append(false);
2838 			acts.append(actIndexEnd);
2839 		}
2840 	}
2841 	else if(sbgDayNHours[d2_morning]>0){
2842 		int actIndexEnd=-1;
2843 		int h2;
2844 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
2845 			if(sbgTimetable(d2_morning,h2)>=0){
2846 				actIndexEnd=sbgTimetable(d2_morning,h2);
2847 				break;
2848 			}
2849 		}
2850 		if(actIndexEnd>=0)
2851 			if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai /* || actIndexEnd==actIndexBegin */)
2852 				actIndexEnd=-1;
2853 
2854 		if(actIndexEnd>=0){
2855 			assert(!acts.contains(actIndexEnd));
2856 			possibleDays.append(d2_morning);
2857 			atBeginning.append(false);
2858 			acts.append(actIndexEnd);
2859 		}
2860 	}
2861 
2862 	bool possibleBeginOrEnd=true;
2863 	if(possibleDays.count()==0)
2864 		possibleBeginOrEnd=false;
2865 
2866 	if(possibleBeginOrEnd){
2867 		int t;
2868 
2869 		if(level==0){
2870 			int optMinWrong=INF;
2871 
2872 			QList<int> tl;
2873 
2874 			for(int q=0; q<acts.count(); q++){
2875 				int ai2=acts.at(q);
2876 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
2877 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
2878 				}
2879 			}
2880 
2881 			for(int q=0; q<acts.count(); q++){
2882 				int ai2=acts.at(q);
2883 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
2884 					tl.append(q);
2885 			}
2886 
2887 			assert(tl.count()>=1);
2888 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
2889 
2890 			assert(mpos>=0 && mpos<acts.count());
2891 			t=mpos;
2892 		}
2893 		else{
2894 			t=rng.intMRG32k3a(possibleDays.count());
2895 		}
2896 
2897 		assert(t>=0 && t<possibleDays.count());
2898 
2899 		int d2=possibleDays.at(t);
2900 		bool begin=atBeginning.at(t);
2901 		int ai2=acts.at(t);
2902 
2903 		removedActivity=ai2;
2904 
2905 		if(begin){
2906 			int h2;
2907 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
2908 				if(sbgTimetable(d2,h2)>=0)
2909 					break;
2910 			assert(h2<gt.rules.nHoursPerDay);
2911 
2912 			assert(sbgTimetable(d2,h2)==ai2);
2913 
2914 			assert(!conflActivities.contains(ai2));
2915 			conflActivities.append(ai2);
2916 			nConflActivities++;
2917 			assert(nConflActivities==conflActivities.count());
2918 		}
2919 		else{
2920 			int h2;
2921 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
2922 				if(sbgTimetable(d2,h2)>=0)
2923 					break;
2924 			assert(h2>=0);
2925 
2926 			assert(sbgTimetable(d2,h2)==ai2);
2927 
2928 			assert(!conflActivities.contains(ai2));
2929 			conflActivities.append(ai2);
2930 			nConflActivities++;
2931 			assert(nConflActivities==conflActivities.count());
2932 		}
2933 
2934 		return true;
2935 	}
2936 	else
2937 		return false;
2938 }
2939 
subgroupRemoveAnActivityFromBegin(int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)2940 inline bool Generate::subgroupRemoveAnActivityFromBegin(int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
2941 {
2942 	//Subgroup: remove an activity from the beginning of any day
2943 	QList<int> possibleDays;
2944 	QList<int> acts;
2945 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
2946 		if(sbgDayNHours[d2]>0){
2947 			int actIndexBegin=-1;
2948 			int h2;
2949 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
2950 				if(sbgTimetable(d2,h2)>=0){
2951 					actIndexBegin=sbgTimetable(d2,h2);
2952 					break;
2953 				}
2954 			}
2955 			if(actIndexBegin>=0)
2956 				if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
2957 					actIndexBegin=-1;
2958 
2959 			if(actIndexBegin>=0){
2960 				assert(!acts.contains(actIndexBegin));
2961 				possibleDays.append(d2);
2962 				acts.append(actIndexBegin);
2963 			}
2964 		}
2965 	}
2966 
2967 	bool possibleBegin=true;
2968 	if(possibleDays.count()==0)
2969 		possibleBegin=false;
2970 
2971 	if(possibleBegin){
2972 		int t;
2973 
2974 		if(level==0){
2975 			int optMinWrong=INF;
2976 
2977 			QList<int> tl;
2978 
2979 			for(int q=0; q<acts.count(); q++){
2980 				int ai2=acts.at(q);
2981 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
2982 				 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
2983 				}
2984 			}
2985 
2986 			for(int q=0; q<acts.count(); q++){
2987 				int ai2=acts.at(q);
2988 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
2989 					tl.append(q);
2990 			}
2991 
2992 			assert(tl.count()>=1);
2993 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
2994 
2995 			assert(mpos>=0 && mpos<acts.count());
2996 			t=mpos;
2997 		}
2998 		else{
2999 			t=rng.intMRG32k3a(possibleDays.count());
3000 		}
3001 
3002 		assert(t>=0 && t<possibleDays.count());
3003 
3004 		int d2=possibleDays.at(t);
3005 		int ai2=acts.at(t);
3006 
3007 		removedActivity=ai2;
3008 
3009 		int h2;
3010 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
3011 			if(sbgTimetable(d2,h2)>=0)
3012 				break;
3013 		assert(h2<gt.rules.nHoursPerDay);
3014 
3015 		assert(sbgTimetable(d2,h2)==ai2);
3016 
3017 		assert(!conflActivities.contains(ai2));
3018 		conflActivities.append(ai2);
3019 		nConflActivities++;
3020 		assert(nConflActivities==conflActivities.count());
3021 
3022 		return true;
3023 	}
3024 	else
3025 		return false;
3026 }
3027 
subgroupRemoveAnActivityFromBeginMorning(int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3028 inline bool Generate::subgroupRemoveAnActivityFromBeginMorning(int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3029 {
3030 	QList<int> possibleDays;
3031 	QList<int> acts;
3032 	for(int real_d=0; real_d<gt.rules.nDaysPerWeek/2; real_d++){
3033 		int d2=real_d*2;
3034 		if(sbgDayNHours[d2]>0){
3035 			int actIndexBegin=-1;
3036 			int h2;
3037 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
3038 				if(sbgTimetable(d2,h2)>=0){
3039 					actIndexBegin=sbgTimetable(d2,h2);
3040 					break;
3041 				}
3042 			}
3043 			if(actIndexBegin>=0)
3044 				if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
3045 					actIndexBegin=-1;
3046 
3047 			if(actIndexBegin>=0){
3048 				assert(!acts.contains(actIndexBegin));
3049 				possibleDays.append(d2);
3050 				acts.append(actIndexBegin);
3051 			}
3052 		}
3053 	}
3054 
3055 	bool possibleBegin=true;
3056 	if(possibleDays.count()==0)
3057 		possibleBegin=false;
3058 
3059 	if(possibleBegin){
3060 		int t;
3061 
3062 		if(level==0){
3063 			int optMinWrong=INF;
3064 
3065 			QList<int> tl;
3066 
3067 			for(int q=0; q<acts.count(); q++){
3068 				int ai2=acts.at(q);
3069 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
3070 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
3071 				}
3072 			}
3073 
3074 			for(int q=0; q<acts.count(); q++){
3075 				int ai2=acts.at(q);
3076 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
3077 					tl.append(q);
3078 			}
3079 
3080 			assert(tl.count()>=1);
3081 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
3082 
3083 			assert(mpos>=0 && mpos<acts.count());
3084 			t=mpos;
3085 		}
3086 		else{
3087 			t=rng.intMRG32k3a(possibleDays.count());
3088 		}
3089 
3090 		assert(t>=0 && t<possibleDays.count());
3091 
3092 		int d2=possibleDays.at(t);
3093 		int ai2=acts.at(t);
3094 
3095 		removedActivity=ai2;
3096 
3097 		int h2;
3098 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
3099 			if(sbgTimetable(d2,h2)>=0)
3100 				break;
3101 		assert(h2<gt.rules.nHoursPerDay);
3102 
3103 		assert(sbgTimetable(d2,h2)==ai2);
3104 
3105 		assert(!conflActivities.contains(ai2));
3106 		conflActivities.append(ai2);
3107 		nConflActivities++;
3108 		assert(nConflActivities==conflActivities.count());
3109 
3110 		return true;
3111 	}
3112 	else
3113 		return false;
3114 }
3115 
subgroupRemoveAnActivityFromBeginCertainDay(int d2,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3116 inline bool Generate::subgroupRemoveAnActivityFromBeginCertainDay(int d2, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3117 {
3118 	//Subgroup: remove an activity from the beginning of a certain day
3119 	if(sbgDayNHours[d2]>0){
3120 		int actIndexBegin=-1;
3121 		int h2;
3122 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
3123 			if(sbgTimetable(d2,h2)>=0){
3124 				actIndexBegin=sbgTimetable(d2,h2);
3125 				break;
3126 			}
3127 		}
3128 		if(actIndexBegin>=0)
3129 			if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
3130 				actIndexBegin=-1;
3131 
3132 		if(actIndexBegin>=0){
3133 			removedActivity=actIndexBegin;
3134 
3135 			assert(!conflActivities.contains(actIndexBegin));
3136 			conflActivities.append(actIndexBegin);
3137 			nConflActivities++;
3138 			assert(nConflActivities==conflActivities.count());
3139 
3140 			return true;
3141 		}
3142 		else
3143 			return false;
3144 	}
3145 	else
3146 		return false;
3147 }
3148 
subgroupRemoveAnActivityFromBeginCertainTwoDays(int d2,int d4,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3149 inline bool Generate::subgroupRemoveAnActivityFromBeginCertainTwoDays(int d2, int d4, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3150 {
3151 	int actIndexBegin2=-1;
3152 	if(sbgDayNHours[d2]>0){
3153 		int h2;
3154 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
3155 			if(sbgTimetable(d2,h2)>=0){
3156 				actIndexBegin2=sbgTimetable(d2,h2);
3157 				break;
3158 			}
3159 		}
3160 		if(actIndexBegin2>=0)
3161 			if(fixedTimeActivity[actIndexBegin2] || swappedActivities[actIndexBegin2] || actIndexBegin2==ai)
3162 				actIndexBegin2=-1;
3163 	}
3164 	int actIndexBegin4=-1;
3165 	if(sbgDayNHours[d4]>0){
3166 		int h2;
3167 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
3168 			if(sbgTimetable(d4,h2)>=0){
3169 				actIndexBegin4=sbgTimetable(d4,h2);
3170 				break;
3171 			}
3172 		}
3173 		if(actIndexBegin4>=0)
3174 			if(fixedTimeActivity[actIndexBegin4] || swappedActivities[actIndexBegin4] || actIndexBegin4==ai)
3175 				actIndexBegin4=-1;
3176 	}
3177 
3178 	int actIndexBegin=-1;
3179 
3180 	if(actIndexBegin2==-1)
3181 		actIndexBegin=actIndexBegin4;
3182 	else if(actIndexBegin4==-1)
3183 		actIndexBegin=actIndexBegin2;
3184 	else{
3185 		if(level==0){
3186 			int optMinWrong=INF;
3187 
3188 			if(optMinWrong>triedRemovals(actIndexBegin2,c.times[actIndexBegin2]))
3189 				optMinWrong=triedRemovals(actIndexBegin2,c.times[actIndexBegin2]);
3190 			if(optMinWrong>triedRemovals(actIndexBegin4,c.times[actIndexBegin4]))
3191 				optMinWrong=triedRemovals(actIndexBegin4,c.times[actIndexBegin4]);
3192 
3193 			QList<int> tl;
3194 
3195 			if(optMinWrong==triedRemovals(actIndexBegin2,c.times[actIndexBegin2]))
3196 				tl.append(0);
3197 			if(optMinWrong==triedRemovals(actIndexBegin4,c.times[actIndexBegin4]))
3198 				tl.append(1);
3199 
3200 			assert(tl.count()>=1);
3201 			int ii=tl.at(rng.intMRG32k3a(tl.count()));
3202 
3203 			if(ii==0)
3204 				actIndexBegin=actIndexBegin2;
3205 			else if(ii==1)
3206 				actIndexBegin=actIndexBegin4;
3207 		}
3208 		else{
3209 			if(rng.intMRG32k3a(2)==0)
3210 				actIndexBegin=actIndexBegin2;
3211 			else
3212 				actIndexBegin=actIndexBegin4;
3213 		}
3214 	}
3215 
3216 	if(actIndexBegin>=0){
3217 		removedActivity=actIndexBegin;
3218 
3219 		assert(!conflActivities.contains(actIndexBegin));
3220 		conflActivities.append(actIndexBegin);
3221 		nConflActivities++;
3222 		assert(nConflActivities==conflActivities.count());
3223 
3224 		return true;
3225 	}
3226 	else
3227 		return false;
3228 }
3229 
subgroupRemoveAnActivityFromEnd(int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3230 inline bool Generate::subgroupRemoveAnActivityFromEnd(int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3231 {
3232 	//Subgroup: remove an activity from the end of any day
3233 	QList<int> possibleDays;
3234 	QList<int> acts;
3235 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
3236 		if(sbgDayNHours[d2]>0){
3237 			int actIndexEnd=-1;
3238 			int h2;
3239 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
3240 				if(sbgTimetable(d2,h2)>=0){
3241 					actIndexEnd=sbgTimetable(d2,h2);
3242 					break;
3243 				}
3244 			}
3245 			if(actIndexEnd>=0)
3246 				if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai)
3247 					actIndexEnd=-1;
3248 
3249 			if(actIndexEnd>=0){
3250 				assert(!acts.contains(actIndexEnd));
3251 				possibleDays.append(d2);
3252 				acts.append(actIndexEnd);
3253 			}
3254 		}
3255 	}
3256 
3257 	bool possibleEnd=true;
3258 	if(possibleDays.count()==0)
3259 		possibleEnd=false;
3260 
3261 	if(possibleEnd){
3262 		int t;
3263 
3264 		if(level==0){
3265 			int optMinWrong=INF;
3266 
3267 			QList<int> tl;
3268 
3269 			for(int q=0; q<acts.count(); q++){
3270 				int ai2=acts.at(q);
3271 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
3272 				 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
3273 				}
3274 			}
3275 
3276 			for(int q=0; q<acts.count(); q++){
3277 				int ai2=acts.at(q);
3278 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
3279 					tl.append(q);
3280 			}
3281 
3282 			assert(tl.count()>=1);
3283 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
3284 
3285 			assert(mpos>=0 && mpos<acts.count());
3286 			t=mpos;
3287 		}
3288 		else{
3289 			t=rng.intMRG32k3a(possibleDays.count());
3290 		}
3291 
3292 		assert(t>=0 && t<possibleDays.count());
3293 
3294 		int d2=possibleDays.at(t);
3295 		int ai2=acts.at(t);
3296 
3297 		removedActivity=ai2;
3298 
3299 		int h2;
3300 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
3301 			if(sbgTimetable(d2,h2)>=0)
3302 				break;
3303 		assert(h2>=0);
3304 
3305 		assert(sbgTimetable(d2,h2)==ai2);
3306 
3307 		assert(!conflActivities.contains(ai2));
3308 		conflActivities.append(ai2);
3309 		nConflActivities++;
3310 		assert(nConflActivities==conflActivities.count());
3311 
3312 		return true;
3313 	}
3314 	else
3315 		return false;
3316 }
3317 
subgroupRemoveAnActivityFromEndAfternoon(int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3318 inline bool Generate::subgroupRemoveAnActivityFromEndAfternoon(int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3319 {
3320 	QList<int> possibleDays;
3321 	QList<int> acts;
3322 	for(int real_d=0; real_d<gt.rules.nDaysPerWeek/2; real_d++){
3323 		int d2=2*real_d+1;
3324 		if(sbgDayNHours[d2]>0){
3325 			int actIndexEnd=-1;
3326 			int h2;
3327 			for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
3328 				if(sbgTimetable(d2,h2)>=0){
3329 					actIndexEnd=sbgTimetable(d2,h2);
3330 					break;
3331 				}
3332 			}
3333 			if(actIndexEnd>=0)
3334 				if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai)
3335 					actIndexEnd=-1;
3336 
3337 			if(actIndexEnd>=0){
3338 				assert(!acts.contains(actIndexEnd));
3339 				possibleDays.append(d2);
3340 				acts.append(actIndexEnd);
3341 			}
3342 		}
3343 	}
3344 
3345 	bool possibleEnd=true;
3346 	if(possibleDays.count()==0)
3347 		possibleEnd=false;
3348 
3349 	if(possibleEnd){
3350 		int t;
3351 
3352 		if(level==0){
3353 			int optMinWrong=INF;
3354 
3355 			QList<int> tl;
3356 
3357 			for(int q=0; q<acts.count(); q++){
3358 				int ai2=acts.at(q);
3359 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
3360 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
3361 				}
3362 			}
3363 
3364 			for(int q=0; q<acts.count(); q++){
3365 				int ai2=acts.at(q);
3366 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
3367 					tl.append(q);
3368 			}
3369 
3370 			assert(tl.count()>=1);
3371 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
3372 
3373 			assert(mpos>=0 && mpos<acts.count());
3374 			t=mpos;
3375 		}
3376 		else{
3377 			t=rng.intMRG32k3a(possibleDays.count());
3378 		}
3379 
3380 		assert(t>=0 && t<possibleDays.count());
3381 
3382 		int d2=possibleDays.at(t);
3383 		int ai2=acts.at(t);
3384 
3385 		removedActivity=ai2;
3386 
3387 		int h2;
3388 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
3389 			if(sbgTimetable(d2,h2)>=0)
3390 				break;
3391 		assert(h2>=0);
3392 
3393 		assert(sbgTimetable(d2,h2)==ai2);
3394 
3395 		assert(!conflActivities.contains(ai2));
3396 		conflActivities.append(ai2);
3397 		nConflActivities++;
3398 		assert(nConflActivities==conflActivities.count());
3399 
3400 		return true;
3401 	}
3402 	else
3403 		return false;
3404 }
3405 
subgroupRemoveAnActivityFromEndCertainDay(int d2,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3406 inline bool Generate::subgroupRemoveAnActivityFromEndCertainDay(int d2, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3407 {
3408 	//Subgroup: remove an activity from the end of a certain day
3409 	if(sbgDayNHours[d2]>0){
3410 		int actIndexEnd=-1;
3411 		int h2;
3412 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
3413 			if(sbgTimetable(d2,h2)>=0){
3414 				actIndexEnd=sbgTimetable(d2,h2);
3415 				break;
3416 			}
3417 		}
3418 		if(actIndexEnd>=0)
3419 			if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai)
3420 				actIndexEnd=-1;
3421 
3422 		if(actIndexEnd>=0){
3423 			removedActivity=actIndexEnd;
3424 
3425 			assert(!conflActivities.contains(actIndexEnd));
3426 			conflActivities.append(actIndexEnd);
3427 			nConflActivities++;
3428 			assert(nConflActivities==conflActivities.count());
3429 
3430 			return true;
3431 		}
3432 		else
3433 			return false;
3434 	}
3435 	else
3436 		return false;
3437 }
3438 
subgroupRemoveAnActivityFromEndCertainTwoDays(int d2,int d4,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3439 inline bool Generate::subgroupRemoveAnActivityFromEndCertainTwoDays(int d2, int d4, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3440 {
3441 	int actIndexEnd2=-1;
3442 	if(sbgDayNHours[d2]>0){
3443 		int h2;
3444 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
3445 			if(sbgTimetable(d2,h2)>=0){
3446 				actIndexEnd2=sbgTimetable(d2,h2);
3447 				break;
3448 			}
3449 		}
3450 		if(actIndexEnd2>=0)
3451 			if(fixedTimeActivity[actIndexEnd2] || swappedActivities[actIndexEnd2] || actIndexEnd2==ai)
3452 				actIndexEnd2=-1;
3453 	}
3454 	int actIndexEnd4=-1;
3455 	if(sbgDayNHours[d4]>0){
3456 		int h2;
3457 		for(h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
3458 			if(sbgTimetable(d4,h2)>=0){
3459 				actIndexEnd4=sbgTimetable(d4,h2);
3460 				break;
3461 			}
3462 		}
3463 		if(actIndexEnd4>=0)
3464 			if(fixedTimeActivity[actIndexEnd4] || swappedActivities[actIndexEnd4] || actIndexEnd4==ai)
3465 				actIndexEnd4=-1;
3466 	}
3467 
3468 	int actIndexEnd=-1;
3469 
3470 	if(actIndexEnd2==-1)
3471 		actIndexEnd=actIndexEnd4;
3472 	else if(actIndexEnd4==-1)
3473 		actIndexEnd=actIndexEnd2;
3474 	else{
3475 		if(level==0){
3476 			int optMinWrong=INF;
3477 
3478 			if(optMinWrong>triedRemovals(actIndexEnd2,c.times[actIndexEnd2]))
3479 				optMinWrong=triedRemovals(actIndexEnd2,c.times[actIndexEnd2]);
3480 			if(optMinWrong>triedRemovals(actIndexEnd4,c.times[actIndexEnd4]))
3481 				optMinWrong=triedRemovals(actIndexEnd4,c.times[actIndexEnd4]);
3482 
3483 			QList<int> tl;
3484 
3485 			if(optMinWrong==triedRemovals(actIndexEnd2,c.times[actIndexEnd2]))
3486 				tl.append(0);
3487 			if(optMinWrong==triedRemovals(actIndexEnd4,c.times[actIndexEnd4]))
3488 				tl.append(1);
3489 
3490 			assert(tl.count()>=1);
3491 			int ii=tl.at(rng.intMRG32k3a(tl.count()));
3492 
3493 			if(ii==0)
3494 				actIndexEnd=actIndexEnd2;
3495 			else if(ii==1)
3496 				actIndexEnd=actIndexEnd4;
3497 		}
3498 		else{
3499 			if(rng.intMRG32k3a(2)==0)
3500 				actIndexEnd=actIndexEnd2;
3501 			else
3502 				actIndexEnd=actIndexEnd4;
3503 		}
3504 	}
3505 
3506 	if(actIndexEnd>=0){
3507 		removedActivity=actIndexEnd;
3508 
3509 		assert(!conflActivities.contains(actIndexEnd));
3510 		conflActivities.append(actIndexEnd);
3511 		nConflActivities++;
3512 		assert(nConflActivities==conflActivities.count());
3513 
3514 		return true;
3515 	}
3516 	else
3517 		return false;
3518 }
3519 
subgroupRemoveAnActivityFromAnywhere(int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3520 inline bool Generate::subgroupRemoveAnActivityFromAnywhere(int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3521 {
3522 	//Subgroup: remove an activity from anywhere
3523 	QList<int> acts;
3524 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
3525 		if(sbgDayNHours[d2]>0){
3526 			int actIndex=-1;
3527 			int h2;
3528 			for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
3529 				if(sbgTimetable(d2,h2)>=0){
3530 					actIndex=sbgTimetable(d2,h2);
3531 
3532 					if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex))
3533 						actIndex=-1;
3534 
3535 					if(actIndex>=0){
3536 						assert(!acts.contains(actIndex));
3537 						acts.append(actIndex);
3538 					}
3539 				}
3540 		}
3541 	}
3542 
3543 	if(acts.count()>0){
3544 		int t;
3545 
3546 		if(level==0){
3547 			int optMinWrong=INF;
3548 
3549 			QList<int> tl;
3550 
3551 			for(int q=0; q<acts.count(); q++){
3552 				int ai2=acts.at(q);
3553 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
3554 				 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
3555 				}
3556 			}
3557 
3558 			for(int q=0; q<acts.count(); q++){
3559 				int ai2=acts.at(q);
3560 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
3561 					tl.append(q);
3562 			}
3563 
3564 			assert(tl.count()>=1);
3565 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
3566 
3567 			assert(mpos>=0 && mpos<acts.count());
3568 			t=mpos;
3569 		}
3570 		else{
3571 			t=rng.intMRG32k3a(acts.count());
3572 		}
3573 
3574 		int ai2=acts.at(t);
3575 
3576 		removedActivity=ai2;
3577 
3578 		assert(!conflActivities.contains(ai2));
3579 		conflActivities.append(ai2);
3580 		nConflActivities++;
3581 		assert(nConflActivities==conflActivities.count());
3582 
3583 		return true;
3584 	}
3585 	else
3586 		return false;
3587 }
3588 
subgroupRemoveAnActivityFromAnywhereCertainDay(int d2,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3589 inline bool Generate::subgroupRemoveAnActivityFromAnywhereCertainDay(int d2, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3590 {
3591 	//Subgroup: remove an activity from anywhere certain day
3592 	QList<int> acts;
3593 	if(sbgDayNHours[d2]>0){
3594 		int actIndex=-1;
3595 		int h2;
3596 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
3597 			if(sbgTimetable(d2,h2)>=0){
3598 				actIndex=sbgTimetable(d2,h2);
3599 
3600 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex))
3601 					actIndex=-1;
3602 
3603 				if(actIndex>=0){
3604 					assert(!acts.contains(actIndex));
3605 					acts.append(actIndex);
3606 				}
3607 			}
3608 	}
3609 
3610 	if(acts.count()>0){
3611 		int t;
3612 
3613 		if(level==0){
3614 			int optMinWrong=INF;
3615 
3616 			QList<int> tl;
3617 
3618 			for(int q=0; q<acts.count(); q++){
3619 				int ai2=acts.at(q);
3620 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
3621 				 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
3622 				}
3623 			}
3624 
3625 			for(int q=0; q<acts.count(); q++){
3626 				int ai2=acts.at(q);
3627 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
3628 					tl.append(q);
3629 			}
3630 
3631 			assert(tl.count()>=1);
3632 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
3633 
3634 			assert(mpos>=0 && mpos<acts.count());
3635 			t=mpos;
3636 		}
3637 		else{
3638 			t=rng.intMRG32k3a(acts.count());
3639 		}
3640 
3641 		int ai2=acts.at(t);
3642 
3643 		removedActivity=ai2;
3644 
3645 		assert(!conflActivities.contains(ai2));
3646 		conflActivities.append(ai2);
3647 		nConflActivities++;
3648 		assert(nConflActivities==conflActivities.count());
3649 
3650 		return true;
3651 	}
3652 	else
3653 		return false;
3654 }
3655 
subgroupRemoveAnActivityFromBeginOrEndCertainDay(int d2,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3656 inline bool Generate::subgroupRemoveAnActivityFromBeginOrEndCertainDay(int d2, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3657 {
3658 	//Subgroup: remove an activity from the beginning or from the end of a certain day
3659 	int actIndexBegin=-1, actIndexEnd=-1;
3660 
3661 	if(sbgDayNHours[d2]>0){
3662 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
3663 			if(sbgTimetable(d2,h2)>=0){
3664 				actIndexBegin=sbgTimetable(d2,h2);
3665 				break;
3666 			}
3667 		}
3668 		if(actIndexBegin>=0)
3669 			if(fixedTimeActivity[actIndexBegin] || swappedActivities[actIndexBegin] || actIndexBegin==ai)
3670 				actIndexBegin=-1;
3671 		for(int h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
3672 			if(sbgTimetable(d2,h2)>=0){
3673 				actIndexEnd=sbgTimetable(d2,h2);
3674 				break;
3675 			}
3676 		}
3677 		if(actIndexEnd>=0)
3678 			if(fixedTimeActivity[actIndexEnd] || swappedActivities[actIndexEnd] || actIndexEnd==ai || actIndexEnd==actIndexBegin)
3679 				actIndexEnd=-1;
3680 	}
3681 
3682 	if(actIndexEnd>=0 || actIndexBegin>=0){
3683 		int ai2=-1;
3684 		if(level==0){
3685 			int optMinWrong=INF;
3686 
3687 			if(actIndexBegin>=0){
3688 				if(optMinWrong>triedRemovals(actIndexBegin,c.times[actIndexBegin])){
3689 				 	optMinWrong=triedRemovals(actIndexBegin,c.times[actIndexBegin]);
3690 				}
3691 				ai2=actIndexBegin;
3692 			}
3693 
3694 			if(actIndexEnd>=0){
3695 				if(optMinWrong>triedRemovals(actIndexEnd,c.times[actIndexEnd])){
3696 				 	optMinWrong=triedRemovals(actIndexEnd,c.times[actIndexEnd]);
3697 				}
3698 				ai2=actIndexEnd;
3699 			}
3700 
3701 			if(actIndexBegin>=0 && actIndexEnd>=0 && optMinWrong==triedRemovals(actIndexEnd,c.times[actIndexEnd]) &&
3702 			  optMinWrong==triedRemovals(actIndexBegin,c.times[actIndexBegin])){
3703 				if(rng.intMRG32k3a(2)==0)
3704 					ai2=actIndexBegin;
3705 				else
3706 					ai2=actIndexEnd;
3707 			}
3708 		}
3709 		else{
3710 			if(actIndexBegin>=0 && actIndexEnd<0)
3711 				ai2=actIndexBegin;
3712 			else if(actIndexEnd>=0 && actIndexBegin<0)
3713 				ai2=actIndexEnd;
3714 			else{
3715 				if(rng.intMRG32k3a(2)==0)
3716 					ai2=actIndexBegin;
3717 				else
3718 					ai2=actIndexEnd;
3719 			}
3720 		}
3721 		assert(ai2>=0);
3722 
3723 		removedActivity=ai2;
3724 
3725 		assert(!conflActivities.contains(ai2));
3726 		conflActivities.append(ai2);
3727 		nConflActivities++;
3728 		assert(nConflActivities==conflActivities.count());
3729 
3730 		return true;
3731 	}
3732 	else
3733 		return false;
3734 }
3735 
subgroupRemoveAnActivityFromAnywhereCertainDayCertainActivityTag(int d2,int actTag,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3736 inline bool Generate::subgroupRemoveAnActivityFromAnywhereCertainDayCertainActivityTag(int d2, int actTag, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3737 {
3738 	//Subgroup: remove an activity from anywhere certain day certain activity tag
3739 	QList<int> acts;
3740 	if(sbgDayNHours[d2]>0){
3741 		int actIndex=-1;
3742 		int h2;
3743 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
3744 			if(sbgTimetable(d2,h2)>=0){
3745 				actIndex=sbgTimetable(d2,h2);
3746 
3747 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex) || !gt.rules.internalActivitiesList[actIndex].iActivityTagsSet.contains(actTag))
3748 					actIndex=-1;
3749 
3750 				if(actIndex>=0){
3751 					assert(!acts.contains(actIndex));
3752 					acts.append(actIndex);
3753 				}
3754 			}
3755 	}
3756 
3757 	if(acts.count()>0){
3758 		int t;
3759 
3760 		if(level==0){
3761 			int optMinWrong=INF;
3762 
3763 			QList<int> tl;
3764 
3765 			for(int q=0; q<acts.count(); q++){
3766 				int ai2=acts.at(q);
3767 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
3768 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
3769 				}
3770 			}
3771 
3772 			for(int q=0; q<acts.count(); q++){
3773 				int ai2=acts.at(q);
3774 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
3775 					tl.append(q);
3776 			}
3777 
3778 			assert(tl.count()>=1);
3779 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
3780 
3781 			assert(mpos>=0 && mpos<acts.count());
3782 			t=mpos;
3783 		}
3784 		else{
3785 			t=rng.intMRG32k3a(acts.count());
3786 		}
3787 
3788 		int ai2=acts.at(t);
3789 
3790 		removedActivity=ai2;
3791 
3792 		assert(!conflActivities.contains(ai2));
3793 		conflActivities.append(ai2);
3794 		nConflActivities++;
3795 		assert(nConflActivities==conflActivities.count());
3796 
3797 		return true;
3798 	}
3799 	else
3800 		return false;
3801 }
3802 
subgroupRemoveAnActivityFromAnywhereCertainDayDayPairCertainActivityTag(int d2,int dpair2,int actTag,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3803 inline bool Generate::subgroupRemoveAnActivityFromAnywhereCertainDayDayPairCertainActivityTag(int d2, int dpair2, int actTag, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3804 {
3805 	QList<int> acts;
3806 	if(sbgDayNHours[d2]>0){
3807 		int actIndex=-1;
3808 		int h2;
3809 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
3810 			if(sbgTimetable(d2,h2)>=0){
3811 				actIndex=sbgTimetable(d2,h2);
3812 
3813 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex) || !gt.rules.internalActivitiesList[actIndex].iActivityTagsSet.contains(actTag))
3814 					actIndex=-1;
3815 
3816 				if(actIndex>=0){
3817 					assert(!acts.contains(actIndex));
3818 					acts.append(actIndex);
3819 				}
3820 			}
3821 	}
3822 	if(sbgDayNHours[dpair2]>0){
3823 		int actIndex=-1;
3824 		int h2;
3825 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
3826 			if(sbgTimetable(dpair2,h2)>=0){
3827 				actIndex=sbgTimetable(dpair2,h2);
3828 
3829 				assert(actIndex!=ai);
3830 
3831 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || acts.contains(actIndex) || !gt.rules.internalActivitiesList[actIndex].iActivityTagsSet.contains(actTag))
3832 					actIndex=-1;
3833 
3834 				if(actIndex>=0){
3835 					assert(!acts.contains(actIndex));
3836 					acts.append(actIndex);
3837 				}
3838 			}
3839 	}
3840 
3841 	if(acts.count()>0){
3842 		int t;
3843 
3844 		if(level==0){
3845 			int optMinWrong=INF;
3846 
3847 			QList<int> tl;
3848 
3849 			for(int q=0; q<acts.count(); q++){
3850 				int ai2=acts.at(q);
3851 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
3852 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
3853 				}
3854 			}
3855 
3856 			for(int q=0; q<acts.count(); q++){
3857 				int ai2=acts.at(q);
3858 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
3859 					tl.append(q);
3860 			}
3861 
3862 			assert(tl.count()>=1);
3863 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
3864 
3865 			assert(mpos>=0 && mpos<acts.count());
3866 			t=mpos;
3867 		}
3868 		else{
3869 			t=rng.intMRG32k3a(acts.count());
3870 		}
3871 
3872 		int ai2=acts.at(t);
3873 
3874 		removedActivity=ai2;
3875 
3876 		assert(!conflActivities.contains(ai2));
3877 		conflActivities.append(ai2);
3878 		nConflActivities++;
3879 		assert(nConflActivities==conflActivities.count());
3880 
3881 		return true;
3882 	}
3883 	else
3884 		return false;
3885 }
3886 
subgroupRemoveAnActivityFromAnywhereCertainTwoDays(int d2,int d4,int level,int ai,QList<int> & conflActivities,int & nConflActivities,int & removedActivity)3887 inline bool Generate::subgroupRemoveAnActivityFromAnywhereCertainTwoDays(int d2, int d4, int level, int ai, QList<int>& conflActivities, int& nConflActivities, int& removedActivity) //returns true if successful, false if impossible
3888 {
3889 	QList<int> acts;
3890 	if(sbgDayNHours[d2]>0){
3891 		int actIndex=-1;
3892 		int h2;
3893 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
3894 			if(sbgTimetable(d2,h2)>=0){
3895 				actIndex=sbgTimetable(d2,h2);
3896 
3897 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex))
3898 					actIndex=-1;
3899 
3900 				if(actIndex>=0){
3901 					assert(!acts.contains(actIndex));
3902 					acts.append(actIndex);
3903 				}
3904 			}
3905 	}
3906 	if(sbgDayNHours[d4]>0){
3907 		int actIndex=-1;
3908 		int h2;
3909 		for(h2=0; h2<gt.rules.nHoursPerDay; h2++)
3910 			if(sbgTimetable(d4,h2)>=0){
3911 				actIndex=sbgTimetable(d4,h2);
3912 
3913 				if(fixedTimeActivity[actIndex] || swappedActivities[actIndex] || actIndex==ai || acts.contains(actIndex))
3914 					actIndex=-1;
3915 
3916 				if(actIndex>=0){
3917 					assert(!acts.contains(actIndex));
3918 					acts.append(actIndex);
3919 				}
3920 			}
3921 	}
3922 
3923 	if(acts.count()>0){
3924 		int t;
3925 
3926 		if(level==0){
3927 			int optMinWrong=INF;
3928 
3929 			QList<int> tl;
3930 
3931 			for(int q=0; q<acts.count(); q++){
3932 				int ai2=acts.at(q);
3933 				if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
3934 					optMinWrong=triedRemovals(ai2,c.times[ai2]);
3935 				}
3936 			}
3937 
3938 			for(int q=0; q<acts.count(); q++){
3939 				int ai2=acts.at(q);
3940 				if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
3941 					tl.append(q);
3942 			}
3943 
3944 			assert(tl.count()>=1);
3945 			int mpos=tl.at(rng.intMRG32k3a(tl.count()));
3946 
3947 			assert(mpos>=0 && mpos<acts.count());
3948 			t=mpos;
3949 		}
3950 		else{
3951 			t=rng.intMRG32k3a(acts.count());
3952 		}
3953 
3954 		int ai2=acts.at(t);
3955 
3956 		removedActivity=ai2;
3957 
3958 		assert(!conflActivities.contains(ai2));
3959 		conflActivities.append(ai2);
3960 		nConflActivities++;
3961 		assert(nConflActivities==conflActivities.count());
3962 
3963 		return true;
3964 	}
3965 	else
3966 		return false;
3967 }
3968 
skipRandom(double weightPercentage)3969 bool Generate::skipRandom(double weightPercentage)
3970 {
3971 	if(weightPercentage<0.0)
3972 		return true; //non-existing constraint
3973 
3974 	if(weightPercentage>=100.0)
3975 		return false;
3976 
3977 	double t=weightPercentage/100.0;
3978 	assert(t>=0.0 && t<1.0);
3979 
3980 	qint64 tt=qint64(t*double(MULTIPLICANT_DOUBLE_PRECISION));
3981 	tt*=rng.m1;
3982 	tt+=HALF_MULTIPLICANT_DOUBLE_PRECISION; //round the result of the division below
3983 	tt/=MULTIPLICANT_DOUBLE_PRECISION;
3984 	assert(tt>=0 && tt<rng.m1);
3985 	unsigned int ttu=(unsigned int)(tt);
3986 
3987 	unsigned int r=rng.uiMRG32k3a();
3988 
3989 	if(ttu<=r)
3990 		return true;
3991 	else
3992 		return false;
3993 
3994 	//old code below
3995 	//t*=double(MM);
3996 	//int tt=int(t+0.5);
3997 	//assert(tt>=0 /* && tt<=MM */); //the second condition is always true, because MM >= any int value
3998 
3999 	//int r=randomKnuth1MM1();
4000 	//assert(r>0 && r<MM); //r cannot be 0
4001 	//if(tt<=r)
4002 	//	return true;
4003 	//else
4004 	//	return false;
4005 }
4006 
4007 
Generate()4008 Generate::Generate()
4009 {
4010 	this->nThread=0; //only used in multiple generation on multiple threads.
4011 }
4012 
~Generate()4013 Generate::~Generate()
4014 {
4015 }
4016 
precompute(QWidget * parent,QTextStream * initialOrderStream)4017 bool Generate::precompute(QWidget* parent, QTextStream* initialOrderStream)
4018 {
4019 	return processTimeSpaceConstraints(parent, initialOrderStream);
4020 }
4021 
checkBuildingChanges(int sbg,int tch,const QList<int> & globalConflActivities,int rm,int level,const Activity * act,int ai,int d,int h,QList<int> & tmp_list)4022 inline bool Generate::checkBuildingChanges(int sbg, int tch, const QList<int>& globalConflActivities, int rm, int level, const Activity* act, int ai, int d, int h, QList<int>& tmp_list)
4023 {
4024 	assert((sbg==-1 && tch>=0) || (sbg>=0 && tch==-1));
4025 	if(sbg>=0)
4026 		assert(sbg<gt.rules.nInternalSubgroups);
4027 	if(tch>=0)
4028 		assert(tch<gt.rules.nInternalTeachers);
4029 
4030 	if(sbg>=0)
4031 		assert(minGapsBetweenBuildingChangesForStudentsPercentages[sbg]>=0 || maxBuildingChangesPerDayForStudentsPercentages[sbg]>=0
4032 		   || maxBuildingChangesPerWeekForStudentsPercentages[sbg]>=0);
4033 	if(tch>=0)
4034 		assert(minGapsBetweenBuildingChangesForTeachersPercentages[tch]>=0 || maxBuildingChangesPerDayForTeachersPercentages[tch]>=0
4035 		   || maxBuildingChangesPerWeekForTeachersPercentages[tch]>=0);
4036 
4037 	//int buildings[MAX_HOURS_PER_DAY], activities[MAX_HOURS_PER_DAY];
4038 	for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
4039 		int ai2;
4040 		if(sbg>=0)
4041 			ai2=newSubgroupsTimetable(sbg,d,h2);
4042 		else
4043 			ai2=newTeachersTimetable(tch,d,h2);
4044 
4045 		if(ai2>=0 && !globalConflActivities.contains(ai2) && !tmp_list.contains(ai2)){
4046 			int rm2;
4047 			if(h2>=h && h2<h+act->duration){
4048 				assert(ai2==ai);
4049 				rm2=rm;
4050 			}
4051 			else
4052 				rm2=c.rooms[ai2];
4053 			if(rm2!=UNALLOCATED_SPACE && rm2!=UNSPECIFIED_ROOM){
4054 				assert(rm2>=0);
4055 				activities[h2]=ai2;
4056 				buildings[h2]=gt.rules.internalRoomsList[rm2]->buildingIndex;
4057 			}
4058 			else{
4059 				activities[h2]=ai2;
4060 				buildings[h2]=-1;
4061 			}
4062 		}
4063 		else{
4064 			buildings[h2]=-1;
4065 			activities[h2]=-1;
4066 		}
4067 	}
4068 
4069 	assert(buildings[h]!=-1); //because we checked this before calling the function checkBuildingChanges(...)
4070 	//if(buildings[h]==-1) //no problem
4071 	//	return true;
4072 
4073 	//min gaps
4074 	double perc;
4075 	int mg;
4076 	if(sbg>=0){
4077 		perc=minGapsBetweenBuildingChangesForStudentsPercentages[sbg];
4078 		mg=minGapsBetweenBuildingChangesForStudentsMinGaps[sbg];
4079 	}
4080 	else{
4081 		perc=minGapsBetweenBuildingChangesForTeachersPercentages[tch];
4082 		mg=minGapsBetweenBuildingChangesForTeachersMinGaps[tch];
4083 	}
4084 	if(perc>=0){
4085 		for(int h2=max(0, h-mg); h2<=min(h+act->duration-1+mg, gt.rules.nHoursPerDay-1); h2++)
4086 			if(!(h2>=h && h2<h+act->duration))
4087 				if(buildings[h2]!=buildings[h] && buildings[h2]!=-1){
4088 					int ai2=activities[h2];
4089 					assert(ai2>=0);
4090 					if(!swappedActivities[ai2] && !(fixedTimeActivity[ai2] && fixedSpaceActivity[ai2])){
4091 						if(!tmp_list.contains(ai2)){
4092 							tmp_list.append(ai2);
4093 
4094 							int ha=c.times[ai2]/gt.rules.nDaysPerWeek;
4095 							int dura=gt.rules.internalActivitiesList[ai2].duration;
4096 							for(int h3=ha; h3<ha+dura; h3++){
4097 								assert(activities[h3]==ai2);
4098 								assert(buildings[h3]!=-1);
4099 								buildings[h3]=-1;
4100 								activities[h3]=-1;
4101 							}
4102 						}
4103 					}
4104 					else{
4105 						return false;
4106 					}
4107 				}
4108 	}
4109 
4110 	//max changes per day
4111 	int mc;
4112 	if(sbg>=0){
4113 		perc=maxBuildingChangesPerDayForStudentsPercentages[sbg];
4114 		mc=maxBuildingChangesPerDayForStudentsMaxChanges[sbg];
4115 	}
4116 	else{
4117 		perc=maxBuildingChangesPerDayForTeachersPercentages[tch];
4118 		mc=maxBuildingChangesPerDayForTeachersMaxChanges[tch];
4119 	}
4120 
4121 	if(perc>=0){
4122 		int crt_building=-1;
4123 		int n_changes=0;
4124 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++)
4125 			if(buildings[h2]!=-1){
4126 				if(crt_building!=buildings[h2]){
4127 					if(crt_building!=-1)
4128 						n_changes++;
4129 					crt_building=buildings[h2];
4130 				}
4131 			}
4132 
4133 		if(n_changes>mc){ //not OK
4134 			if(level>=LEVEL_STOP_CONFLICTS_CALCULATION)
4135 				return false;
4136 
4137 			QList<int> removableActsList;
4138 			for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
4139 				if(!(h2>=h && h2<h+act->duration))
4140 					if(buildings[h2]!=-1 && activities[h2]>=0 && !swappedActivities[activities[h2]] && !(fixedTimeActivity[activities[h2]] && fixedSpaceActivity[activities[h2]]))
4141 						if(!removableActsList.contains(activities[h2])){
4142 							removableActsList.append(activities[h2]);
4143 							assert(!globalConflActivities.contains(activities[h2]));
4144 							assert(!tmp_list.contains(activities[h2]));
4145 						}
4146 			}
4147 
4148 			for(;;){
4149 				int ai2=-1;
4150 				QList<int> optimalRemovableActs;
4151 				if(level==0){
4152 					int nWrong=INF;
4153 					for(int a : qAsConst(removableActsList))
4154 						if(nWrong>triedRemovals(a,c.times[a])){
4155 							nWrong=triedRemovals(a,c.times[a]);
4156 						}
4157 					for(int a : qAsConst(removableActsList))
4158 						if(nWrong==triedRemovals(a,c.times[a]))
4159 							optimalRemovableActs.append(a);
4160 				}
4161 				else
4162 					optimalRemovableActs=removableActsList;
4163 
4164 				if(removableActsList.count()>0)
4165 					assert(optimalRemovableActs.count()>0);
4166 
4167 				if(optimalRemovableActs.count()==0)
4168 					return false;
4169 
4170 				ai2=optimalRemovableActs.at(rng.intMRG32k3a(optimalRemovableActs.count()));
4171 
4172 				assert(!swappedActivities[ai2]);
4173 				assert(!(fixedTimeActivity[ai2] && fixedSpaceActivity[ai2]));
4174 				assert(!globalConflActivities.contains(ai2));
4175 				assert(!tmp_list.contains(ai2));
4176 				assert(ai2>=0);
4177 
4178 				tmp_list.append(ai2);
4179 
4180 				int t=removableActsList.removeAll(ai2);
4181 				assert(t==1);
4182 
4183 				int ha=c.times[ai2]/gt.rules.nDaysPerWeek;
4184 				int da=gt.rules.internalActivitiesList[ai2].duration;
4185 				for(int h2=ha; h2<ha+da; h2++){
4186 					assert(activities[h2]==ai2);
4187 					assert(buildings[h2]!=-1);
4188 					buildings[h2]=-1;
4189 					activities[h2]=-1;
4190 				}
4191 
4192 				int crt_building=-1;
4193 				int n_changes=0;
4194 				for(int h2=0; h2<gt.rules.nHoursPerDay; h2++)
4195 					if(buildings[h2]!=-1){
4196 						if(crt_building!=buildings[h2]){
4197 							if(crt_building!=-1)
4198 								n_changes++;
4199 							crt_building=buildings[h2];
4200 						}
4201 					}
4202 
4203 				if(n_changes<=mc){ //OK
4204 					break;
4205 				}
4206 			}
4207 		}
4208 	}
4209 
4210 	//max changes per week
4211 	if(sbg>=0){
4212 		perc=maxBuildingChangesPerWeekForStudentsPercentages[sbg];
4213 		mc=maxBuildingChangesPerWeekForStudentsMaxChanges[sbg];
4214 	}
4215 	else{
4216 		perc=maxBuildingChangesPerWeekForTeachersPercentages[tch];
4217 		mc=maxBuildingChangesPerWeekForTeachersMaxChanges[tch];
4218 	}
4219 	if(perc==-1){
4220 		assert(mc==-1);
4221 		return true;
4222 	}
4223 
4224 	//Old comment below
4225 	//I would like to get rid of these large static variables, but making them dynamic slows down ~33% for a sample from Timisoara Economics
4226 	//static int weekBuildings[MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
4227 	//static int weekActivities[MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
4228 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
4229 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
4230 			int ai2;
4231 			if(sbg>=0)
4232 				ai2=newSubgroupsTimetable(sbg,d2,h2);
4233 			else
4234 				ai2=newTeachersTimetable(tch,d2,h2);
4235 
4236 			if(ai2>=0 && !globalConflActivities.contains(ai2) && !tmp_list.contains(ai2)){
4237 				int rm2;
4238 				if(d==d2 && h2>=h && h2<h+act->duration){
4239 					assert(ai2==ai);
4240 					rm2=rm;
4241 				}
4242 				else
4243 					rm2=c.rooms[ai2];
4244 				if(rm2!=UNALLOCATED_SPACE && rm2!=UNSPECIFIED_ROOM){
4245 					assert(rm2>=0);
4246 					weekActivities[d2][h2]=ai2;
4247 					weekBuildings[d2][h2]=gt.rules.internalRoomsList[rm2]->buildingIndex;
4248 				}
4249 				else{
4250 					weekActivities[d2][h2]=ai2;
4251 					weekBuildings[d2][h2]=-1;
4252 				}
4253 			}
4254 			else{
4255 				weekBuildings[d2][h2]=-1;
4256 				weekActivities[d2][h2]=-1;
4257 			}
4258 		}
4259 	}
4260 
4261 	int n_changes=0;
4262 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
4263 		int crt_building=-1;
4264 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++)
4265 			if(weekBuildings[d2][h2]!=-1){
4266 				if(crt_building!=weekBuildings[d2][h2]){
4267 					if(crt_building!=-1)
4268 						n_changes++;
4269 					crt_building=weekBuildings[d2][h2];
4270 				}
4271 			}
4272 	}
4273 
4274 	if(n_changes>mc){ //not OK
4275 		if(level>=LEVEL_STOP_CONFLICTS_CALCULATION)
4276 			return false;
4277 
4278 		QList<int> removableActsList;
4279 		for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
4280 			for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
4281 				if(!(d2==d && h2>=h && h2<h+act->duration))
4282 					if(weekBuildings[d2][h2]!=-1 && weekActivities[d2][h2]>=0 && !swappedActivities[weekActivities[d2][h2]] && !(fixedTimeActivity[weekActivities[d2][h2]] && fixedSpaceActivity[weekActivities[d2][h2]]))
4283 						if(!removableActsList.contains(weekActivities[d2][h2])){
4284 							removableActsList.append(weekActivities[d2][h2]);
4285 							assert(!globalConflActivities.contains(weekActivities[d2][h2]));
4286 							assert(!tmp_list.contains(weekActivities[d2][h2]));
4287 						}
4288 			}
4289 		}
4290 
4291 		for(;;){
4292 			int ai2=-1;
4293 			QList<int> optimalRemovableActs;
4294 			if(level==0){
4295 				int nWrong=INF;
4296 				for(int a : qAsConst(removableActsList))
4297 					if(nWrong>triedRemovals(a,c.times[a])){
4298 						nWrong=triedRemovals(a,c.times[a]);
4299 					}
4300 				for(int a : qAsConst(removableActsList))
4301 					if(nWrong==triedRemovals(a,c.times[a]))
4302 						optimalRemovableActs.append(a);
4303 			}
4304 			else
4305 				optimalRemovableActs=removableActsList;
4306 
4307 			if(removableActsList.count()>0)
4308 				assert(optimalRemovableActs.count()>0);
4309 
4310 			if(optimalRemovableActs.count()==0)
4311 				return false;
4312 
4313 			ai2=optimalRemovableActs.at(rng.intMRG32k3a(optimalRemovableActs.count()));
4314 
4315 			assert(!swappedActivities[ai2]);
4316 			assert(!(fixedTimeActivity[ai2] && fixedSpaceActivity[ai2]));
4317 			assert(!globalConflActivities.contains(ai2));
4318 			assert(!tmp_list.contains(ai2));
4319 			assert(ai2>=0);
4320 
4321 			tmp_list.append(ai2);
4322 
4323 			int t=removableActsList.removeAll(ai2);
4324 			assert(t==1);
4325 
4326 			int ha=c.times[ai2]/gt.rules.nDaysPerWeek;
4327 			int da=c.times[ai2]%gt.rules.nDaysPerWeek;
4328 			int dura=gt.rules.internalActivitiesList[ai2].duration;
4329 			for(int h2=ha; h2<ha+dura; h2++){
4330 				assert(weekActivities[da][h2]==ai2);
4331 				assert(weekBuildings[da][h2]!=-1);
4332 				weekBuildings[da][h2]=-1;
4333 				weekActivities[da][h2]=-1;
4334 			}
4335 
4336 			int n_changes=0;
4337 			for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
4338 				int crt_building=-1;
4339 				for(int h2=0; h2<gt.rules.nHoursPerDay; h2++)
4340 					if(weekBuildings[d2][h2]!=-1){
4341 						if(crt_building!=weekBuildings[d2][h2]){
4342 							if(crt_building!=-1)
4343 								n_changes++;
4344 							crt_building=weekBuildings[d2][h2];
4345 						}
4346 					}
4347 			}
4348 
4349 			if(n_changes<=mc){ //OK
4350 				break;
4351 			}
4352 		}
4353 	}
4354 
4355 	return true;
4356 }
4357 
checkRoomChanges(int sbg,int tch,const QList<int> & globalConflActivities,int rm,int level,const Activity * act,int ai,int d,int h,QList<int> & tmp_list)4358 inline bool Generate::checkRoomChanges(int sbg, int tch, const QList<int>& globalConflActivities, int rm, int level, const Activity* act, int ai, int d, int h, QList<int>& tmp_list)
4359 {
4360 	assert((sbg==-1 && tch>=0) || (sbg>=0 && tch==-1));
4361 	if(sbg>=0)
4362 		assert(sbg<gt.rules.nInternalSubgroups);
4363 	if(tch>=0)
4364 		assert(tch<gt.rules.nInternalTeachers);
4365 
4366 	if(sbg>=0)
4367 		assert(minGapsBetweenRoomChangesForStudentsPercentages[sbg]>=0 || maxRoomChangesPerDayForStudentsPercentages[sbg]>=0
4368 		   || maxRoomChangesPerWeekForStudentsPercentages[sbg]>=0);
4369 	if(tch>=0)
4370 		assert(minGapsBetweenRoomChangesForTeachersPercentages[tch]>=0 || maxRoomChangesPerDayForTeachersPercentages[tch]>=0
4371 		   || maxRoomChangesPerWeekForTeachersPercentages[tch]>=0);
4372 
4373 	//int rooms[MAX_HOURS_PER_DAY], activities[MAX_HOURS_PER_DAY];
4374 	for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
4375 		int ai2;
4376 		if(sbg>=0)
4377 			ai2=newSubgroupsTimetable(sbg,d,h2);
4378 		else
4379 			ai2=newTeachersTimetable(tch,d,h2);
4380 
4381 		if(ai2>=0 && !globalConflActivities.contains(ai2) && !tmp_list.contains(ai2)){
4382 			int rm2;
4383 			if(h2>=h && h2<h+act->duration){
4384 				assert(ai2==ai);
4385 				rm2=rm;
4386 			}
4387 			else
4388 				rm2=c.rooms[ai2];
4389 			if(rm2!=UNALLOCATED_SPACE && rm2!=UNSPECIFIED_ROOM){
4390 				assert(rm2>=0);
4391 				activities[h2]=ai2;
4392 				rooms[h2]=rm2;
4393 			}
4394 			else{
4395 				activities[h2]=ai2;
4396 				rooms[h2]=-1;
4397 			}
4398 		}
4399 		else{
4400 			rooms[h2]=-1;
4401 			activities[h2]=-1;
4402 		}
4403 	}
4404 
4405 	assert(rooms[h]!=-1); //because we checked this before calling the function checkRoomChanges(...)
4406 	//if(rooms[h]==-1) //no problem
4407 	//	return true;
4408 
4409 	//min gaps
4410 	double perc;
4411 	int mg;
4412 	if(sbg>=0){
4413 		perc=minGapsBetweenRoomChangesForStudentsPercentages[sbg];
4414 		mg=minGapsBetweenRoomChangesForStudentsMinGaps[sbg];
4415 	}
4416 	else{
4417 		perc=minGapsBetweenRoomChangesForTeachersPercentages[tch];
4418 		mg=minGapsBetweenRoomChangesForTeachersMinGaps[tch];
4419 	}
4420 	if(perc>=0){
4421 		for(int h2=max(0, h-mg); h2<=min(h+act->duration-1+mg, gt.rules.nHoursPerDay-1); h2++)
4422 			if(!(h2>=h && h2<h+act->duration))
4423 				if(rooms[h2]!=rooms[h] && rooms[h2]!=-1){
4424 					int ai2=activities[h2];
4425 					assert(ai2>=0);
4426 					if(!swappedActivities[ai2] && !(fixedTimeActivity[ai2] && fixedSpaceActivity[ai2])){
4427 						if(!tmp_list.contains(ai2)){
4428 							tmp_list.append(ai2);
4429 
4430 							int ha=c.times[ai2]/gt.rules.nDaysPerWeek;
4431 							int dura=gt.rules.internalActivitiesList[ai2].duration;
4432 							for(int h3=ha; h3<ha+dura; h3++){
4433 								assert(activities[h3]==ai2);
4434 								assert(rooms[h3]!=-1);
4435 								rooms[h3]=-1;
4436 								activities[h3]=-1;
4437 							}
4438 						}
4439 					}
4440 					else{
4441 						return false;
4442 					}
4443 				}
4444 	}
4445 
4446 	//max changes per day
4447 	int mc;
4448 	if(sbg>=0){
4449 		perc=maxRoomChangesPerDayForStudentsPercentages[sbg];
4450 		mc=maxRoomChangesPerDayForStudentsMaxChanges[sbg];
4451 	}
4452 	else{
4453 		perc=maxRoomChangesPerDayForTeachersPercentages[tch];
4454 		mc=maxRoomChangesPerDayForTeachersMaxChanges[tch];
4455 	}
4456 
4457 	if(perc>=0){
4458 		int crt_room=-1;
4459 		int n_changes=0;
4460 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++)
4461 			if(rooms[h2]!=-1){
4462 				if(crt_room!=rooms[h2]){
4463 					if(crt_room!=-1)
4464 						n_changes++;
4465 					crt_room=rooms[h2];
4466 				}
4467 			}
4468 
4469 		if(n_changes>mc){ //not OK
4470 			if(level>=LEVEL_STOP_CONFLICTS_CALCULATION)
4471 				return false;
4472 
4473 			QList<int> removableActsList;
4474 			for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
4475 				if(!(h2>=h && h2<h+act->duration))
4476 					if(rooms[h2]!=-1 && activities[h2]>=0 && !swappedActivities[activities[h2]] && !(fixedTimeActivity[activities[h2]] && fixedSpaceActivity[activities[h2]]))
4477 						if(!removableActsList.contains(activities[h2])){
4478 							removableActsList.append(activities[h2]);
4479 							assert(!globalConflActivities.contains(activities[h2]));
4480 							assert(!tmp_list.contains(activities[h2]));
4481 						}
4482 			}
4483 
4484 			for(;;){
4485 				int ai2=-1;
4486 				QList<int> optimalRemovableActs;
4487 				if(level==0){
4488 					int nWrong=INF;
4489 					for(int a : qAsConst(removableActsList))
4490 						if(nWrong>triedRemovals(a,c.times[a])){
4491 							nWrong=triedRemovals(a,c.times[a]);
4492 						}
4493 					for(int a : qAsConst(removableActsList))
4494 						if(nWrong==triedRemovals(a,c.times[a]))
4495 							optimalRemovableActs.append(a);
4496 				}
4497 				else
4498 					optimalRemovableActs=removableActsList;
4499 
4500 				if(removableActsList.count()>0)
4501 					assert(optimalRemovableActs.count()>0);
4502 
4503 				if(optimalRemovableActs.count()==0)
4504 					return false;
4505 
4506 				ai2=optimalRemovableActs.at(rng.intMRG32k3a(optimalRemovableActs.count()));
4507 
4508 				assert(!swappedActivities[ai2]);
4509 				assert(!(fixedTimeActivity[ai2] && fixedSpaceActivity[ai2]));
4510 				assert(!globalConflActivities.contains(ai2));
4511 				assert(!tmp_list.contains(ai2));
4512 				assert(ai2>=0);
4513 
4514 				tmp_list.append(ai2);
4515 
4516 				int t=removableActsList.removeAll(ai2);
4517 				assert(t==1);
4518 
4519 				int ha=c.times[ai2]/gt.rules.nDaysPerWeek;
4520 				int da=gt.rules.internalActivitiesList[ai2].duration;
4521 				for(int h2=ha; h2<ha+da; h2++){
4522 					assert(activities[h2]==ai2);
4523 					assert(rooms[h2]!=-1);
4524 					rooms[h2]=-1;
4525 					activities[h2]=-1;
4526 				}
4527 
4528 				int crt_room=-1;
4529 				int n_changes=0;
4530 				for(int h2=0; h2<gt.rules.nHoursPerDay; h2++)
4531 					if(rooms[h2]!=-1){
4532 						if(crt_room!=rooms[h2]){
4533 							if(crt_room!=-1)
4534 								n_changes++;
4535 							crt_room=rooms[h2];
4536 						}
4537 					}
4538 
4539 				if(n_changes<=mc){ //OK
4540 					break;
4541 				}
4542 			}
4543 		}
4544 	}
4545 
4546 	//max changes per week
4547 	if(sbg>=0){
4548 		perc=maxRoomChangesPerWeekForStudentsPercentages[sbg];
4549 		mc=maxRoomChangesPerWeekForStudentsMaxChanges[sbg];
4550 	}
4551 	else{
4552 		perc=maxRoomChangesPerWeekForTeachersPercentages[tch];
4553 		mc=maxRoomChangesPerWeekForTeachersMaxChanges[tch];
4554 	}
4555 	if(perc==-1){
4556 		assert(mc==-1);
4557 		return true;
4558 	}
4559 
4560 	//Old comment below
4561 	//I would like to get rid of these large static variables, but making them dynamic slows down ~33% for a sample from Timisoara Economics
4562 	//static int weekRooms[MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
4563 	//static int weekActivities[MAX_DAYS_PER_WEEK][MAX_HOURS_PER_DAY];
4564 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
4565 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
4566 			int ai2;
4567 			if(sbg>=0)
4568 				ai2=newSubgroupsTimetable(sbg,d2,h2);
4569 			else
4570 				ai2=newTeachersTimetable(tch,d2,h2);
4571 
4572 			if(ai2>=0 && !globalConflActivities.contains(ai2) && !tmp_list.contains(ai2)){
4573 				int rm2;
4574 				if(d==d2 && h2>=h && h2<h+act->duration){
4575 					assert(ai2==ai);
4576 					rm2=rm;
4577 				}
4578 				else
4579 					rm2=c.rooms[ai2];
4580 				if(rm2!=UNALLOCATED_SPACE && rm2!=UNSPECIFIED_ROOM){
4581 					assert(rm2>=0);
4582 					weekActivities[d2][h2]=ai2;
4583 					weekRooms[d2][h2]=rm2;
4584 				}
4585 				else{
4586 					weekActivities[d2][h2]=ai2;
4587 					weekRooms[d2][h2]=-1;
4588 				}
4589 			}
4590 			else{
4591 				weekRooms[d2][h2]=-1;
4592 				weekActivities[d2][h2]=-1;
4593 			}
4594 		}
4595 	}
4596 
4597 	int n_changes=0;
4598 	for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
4599 		int crt_room=-1;
4600 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++)
4601 			if(weekRooms[d2][h2]!=-1){
4602 				if(crt_room!=weekRooms[d2][h2]){
4603 					if(crt_room!=-1)
4604 						n_changes++;
4605 					crt_room=weekRooms[d2][h2];
4606 				}
4607 			}
4608 	}
4609 
4610 	if(n_changes>mc){ //not OK
4611 		if(level>=LEVEL_STOP_CONFLICTS_CALCULATION)
4612 			return false;
4613 
4614 		QList<int> removableActsList;
4615 		for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
4616 			for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
4617 				if(!(d2==d && h2>=h && h2<h+act->duration))
4618 					if(weekRooms[d2][h2]!=-1 && weekActivities[d2][h2]>=0 && !swappedActivities[weekActivities[d2][h2]] && !(fixedTimeActivity[weekActivities[d2][h2]] && fixedSpaceActivity[weekActivities[d2][h2]]))
4619 						if(!removableActsList.contains(weekActivities[d2][h2])){
4620 							removableActsList.append(weekActivities[d2][h2]);
4621 							assert(!globalConflActivities.contains(weekActivities[d2][h2]));
4622 							assert(!tmp_list.contains(weekActivities[d2][h2]));
4623 						}
4624 			}
4625 		}
4626 
4627 		for(;;){
4628 			int ai2=-1;
4629 			QList<int> optimalRemovableActs;
4630 			if(level==0){
4631 				int nWrong=INF;
4632 				for(int a : qAsConst(removableActsList))
4633 					if(nWrong>triedRemovals(a,c.times[a])){
4634 						nWrong=triedRemovals(a,c.times[a]);
4635 					}
4636 				for(int a : qAsConst(removableActsList))
4637 					if(nWrong==triedRemovals(a,c.times[a]))
4638 						optimalRemovableActs.append(a);
4639 			}
4640 			else
4641 				optimalRemovableActs=removableActsList;
4642 
4643 			if(removableActsList.count()>0)
4644 				assert(optimalRemovableActs.count()>0);
4645 
4646 			if(optimalRemovableActs.count()==0)
4647 				return false;
4648 
4649 			ai2=optimalRemovableActs.at(rng.intMRG32k3a(optimalRemovableActs.count()));
4650 
4651 			assert(!swappedActivities[ai2]);
4652 			assert(!(fixedTimeActivity[ai2] && fixedSpaceActivity[ai2]));
4653 			assert(!globalConflActivities.contains(ai2));
4654 			assert(!tmp_list.contains(ai2));
4655 			assert(ai2>=0);
4656 
4657 			tmp_list.append(ai2);
4658 
4659 			int t=removableActsList.removeAll(ai2);
4660 			assert(t==1);
4661 
4662 			int ha=c.times[ai2]/gt.rules.nDaysPerWeek;
4663 			int da=c.times[ai2]%gt.rules.nDaysPerWeek;
4664 			int dura=gt.rules.internalActivitiesList[ai2].duration;
4665 			for(int h2=ha; h2<ha+dura; h2++){
4666 				assert(weekActivities[da][h2]==ai2);
4667 				assert(weekRooms[da][h2]!=-1);
4668 				weekRooms[da][h2]=-1;
4669 				weekActivities[da][h2]=-1;
4670 			}
4671 
4672 			int n_changes=0;
4673 			for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
4674 				int crt_room=-1;
4675 				for(int h2=0; h2<gt.rules.nHoursPerDay; h2++)
4676 					if(weekRooms[d2][h2]!=-1){
4677 						if(crt_room!=weekRooms[d2][h2]){
4678 							if(crt_room!=-1)
4679 								n_changes++;
4680 							crt_room=weekRooms[d2][h2];
4681 						}
4682 					}
4683 			}
4684 
4685 			if(n_changes<=mc){ //OK
4686 				break;
4687 			}
4688 		}
4689 	}
4690 
4691 	return true;
4692 }
4693 
checkRoomChangesPerRealDay(int sbg,int tch,const QList<int> & globalConflActivities,int rm,int level,const Activity * act,int ai,int d,int h,QList<int> & tmp_list)4694 inline bool Generate::checkRoomChangesPerRealDay(int sbg, int tch, const QList<int>& globalConflActivities, int rm, int level, const Activity* act, int ai, int d, int h, QList<int>& tmp_list)
4695 {
4696 	assert((sbg==-1 && tch>=0) || (sbg>=0 && tch==-1));
4697 	if(sbg>=0){
4698 		assert(sbg<gt.rules.nInternalSubgroups);
4699 		assert(maxRoomChangesPerRealDayForSubgroupsPercentages[sbg]>=0);
4700 	}
4701 	else if(tch>=0){
4702 		assert(tch<gt.rules.nInternalTeachers);
4703 		assert(maxRoomChangesPerRealDayForTeachersPercentages[tch]>=0);
4704 	}
4705 
4706 	//int rooms[2*MAX_HOURS_PER_DAY], activities[2*MAX_HOURS_PER_DAY];
4707 	for(int q=0; q<2; q++){
4708 		for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
4709 			int ai2;
4710 			if(tch>=0)
4711 				ai2=newTeachersTimetable(tch,(d/2)*2+q,h2);
4712 			else
4713 				ai2=newSubgroupsTimetable(sbg,(d/2)*2+q,h2);
4714 
4715 			if(ai2>=0 && !globalConflActivities.contains(ai2) && !tmp_list.contains(ai2)){
4716 				int rm2;
4717 				if((d==(d/2)*2+q) && (h2>=h && h2<h+act->duration)){
4718 					assert(ai2==ai);
4719 					rm2=rm;
4720 				}
4721 				else
4722 					rm2=c.rooms[ai2];
4723 				if(rm2!=UNALLOCATED_SPACE && rm2!=UNSPECIFIED_ROOM){
4724 					assert(rm2>=0);
4725 					activitiesx2[h2+q*gt.rules.nHoursPerDay]=ai2;
4726 					roomsx2[h2+q*gt.rules.nHoursPerDay]=rm2;
4727 				}
4728 				else{
4729 					activitiesx2[h2+q*gt.rules.nHoursPerDay]=ai2;
4730 					roomsx2[h2+q*gt.rules.nHoursPerDay]=-1;
4731 				}
4732 			}
4733 			else{
4734 				roomsx2[h2+q*gt.rules.nHoursPerDay]=-1;
4735 				activitiesx2[h2+q*gt.rules.nHoursPerDay]=-1;
4736 			}
4737 		}
4738 	}
4739 
4740 	if(roomsx2[h+(d%2)*gt.rules.nHoursPerDay]==-1){ //we entered here assuming rm is a valid room
4741 		assert(0);
4742 		return true;
4743 	}
4744 
4745 	double perc;
4746 	int mc;
4747 	if(tch>=0){
4748 		perc=maxRoomChangesPerRealDayForTeachersPercentages[tch];
4749 		mc=maxRoomChangesPerRealDayForTeachersMaxChanges[tch];
4750 	}
4751 	else{
4752 		perc=maxRoomChangesPerRealDayForSubgroupsPercentages[sbg];
4753 		mc=maxRoomChangesPerRealDayForSubgroupsMaxChanges[sbg];
4754 	}
4755 
4756 	if(perc>=0){
4757 		int crt_room=-1;
4758 		int n_changes=0;
4759 		for(int h2=0; h2<2*gt.rules.nHoursPerDay; h2++)
4760 			if(roomsx2[h2]!=-1){
4761 				if(crt_room!=roomsx2[h2]){
4762 					if(crt_room!=-1)
4763 						n_changes++;
4764 					crt_room=roomsx2[h2];
4765 				}
4766 			}
4767 
4768 		if(n_changes>mc){ //not OK
4769 			if(level>=LEVEL_STOP_CONFLICTS_CALCULATION)
4770 				return false;
4771 
4772 			QList<int> removableActsList;
4773 			for(int h2=0; h2<2*gt.rules.nHoursPerDay; h2++){
4774 				if(!(h2>=h+(d%2)*gt.rules.nHoursPerDay && h2<h+act->duration+(d%2)*gt.rules.nHoursPerDay))
4775 					if(roomsx2[h2]!=-1 && activitiesx2[h2]>=0 && !swappedActivities[activitiesx2[h2]] && !(fixedTimeActivity[activitiesx2[h2]]&&fixedSpaceActivity[activitiesx2[h2]]))
4776 						if(!removableActsList.contains(activitiesx2[h2])){
4777 							removableActsList.append(activitiesx2[h2]);
4778 							assert(!globalConflActivities.contains(activitiesx2[h2]));
4779 							assert(!tmp_list.contains(activitiesx2[h2]));
4780 						}
4781 			}
4782 
4783 			for(;;){
4784 				int ai2=-1;
4785 				QList<int> optimalRemovableActs;
4786 				if(level==0){
4787 					int nWrong=INF;
4788 					for(int a : qAsConst(removableActsList))
4789 						if(nWrong>triedRemovals(a,c.times[a]) ){
4790 							nWrong=triedRemovals(a,c.times[a]);
4791 						}
4792 					for(int a : qAsConst(removableActsList))
4793 						if(nWrong==triedRemovals(a,c.times[a]))
4794 							optimalRemovableActs.append(a);
4795 				}
4796 				else
4797 					optimalRemovableActs=removableActsList;
4798 
4799 				if(removableActsList.count()>0)
4800 					assert(optimalRemovableActs.count()>0);
4801 
4802 				if(optimalRemovableActs.count()==0)
4803 					return false;
4804 
4805 				ai2=optimalRemovableActs.at(rng.intMRG32k3a(optimalRemovableActs.count()));
4806 
4807 				assert(!swappedActivities[ai2]);
4808 				assert(!(fixedTimeActivity[ai2]&&fixedSpaceActivity[ai2]));
4809 				assert(!globalConflActivities.contains(ai2));
4810 				assert(!tmp_list.contains(ai2));
4811 				assert(ai2>=0);
4812 
4813 				tmp_list.append(ai2);
4814 
4815 				int t=removableActsList.removeAll(ai2);
4816 				assert(t==1);
4817 
4818 				int ha=c.times[ai2]/gt.rules.nDaysPerWeek;
4819 				int da=c.times[ai2]%gt.rules.nDaysPerWeek;
4820 				int dura=gt.rules.internalActivitiesList[ai2].duration;
4821 				for(int h2=ha; h2<ha+dura; h2++){
4822 					assert(activitiesx2[h2+(da%2)*gt.rules.nHoursPerDay]==ai2);
4823 					assert(roomsx2[h2+(da%2)*gt.rules.nHoursPerDay]!=-1);
4824 					roomsx2[h2+(da%2)*gt.rules.nHoursPerDay]=-1;
4825 					activitiesx2[h2+(da%2)*gt.rules.nHoursPerDay]=-1;
4826 				}
4827 
4828 				int crt_room=-1;
4829 				int n_changes=0;
4830 				for(int h2=0; h2<2*gt.rules.nHoursPerDay; h2++)
4831 					if(roomsx2[h2]!=-1){
4832 						if(crt_room!=roomsx2[h2]){
4833 							if(crt_room!=-1)
4834 								n_changes++;
4835 							crt_room=roomsx2[h2];
4836 						}
4837 					}
4838 
4839 				if(n_changes<=mc){ //OK
4840 					break;
4841 				}
4842 			}
4843 		}
4844 	}
4845 
4846 	return true;
4847 }
4848 
4849 //2012-04-29
checkActivitiesOccupyMaxDifferentRooms(const QList<int> & globalConflActivities,int rm,int level,int ai,QList<int> & tmp_list)4850 inline bool Generate::checkActivitiesOccupyMaxDifferentRooms(const QList<int>& globalConflActivities, int rm, int level, int ai, QList<int>& tmp_list)
4851 {
4852 	for(ActivitiesOccupyMaxDifferentRooms_item* item : qAsConst(aomdrListForActivity[ai])){
4853 		//preliminary
4854 		QSet<int> occupiedRoomsSet;
4855 		for(int ai2 : qAsConst(item->activitiesList))
4856 			if(ai2!=ai && !globalConflActivities.contains(ai2) && !tmp_list.contains(ai2)){
4857 				int rm2=c.rooms[ai2];
4858 				if(rm2!=UNALLOCATED_SPACE && rm2!=UNSPECIFIED_ROOM)
4859 					if(!occupiedRoomsSet.contains(rm2)){
4860 						occupiedRoomsSet.insert(rm2);
4861 						if(occupiedRoomsSet.count()==item->maxDifferentRooms) //no further testing needed
4862 							break;
4863 					}
4864 			}
4865 
4866 		if(!globalConflActivities.contains(ai) && !tmp_list.contains(ai)) //should be always true
4867 			if(rm!=UNALLOCATED_SPACE && rm!=UNSPECIFIED_ROOM) //should be always true
4868 				if(!occupiedRoomsSet.contains(rm))
4869 					occupiedRoomsSet.insert(rm);
4870 
4871 		if(occupiedRoomsSet.count()<=item->maxDifferentRooms)
4872 			continue;
4873 
4874 		//correction needed
4875 		QList<QSet<int>> activitiesInRoom;
4876 		occupiedRoomsSet.clear();
4877 		QList<int> occupiedRoomsList;
4878 		QHash<int, int> roomIndexInOccupiedRoomsList;
4879 		QList<bool> canEmptyRoom;
4880 
4881 		for(int ai2 : qAsConst(item->activitiesList))
4882 			if(!globalConflActivities.contains(ai2) && !tmp_list.contains(ai2)){
4883 				int rm2;
4884 				if(ai2==ai)
4885 					rm2=rm;
4886 				else
4887 					rm2=c.rooms[ai2];
4888 				if(rm2!=UNALLOCATED_SPACE && rm2!=UNSPECIFIED_ROOM){
4889 					int ind;
4890 					if(!occupiedRoomsSet.contains(rm2)){
4891 						occupiedRoomsSet.insert(rm2);
4892 						occupiedRoomsList.append(rm2);
4893 						canEmptyRoom.append(true);
4894 
4895 						QSet<int> tl;
4896 						tl.insert(ai2);
4897 						activitiesInRoom.append(tl);
4898 
4899 						ind=activitiesInRoom.count()-1;
4900 						roomIndexInOccupiedRoomsList.insert(rm2, ind);
4901 					}
4902 					else{
4903 						assert(roomIndexInOccupiedRoomsList.contains(rm2));
4904 						ind=roomIndexInOccupiedRoomsList.value(rm2);
4905 						assert(ind>=0 && ind<activitiesInRoom.count());
4906 						activitiesInRoom[ind].insert(ai2);
4907 					}
4908 
4909 					if(ai2==ai || (fixedTimeActivity[ai2] && fixedSpaceActivity[ai2]) || swappedActivities[ai2])
4910 						if(canEmptyRoom[ind]==true)
4911 							canEmptyRoom[ind]=false;
4912 				}
4913 			}
4914 
4915 		assert(occupiedRoomsSet.count()==item->maxDifferentRooms+1);
4916 
4917 		QList<int> candidates;
4918 		for(int rm2 : qAsConst(occupiedRoomsList)){
4919 			assert(roomIndexInOccupiedRoomsList.contains(rm2));
4920 			int ind=roomIndexInOccupiedRoomsList.value(rm2);
4921 			if(canEmptyRoom.at(ind)==true)
4922 				candidates.append(ind);
4923 		}
4924 
4925 		if(level==0){
4926 			QList<int> finalCandidates;
4927 
4928 			int optConflActivities=MAX_ACTIVITIES;
4929 			int optMinWrong=INF;
4930 			int optNWrong=INF;
4931 			int optMinIndexAct=gt.rules.nInternalActivities;
4932 
4933 			//phase 1
4934 			for(int ind : qAsConst(candidates)){
4935 				const QSet<int>& activitiesForCandidate=activitiesInRoom.at(ind);
4936 
4937 				int tmp_n_confl_acts=activitiesForCandidate.count();
4938 				int tmp_minWrong=INF;
4939 				int tmp_nWrong=0;
4940 				int tmp_minIndexAct=gt.rules.nInternalActivities;
4941 
4942 				if(activitiesForCandidate.count()>0){
4943 					for(int ai2 : qAsConst(activitiesForCandidate)){
4944 						tmp_minWrong=min(tmp_minWrong, triedRemovals(ai2,c.times[ai2]));
4945 						tmp_nWrong+=triedRemovals(ai2,c.times[ai2]);
4946 						tmp_minIndexAct=min(tmp_minIndexAct, invPermutation[ai2]);
4947 					}
4948 				}
4949 				else{
4950 					assert(0);
4951 					tmp_minWrong=0;
4952 					tmp_nWrong=0;
4953 					tmp_minIndexAct=-1;
4954 				}
4955 
4956 				if(optMinWrong>tmp_minWrong ||
4957 				  (optMinWrong==tmp_minWrong && optNWrong>tmp_nWrong) ||
4958 				  (optMinWrong==tmp_minWrong && optNWrong==tmp_nWrong && optConflActivities>tmp_n_confl_acts) ||
4959 				  (optMinWrong==tmp_minWrong && optNWrong==tmp_nWrong && optConflActivities==tmp_n_confl_acts && optMinIndexAct>tmp_minIndexAct)){
4960 					optConflActivities=tmp_n_confl_acts;
4961 					optMinWrong=tmp_minWrong;
4962 					optNWrong=tmp_nWrong;
4963 					optMinIndexAct=tmp_minIndexAct;
4964 				}
4965 			}
4966 
4967 			//phase 2
4968 			for(int ind : qAsConst(candidates)){
4969 				const QSet<int>& activitiesForCandidate=activitiesInRoom.at(ind);
4970 
4971 				int tmp_n_confl_acts=activitiesForCandidate.count();
4972 				int tmp_minWrong=INF;
4973 				int tmp_nWrong=0;
4974 				int tmp_minIndexAct=gt.rules.nInternalActivities;
4975 
4976 				if(activitiesForCandidate.count()>0){
4977 					for(int ai2 : qAsConst(activitiesForCandidate)){
4978 						tmp_minWrong=min(tmp_minWrong, triedRemovals(ai2,c.times[ai2]));
4979 						tmp_nWrong+=triedRemovals(ai2,c.times[ai2]);
4980 						tmp_minIndexAct=min(tmp_minIndexAct, invPermutation[ai2]);
4981 					}
4982 				}
4983 				else{
4984 					assert(0);
4985 					tmp_minWrong=0;
4986 					tmp_nWrong=0;
4987 					tmp_minIndexAct=-1;
4988 				}
4989 
4990 				if(optMinWrong==tmp_minWrong && optNWrong==tmp_nWrong && optConflActivities==tmp_n_confl_acts && optMinIndexAct==tmp_minIndexAct){
4991 					finalCandidates.append(ind);
4992 				}
4993 			}
4994 
4995 			//phase 3
4996 			if(candidates.count()>0)
4997 				assert(finalCandidates.count()>0);
4998 			candidates=finalCandidates;
4999 		}
5000 		else{ //if(level>0)
5001 			QList<int> finalCandidates;
5002 
5003 			int optConflActivities=MAX_ACTIVITIES;
5004 
5005 			for(int ind : qAsConst(candidates)){
5006 				if(activitiesInRoom.at(ind).count()<optConflActivities){
5007 					optConflActivities=activitiesInRoom.at(ind).count();
5008 					finalCandidates.clear();
5009 					finalCandidates.append(ind);
5010 				}
5011 				else if(activitiesInRoom.at(ind).count()==optConflActivities){
5012 					finalCandidates.append(ind);
5013 				}
5014 			}
5015 
5016 			if(candidates.count()>0)
5017 				assert(finalCandidates.count()>0);
5018 			candidates=finalCandidates;
5019 		}
5020 
5021 		if(candidates.count()==0)
5022 			return false;
5023 
5024 		int indexToRemove=candidates.at(rng.intMRG32k3a(candidates.count()));
5025 		assert(canEmptyRoom.at(indexToRemove)==true);
5026 
5027 		//To keep the generation identical on all computers - 2013-01-03
5028 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
5029 		QList<int> tmpListFromSet=QList<int>(activitiesInRoom.at(indexToRemove).constBegin(), activitiesInRoom.at(indexToRemove).constEnd());
5030 #else
5031 		QList<int> tmpListFromSet=activitiesInRoom.at(indexToRemove).toList();
5032 #endif
5033 		std::stable_sort(tmpListFromSet.begin(), tmpListFromSet.end());
5034 		//Randomize list
5035 		for(int i=0; i<tmpListFromSet.count(); i++){
5036 			int t=tmpListFromSet.at(i);
5037 			int r=rng.intMRG32k3a(tmpListFromSet.count()-i);
5038 			tmpListFromSet[i]=tmpListFromSet[i+r];
5039 			tmpListFromSet[i+r]=t;
5040 		}
5041 
5042 		for(int ai2 : qAsConst(tmpListFromSet)){
5043 			assert(!globalConflActivities.contains(ai2) && !tmp_list.contains(ai2));
5044 			assert(ai2!=ai && !(fixedTimeActivity[ai2] && fixedSpaceActivity[ai2]) && !swappedActivities[ai2]);
5045 			tmp_list.append(ai2);
5046 		}
5047 	}
5048 
5049 	return true;
5050 }
5051 
5052 //2013-09-14
checkActivitiesSameRoomIfConsecutive(const QList<int> & globalConflActivities,int rm,int ai,int d,int h,QList<int> & tmp_list)5053 inline bool Generate::checkActivitiesSameRoomIfConsecutive(const QList<int>& globalConflActivities, int rm, int ai, int d, int h, QList<int>& tmp_list)
5054 {
5055 	for(ActivitiesSameRoomIfConsecutive_item* item : qAsConst(asricListForActivity[ai])){
5056 		for(int ai2 : qAsConst(item->activitiesList)){
5057 			if(ai2!=ai && !globalConflActivities.contains(ai2) && !tmp_list.contains(ai2)){
5058 				int rm2=c.rooms[ai2];
5059 				if(rm2!=UNALLOCATED_SPACE && rm2!=UNSPECIFIED_ROOM){
5060 					if(c.times[ai2]!=UNALLOCATED_TIME){
5061 						int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
5062 						int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
5063 
5064 						if( d==d2 && (h2==h+gt.rules.internalActivitiesList[ai].duration || h==h2+gt.rules.internalActivitiesList[ai2].duration) ){
5065 							if(rm!=rm2){ //not OK
5066 								if(!(fixedTimeActivity[ai2] && fixedSpaceActivity[ai2]) && !swappedActivities[ai2])
5067 									tmp_list.append(ai2);
5068 								else
5069 									return false;
5070 							}
5071 						}
5072 					}
5073 				}
5074 			}
5075 		}
5076 	}
5077 
5078 	return true;
5079 }
5080 
chooseRoom(const QList<int> & listOfRooms,const QList<int> & globalConflActivities,int level,const Activity * act,int ai,int d,int h,int & roomSlot,int & selectedSlot,QList<int> & localConflActivities,QList<int> & realRoomsList)5081 inline bool Generate::chooseRoom(const QList<int>& listOfRooms, const QList<int>& globalConflActivities, int level, const Activity* act, int ai, int d, int h,
5082  int& roomSlot, int& selectedSlot, QList<int>& localConflActivities, QList<int>& realRoomsList /*if the selected room is virtual, these are the real rooms for sets 0..nsets-1*/)
5083 {
5084 	roomSlot=selectedSlot=UNSPECIFIED_ROOM; //if we don't find a room, return these values
5085 
5086 	int optConflActivities=MAX_ACTIVITIES;
5087 	int optMinWrong=INF;
5088 	int optNWrong=INF;
5089 	int optMinIndexAct=gt.rules.nInternalActivities;
5090 
5091 	QList<QList<int>> conflActivitiesRooms;
5092 	QList<int> nConflActivitiesRooms;
5093 	QList<int> listedRooms;
5094 
5095 	QList<int> minWrong;
5096 	QList<int> nWrong;
5097 	QList<int> minIndexAct;
5098 
5099 	QList<int> tmp_list;
5100 	int tmp_n_confl_acts;
5101 	int tmp_minWrong;
5102 	int tmp_nWrong;
5103 	int tmp_minIndexAct;
5104 
5105 	int newtime=d+h*gt.rules.nDaysPerWeek;
5106 
5107 	QHash<int, QList<int>> realRoomsHash;
5108 
5109 	for(int rm : qAsConst(listOfRooms)){
5110 		int dur;
5111 		for(dur=0; dur<act->duration; dur++){
5112 			if(notAllowedRoomTimePercentages[rm][newtime+dur*gt.rules.nDaysPerWeek]>=0 &&
5113 			 !skipRandom(notAllowedRoomTimePercentages[rm][newtime+dur*gt.rules.nDaysPerWeek]))
5114 				break;
5115 
5116 			if(haveTeacherRoomNotAllowedTimesConstraints){
5117 				bool ok=true;
5118 				for(int tch : qAsConst(act->iTeachersList)){
5119 					double wp=notAllowedTeacherRoomTimePercentages.value(QPair<qint64, qint64>(teacherRoomQInt64Combination(tch, rm), dayHourQInt64Combination(d, h+dur)), -1);
5120 					if(wp>=0 && !skipRandom(wp)){
5121 						ok=false;
5122 						break;
5123 					}
5124 				}
5125 				if(!ok)
5126 					break;
5127 			}
5128 		}
5129 
5130 		if(dur==act->duration){
5131 			tmp_list.clear();
5132 
5133 			int dur2;
5134 			if(gt.rules.internalRoomsList[rm]->isVirtual==false){
5135 				if(gt.rules.mode!=BLOCK_PLANNING){
5136 					for(dur2=0; dur2<act->duration; dur2++){
5137 						int ai2=roomsTimetable(rm,d,h+dur2);
5138 						if(ai2>=0){
5139 							if(!globalConflActivities.contains(ai2)){
5140 								if(swappedActivities[ai2] || (fixedTimeActivity[ai2] && fixedSpaceActivity[ai2])){
5141 									tmp_n_confl_acts=MAX_ACTIVITIES; //not really needed
5142 									break;
5143 								}
5144 								else{
5145 									if(!tmp_list.contains(ai2)){
5146 										tmp_list.append(ai2);
5147 									}
5148 								}
5149 							}
5150 						}
5151 					}
5152 				}
5153 				else{
5154 					//same room in real life time slots?
5155 					for(dur2=0; dur2<act->duration; dur2++){
5156 						int day_tch;
5157 						for(day_tch=0; day_tch<gt.rules.nDaysPerWeek; day_tch++){
5158 							int ai2=roomsTimetable(rm,day_tch,h+dur2);
5159 							if(ai2>=0){
5160 								if(!globalConflActivities.contains(ai2)){
5161 									if(swappedActivities[ai2] || (fixedTimeActivity[ai2] && fixedSpaceActivity[ai2])){
5162 										tmp_n_confl_acts=MAX_ACTIVITIES; //not really needed
5163 										break;
5164 									}
5165 									else{
5166 										if(!tmp_list.contains(ai2)){
5167 											tmp_list.append(ai2);
5168 										}
5169 									}
5170 								}
5171 							}
5172 						}
5173 						if(day_tch<gt.rules.nDaysPerWeek)
5174 							break;
5175 					}
5176 					////////////////////
5177 				}
5178 			}
5179 			else{
5180 				dur2=act->duration;
5181 			}
5182 
5183 			//2019-09-14, suggested by math - virtual room?
5184 			if(dur2==act->duration){
5185 				if(gt.rules.internalRoomsList[rm]->isVirtual==true){
5186 					assert(tmp_list.isEmpty());
5187 
5188 					const QList<QList<int>>& rrsl=gt.rules.internalRoomsList[rm]->rrsl; //real rooms sets list.
5189 					QList<QList<int>> nrrsl; //new rooms sets list, meaning the accepted ones after checking for not available and not swapped and not fixed.
5190 												//2019-09-22: and after checking for preferred real rooms of this activity.
5191 					QSet<int> acceptedRoomsSet;
5192 					QList<int> acceptedRoomsList;
5193 					QHash<int, int> nConflictingActivitiesHash;
5194 					QHash<int, QSet<int>> conflictingActivitiesHash;
5195 					for(const QList<int>& tl : qAsConst(rrsl)){
5196 						QList<int> nrs; //new rooms set
5197 						for(int rr : qAsConst(tl)){ //real room
5198 							if(acceptedRoomsSet.contains(rr)){
5199 								nrs.append(rr);
5200 								continue;
5201 							}
5202 
5203 							if(!preferredRealRooms[ai].isEmpty())
5204 								if(!preferredRealRooms[ai].contains(rr))
5205 									continue;
5206 							int dur3;
5207 							for(dur3=0; dur3<act->duration; dur3++){
5208 								if(notAllowedRoomTimePercentages[rr][newtime+dur3*gt.rules.nDaysPerWeek]>=0 &&
5209 								 !skipRandom(notAllowedRoomTimePercentages[rr][newtime+dur3*gt.rules.nDaysPerWeek]))
5210 									break;
5211 
5212 								if(haveTeacherRoomNotAllowedTimesConstraints){
5213 									bool ok=true;
5214 									for(int tch : qAsConst(act->iTeachersList)){
5215 										double wp=notAllowedTeacherRoomTimePercentages.value(QPair<qint64, qint64>(teacherRoomQInt64Combination(tch, rr), dayHourQInt64Combination(d, h+dur3)), -1);
5216 										if(wp>=0 && !skipRandom(wp)){
5217 											ok=false;
5218 											break;
5219 										}
5220 									}
5221 									if(!ok)
5222 										break;
5223 								}
5224 							}
5225 
5226 							if(dur3==act->duration){
5227 								QSet<int> conflActivitiesForCurrentRoom;
5228 								int dur4;
5229 								if(gt.rules.mode!=BLOCK_PLANNING){
5230 									for(dur4=0; dur4<act->duration; dur4++){
5231 										int ai2=roomsTimetable(rr,d,h+dur4);
5232 										if(ai2>=0){
5233 											if(!globalConflActivities.contains(ai2)){
5234 												if(swappedActivities[ai2] || (fixedTimeActivity[ai2] && fixedSpaceActivity[ai2])){
5235 													break;
5236 												}
5237 												else if(!conflActivitiesForCurrentRoom.contains(ai2)){
5238 													conflActivitiesForCurrentRoom.insert(ai2);
5239 												}
5240 											}
5241 										}
5242 									}
5243 								}
5244 								else{
5245 									//same room in real life time slots?
5246 									for(dur4=0; dur4<act->duration; dur4++){
5247 										int day_tch;
5248 										for(day_tch=0; day_tch<gt.rules.nDaysPerWeek; day_tch++){
5249 											int ai2=roomsTimetable(rr,day_tch,h+dur4);
5250 											if(ai2>=0){
5251 												if(!globalConflActivities.contains(ai2)){
5252 													if(swappedActivities[ai2] || (fixedTimeActivity[ai2] && fixedSpaceActivity[ai2])){
5253 														break;
5254 													}
5255 													else if(!conflActivitiesForCurrentRoom.contains(ai2)){
5256 														conflActivitiesForCurrentRoom.insert(ai2);
5257 													}
5258 												}
5259 											}
5260 										}
5261 										if(day_tch<gt.rules.nDaysPerWeek)
5262 											break;
5263 									}
5264 									///////////////
5265 								}
5266 								if(dur4==act->duration){
5267 									nrs.append(rr);
5268 									if(!acceptedRoomsSet.contains(rr)){
5269 										acceptedRoomsSet.insert(rr);
5270 
5271 										assert(!nConflictingActivitiesHash.contains(rr));
5272 										nConflictingActivitiesHash.insert(rr, conflActivitiesForCurrentRoom.count());
5273 
5274 										if(level==0){
5275 											assert(!conflictingActivitiesHash.contains(rr));
5276 											conflictingActivitiesHash.insert(rr, conflActivitiesForCurrentRoom);
5277 										}
5278 									}
5279 									else{
5280 										assert(0);
5281 									}
5282 								}
5283 							}
5284 						}
5285 						if(nrs.isEmpty()){
5286 							dur2=0;
5287 							break;
5288 						}
5289 						else{
5290 							nrrsl.append(nrs);
5291 						}
5292 					}
5293 					if(dur2==act->duration){
5294 						assert(rrsl.count()==nrrsl.count());
5295 						if(acceptedRoomsSet.count()<rrsl.count())
5296 							dur2=0;
5297 					}
5298 					if(dur2==act->duration){
5299 						assert(rrsl.count()==nrrsl.count());
5300 
5301 						//Old comment (but some parts of it remain true):
5302 						//Apply a maximum bipartite matching (Hopcroft-Karp) on the randomized bipartite graph, to get a real room from each set.
5303 						//We have a solution if the maximum bipartite matching value == nrrsl.count() (the number of sets).
5304 						//The graph: on the left we have the rooms, on the right we have the sets.
5305 						//There is a connection from a room to a set if this room belongs to this set.
5306 						//The randomization makes the order of the left side vertices random; also their adjacency lists
5307 						//to the nodes on the right will be randomly ordered.
5308 						//This random order of the nodes on the left and of the adjancencies will be considered when running the Hopcroft-Karp
5309 						//maximum bipartite matching algorithm, hoping that it will return a somewhat random maximum bipartite matching out of the
5310 						//multitude of possible solutions (a hopefully random set of chosen rooms, one room from each set, the rooms being different).
5311 						//I could not find a proof that this should work perfectly/acceptably, but practical tests showed a good behavior.
5312 
5313 						//New comment (in addition to the old one above, parts of which remain correct): we find a maximum bipartite matching
5314 						//so that the preferred rooms are those with lowest conflicts. This is possible in O(VE) with depth first search.
5315 
5316 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
5317 						acceptedRoomsList=QList<int>(acceptedRoomsSet.constBegin(), acceptedRoomsSet.constEnd());
5318 #else
5319 						acceptedRoomsList=acceptedRoomsSet.toList();
5320 #endif
5321 						nRealRooms=acceptedRoomsList.count();
5322 						nSets=nrrsl.count();
5323 						NIL_NODE=nRealRooms+nSets;
5324 
5325 						std::stable_sort(acceptedRoomsList.begin(), acceptedRoomsList.end()); //To keep the generation identical for the same random seed.
5326 
5327 						for(int i=0; i<nRealRooms; i++){
5328 							nConflictingActivitiesBipartiteMatching[i]=nConflictingActivitiesHash.value(acceptedRoomsList.at(i), -1);
5329 							assert(nConflictingActivitiesBipartiteMatching[i]>=0);
5330 
5331 							if(level==0){
5332 								assert(conflictingActivitiesHash.contains(acceptedRoomsList.at(i)));
5333 								conflictingActivitiesBipartiteMatching[i]=conflictingActivitiesHash.value(acceptedRoomsList.at(i), QSet<int>());
5334 							}
5335 						}
5336 
5337 						//semi-randomize the order of the rooms
5338 						QList<int> tl;
5339 						for(int i=0; i<nRealRooms; i++)
5340 							tl.append(i);
5341 						for(int i=0; i<nRealRooms; i++){
5342 							int t=tl.at(i);
5343 							int r=rng.intMRG32k3a(nRealRooms-i);
5344 							tl[i]=tl[i+r];
5345 							tl[i+r]=t;
5346 						}
5347 						if(level>0){
5348 							std::stable_sort(tl.begin(), tl.end(), [this](int a, int b){return compareConflictsIncreasing(a, b);});
5349 						}
5350 						else{
5351 							assert(level==0);
5352 							tmpGlobalSolutionCompareLevel0=&c;
5353 							std::stable_sort(tl.begin(), tl.end(), [this](int a, int b){return compareConflictsIncreasingAtLevel0(a, b);});
5354 						}
5355 						for(int i=0; i<nRealRooms; i++)
5356 							semiRandomPermutation[i]=tl.at(i);
5357 
5358 						QHash<int, int> realRoomsHash;
5359 
5360 						int q=0;
5361 						for(int t : qAsConst(acceptedRoomsList)){
5362 							realRoomsHash.insert(t, q);
5363 							adj[q].clear();
5364 							q++;
5365 						}
5366 
5367 						q=0;
5368 						for(const QList<int>& tl2 : qAsConst(nrrsl)){
5369 							for(int rr : qAsConst(tl2)){
5370 								int tr=realRoomsHash.value(rr, -1);
5371 								assert(tr>=0);
5372 
5373 								//now add adjacency from tr (on the left, in U) to q (on the right, in V), as integers
5374 								adj[tr].append(q+nRealRooms);
5375 							}
5376 							q++;
5377 						}
5378 
5379 						//randomize the adjancency of the rooms - this is not a critical step
5380 						//(the chosen real rooms will be the same, only the order of the repartition of these real rooms
5381 						//to the sets might change).
5382 						for(int i=0; i<nRealRooms; i++){
5383 							for(int j=0; j<adj[i].count()-1; j++)
5384 								assert(adj[i].at(j)<adj[i].at(j+1));
5385 
5386 							for(int j=0; j<adj[i].count(); j++){
5387 								int t=adj[i].at(j);
5388 								int r=rng.intMRG32k3a(adj[i].count()-j);
5389 								adj[i][j]=adj[i][j+r];
5390 								adj[i][j+r]=t;
5391 							}
5392 						}
5393 
5394 						//globalLevel=level;
5395 						int m=maximumBipartiteMatching();
5396 						if(m!=nrrsl.count()){
5397 							assert(m<nrrsl.count());
5398 							dur2=0;
5399 						}
5400 					}
5401 					if(dur2==act->duration){
5402 						for(int i=nRealRooms; i<nRealRooms+nSets; i++){
5403 							//int s=i-nRealRooms;
5404 							int rr=acceptedRoomsList.at(pairNode[i]);
5405 
5406 							//for set s we selected real room rr.
5407 							int dur4;
5408 							for(dur4=0; dur4<act->duration; dur4++){
5409 								int ai2=roomsTimetable(rr,d,h+dur4);
5410 								if(ai2>=0){
5411 									if(!globalConflActivities.contains(ai2)){
5412 										if(swappedActivities[ai2] || (fixedTimeActivity[ai2] && fixedSpaceActivity[ai2])){
5413 											assert(0);
5414 											break;
5415 										}
5416 										else{
5417 											if(!tmp_list.contains(ai2)){
5418 												tmp_list.append(ai2);
5419 											}
5420 										}
5421 									}
5422 								}
5423 							}
5424 							assert(dur4==act->duration);
5425 
5426 							QList<int> tl=realRoomsHash.value(rm, QList<int>());
5427 							tl.append(rr);
5428 							realRoomsHash.insert(rm, tl);
5429 						}
5430 					}
5431 				}
5432 			}
5433 
5434 			if(dur2==act->duration){
5435 				bool ok=true;
5436 
5437 				if(rm!=UNALLOCATED_SPACE && rm!=UNSPECIFIED_ROOM){
5438 					//2019-11-14
5439 					//check the room changes for the students and for the teachers
5440 
5441 					//room changes for students
5442 					for(int sbg : qAsConst(act->iSubgroupsList)){
5443 						if(minGapsBetweenRoomChangesForStudentsPercentages[sbg]>=0 || maxRoomChangesPerDayForStudentsPercentages[sbg]>=0
5444 						  || maxRoomChangesPerWeekForStudentsPercentages[sbg]>=0){
5445 							ok=checkRoomChanges(sbg, -1, globalConflActivities, rm, level, act, ai, d, h, tmp_list);
5446 							if(!ok)
5447 								break;
5448 						}
5449 					}
5450 
5451 					if(!ok)
5452 						continue;
5453 
5454 					//room changes for teachers
5455 					for(int tch : qAsConst(act->iTeachersList)){
5456 						if(minGapsBetweenRoomChangesForTeachersPercentages[tch]>=0 || maxRoomChangesPerDayForTeachersPercentages[tch]>=0
5457 						  || maxRoomChangesPerWeekForTeachersPercentages[tch]>=0){
5458 							ok=checkRoomChanges(-1, tch, globalConflActivities, rm, level, act, ai, d, h, tmp_list);
5459 							if(!ok)
5460 								break;
5461 						}
5462 					}
5463 
5464 					if(!ok)
5465 						continue;
5466 
5467 					if(gt.rules.mode==MORNINGS_AFTERNOONS){
5468 						//room changes per day for students
5469 						for(int sbg : qAsConst(act->iSubgroupsList)){
5470 							if(maxRoomChangesPerRealDayForSubgroupsPercentages[sbg]>=0){
5471 								ok=checkRoomChangesPerRealDay(sbg, -1, globalConflActivities, rm, level, act, ai, d, h, tmp_list);
5472 								if(!ok)
5473 									break;
5474 							}
5475 						}
5476 
5477 						if(!ok)
5478 							continue;
5479 
5480 						//room changes per real for teachers
5481 						for(int tch : qAsConst(act->iTeachersList)){
5482 							if(maxRoomChangesPerRealDayForTeachersPercentages[tch]>=0){
5483 								ok=checkRoomChangesPerRealDay(-1, tch, globalConflActivities, rm, level, act, ai, d, h, tmp_list);
5484 								if(!ok)
5485 									break;
5486 							}
5487 						}
5488 
5489 						if(!ok)
5490 							continue;
5491 					}
5492 
5493 					assert(rm!=UNALLOCATED_SPACE && rm!=UNSPECIFIED_ROOM);
5494 					if(gt.rules.internalRoomsList[rm]->buildingIndex!=-1){
5495 						//check the building changes for the students and for the teachers
5496 
5497 						//building changes for students
5498 						for(int sbg : qAsConst(act->iSubgroupsList)){
5499 							if(minGapsBetweenBuildingChangesForStudentsPercentages[sbg]>=0 || maxBuildingChangesPerDayForStudentsPercentages[sbg]>=0
5500 							  || maxBuildingChangesPerWeekForStudentsPercentages[sbg]>=0){
5501 								ok=checkBuildingChanges(sbg, -1, globalConflActivities, rm, level, act, ai, d, h, tmp_list);
5502 								if(!ok)
5503 									break;
5504 							}
5505 						}
5506 
5507 						if(!ok)
5508 							continue;
5509 
5510 						//building changes for teachers
5511 						for(int tch : qAsConst(act->iTeachersList)){
5512 							if(minGapsBetweenBuildingChangesForTeachersPercentages[tch]>=0 || maxBuildingChangesPerDayForTeachersPercentages[tch]>=0
5513 							  || maxBuildingChangesPerWeekForTeachersPercentages[tch]>=0){
5514 								ok=checkBuildingChanges(-1, tch, globalConflActivities, rm, level, act, ai, d, h, tmp_list);
5515 								if(!ok)
5516 									break;
5517 							}
5518 						}
5519 
5520 						if(!ok)
5521 							continue;
5522 					}
5523 				}
5524 
5525 				//max occupied rooms for a set of activities - 2012-04-29
5526 				if(aomdrListForActivity[ai].count()>0)
5527 					ok=checkActivitiesOccupyMaxDifferentRooms(globalConflActivities, rm, level, ai, tmp_list);
5528 
5529 				if(!ok)
5530 					continue;
5531 
5532 				//activities same room if consecutive - 2013-09-14
5533 				if(asricListForActivity[ai].count()>0)
5534 					ok=checkActivitiesSameRoomIfConsecutive(globalConflActivities, rm, ai, d, h, tmp_list);
5535 
5536 				if(!ok)
5537 					continue;
5538 
5539 				tmp_minWrong=INF;
5540 				tmp_nWrong=0;
5541 
5542 				tmp_minIndexAct=gt.rules.nInternalActivities;
5543 
5544 				tmp_n_confl_acts=tmp_list.count();
5545 
5546 				if(level==0){
5547 					if(tmp_list.count()>0){ //serious bug corrected on 2012-05-02, but it seems that it didn't affect the users until now
5548 						for(int ai2 : qAsConst(tmp_list)){
5549 							tmp_minWrong=min(tmp_minWrong, triedRemovals(ai2,c.times[ai2]));
5550 							tmp_nWrong+=triedRemovals(ai2,c.times[ai2]);
5551 							tmp_minIndexAct=min(tmp_minIndexAct, invPermutation[ai2]);
5552 						}
5553 					}
5554 					else{
5555 						tmp_minWrong=0;
5556 						tmp_nWrong=0;
5557 						tmp_minIndexAct=-1;
5558 					}
5559 				}
5560 
5561 				listedRooms.append(rm);
5562 				nConflActivitiesRooms.append(tmp_n_confl_acts);
5563 				conflActivitiesRooms.append(tmp_list);
5564 
5565 				if(level>0){
5566 					if(tmp_n_confl_acts<optConflActivities)
5567 						optConflActivities=tmp_n_confl_acts;
5568 				}
5569 				else{ // if(level==0)
5570 					minWrong.append(tmp_minWrong);
5571 					nWrong.append(tmp_nWrong);
5572 					minIndexAct.append(tmp_minIndexAct);
5573 
5574 					if(optMinWrong>tmp_minWrong ||
5575 					  (optMinWrong==tmp_minWrong && optNWrong>tmp_nWrong) ||
5576 					  (optMinWrong==tmp_minWrong && optNWrong==tmp_nWrong && optConflActivities>tmp_n_confl_acts) ||
5577 					  (optMinWrong==tmp_minWrong && optNWrong==tmp_nWrong && optConflActivities==tmp_n_confl_acts && optMinIndexAct>tmp_minIndexAct)){
5578 						optConflActivities=tmp_n_confl_acts;
5579 						optMinWrong=tmp_minWrong;
5580 						optNWrong=tmp_nWrong;
5581 						optMinIndexAct=tmp_minIndexAct;
5582 					}
5583 				}
5584 			}
5585 		}
5586 		else //not really needed
5587 			tmp_n_confl_acts=MAX_ACTIVITIES;
5588 	}
5589 
5590 	if(optConflActivities==MAX_ACTIVITIES) //roomSlot == selectedSlot == UNSPECIFIED_ROOM
5591 		return false;
5592 
5593 	assert(optConflActivities<MAX_ACTIVITIES);
5594 
5595 	QList<int> allowedRoomsIndex;
5596 
5597 	assert(listedRooms.count()==nConflActivitiesRooms.count());
5598 	assert(listedRooms.count()==conflActivitiesRooms.count());
5599 
5600 	if(level>0){
5601 		for(int q=0; q<listedRooms.count(); q++){
5602 			//The 'mapr' custom version disables the test below and always does an .append(q). But this
5603 			//gives a very bad behavior for the file examples/Egypt/Dakahlia/institution-2012-2013.fet
5604 			//(usually, the file seems not to solve anymore, and one time it solved very slowly).
5605 			//Update 2016-08-26: Also mapr for zt3's input is working much better with a code _with_ the test below
5606 			//(which does not always .append(q) ).
5607 			if(nConflActivitiesRooms.at(q)==optConflActivities){
5608 				allowedRoomsIndex.append(q);
5609 			}
5610 		}
5611 	}
5612 	else{
5613 		for(int q=0; q<listedRooms.count(); q++){
5614 			if(optMinWrong==minWrong.at(q) && optNWrong==nWrong.at(q) && optConflActivities==nConflActivitiesRooms.at(q) && optMinIndexAct==minIndexAct.at(q)){
5615 				allowedRoomsIndex.append(q);
5616 			}
5617 		}
5618 		/*if(allowedRoomsIndex.count()!=1)
5619 			cout<<"allowedRoomsIndex.count()=="<<allowedRoomsIndex.count()<<endl;
5620 		assert(allowedRoomsIndex.count()==1);*/
5621 	}
5622 
5623 	assert(allowedRoomsIndex.count()>0);
5624 	int q=rng.intMRG32k3a(allowedRoomsIndex.count());
5625 	int t=allowedRoomsIndex.at(q);
5626 	assert(t>=0 && t<listedRooms.count());
5627 	int r=listedRooms.at(t);
5628 	assert(r>=0 && r<gt.rules.nInternalRooms);
5629 	selectedSlot=r;
5630 	roomSlot=r;
5631 	realRoomsList=realRoomsHash.value(r, QList<int>());
5632 
5633 	assert(nConflActivitiesRooms.at(t)==conflActivitiesRooms.at(t).count());
5634 
5635 	localConflActivities.clear(); /////Liviu: added 22 August 2008 (modified 22 May 2020): bug fix, which might have been a crash bug or a non-observable bug.
5636 									//Liviu, 19 May 2020: If I remember correctly, the explanation was: because in the function getRoom() below we might call
5637 									//the function getPreferredRoom() and after that the function getHomeRoom() without clearing the list localConflActivities
5638 									//in between the calls, more precisely if okp is true and localConflActivities.count()>0 after calling getPreferredRoom().
5639 									//22 May 2020: If localConflActivities is not emptied between the call to getPreferredRoom() and getHomeRoom(),
5640 									//we would get a crash bug (if localConflActivities would contain duplicates) or a non-observable bug (otherwise).
5641 
5642 	for(int a : qAsConst(conflActivitiesRooms.at(t))){
5643 		assert(!globalConflActivities.contains(a));
5644 		assert(!localConflActivities.contains(a)); ///////////Liviu: added 22 August 2008.
5645 		localConflActivities.append(a);
5646 	}
5647 
5648 	return true;
5649 }
5650 
getHomeRoom(const QList<int> & globalConflActivities,int level,const Activity * act,int ai,int d,int h,int & roomSlot,int & selectedSlot,QList<int> & localConflActivities,QList<int> & realRoomsList)5651 inline bool Generate::getHomeRoom(const QList<int>& globalConflActivities, int level, const Activity* act, int ai, int d, int h,
5652  int& roomSlot, int& selectedSlot, QList<int>& localConflActivities, QList<int>& realRoomsList)
5653 {
5654 	assert(!unspecifiedHomeRoom[ai]);
5655 
5656 	return chooseRoom(activitiesHomeRoomsHomeRooms[ai], globalConflActivities, level, act, ai, d, h, roomSlot, selectedSlot, localConflActivities, realRoomsList);
5657 }
5658 
getPreferredRoom(const QList<int> & globalConflActivities,int level,const Activity * act,int ai,int d,int h,int & roomSlot,int & selectedSlot,QList<int> & localConflActivities,bool & canBeUnspecifiedPreferredRoom,QList<int> & realRoomsList)5659 inline bool Generate::getPreferredRoom(const QList<int>& globalConflActivities, int level, const Activity* act, int ai, int d, int h,
5660  int& roomSlot, int& selectedSlot, QList<int>& localConflActivities, bool& canBeUnspecifiedPreferredRoom, QList<int>& realRoomsList)
5661 {
5662 	assert(!unspecifiedPreferredRoom[ai]);
5663 
5664 	bool unspecifiedRoom=true;
5665 	QSet<int> allowedRooms;
5666 	for(const PreferredRoomsItem& it : qAsConst(activitiesPreferredRoomsList[ai])){
5667 		bool skip=skipRandom(it.percentage);
5668 
5669 		if(!skip){
5670 			if(unspecifiedRoom){
5671 				unspecifiedRoom=false;
5672 				allowedRooms=it.preferredRooms;
5673 			}
5674 			else{
5675 				allowedRooms.intersect(it.preferredRooms);
5676 			}
5677 		}
5678 		else{
5679 			if(unspecifiedRoom){
5680 				allowedRooms.unite(it.preferredRooms);
5681 			}
5682 			else{
5683 				//do nothing
5684 			}
5685 		}
5686 	}
5687 
5688 	QList<int> allowedRoomsList;
5689 	for(int rm : qAsConst(allowedRooms))
5690 		allowedRoomsList.append(rm);
5691 	std::stable_sort(allowedRoomsList.begin(), allowedRoomsList.end()); //To keep the generation identical on all computers - 2013-01-03
5692 	//Randomize list
5693 	for(int i=0; i<allowedRoomsList.count(); i++){
5694 		int t=allowedRoomsList.at(i);
5695 		int r=rng.intMRG32k3a(allowedRoomsList.count()-i);
5696 		allowedRoomsList[i]=allowedRoomsList[i+r];
5697 		allowedRoomsList[i+r]=t;
5698 	}
5699 
5700 	canBeUnspecifiedPreferredRoom=unspecifiedRoom;
5701 
5702 	return chooseRoom(allowedRoomsList, globalConflActivities, level, act, ai, d, h, roomSlot, selectedSlot, localConflActivities, realRoomsList);
5703 }
5704 
getRoom(int level,const Activity * act,int ai,int d,int h,int & roomSlot,int & selectedSlot,QList<int> & conflActivities,int & nConflActivities,QList<int> & realRoomsList)5705 inline bool Generate::getRoom(int level, const Activity* act, int ai, int d, int h,
5706  int& roomSlot, int& selectedSlot, QList<int>& conflActivities, int& nConflActivities, QList<int>& realRoomsList)
5707 {
5708 	bool okh;
5709 
5710 	QList<int> localConflActivities;
5711 
5712 	if(unspecifiedPreferredRoom[ai]){
5713 		if(unspecifiedHomeRoom[ai]){
5714 			roomSlot=UNSPECIFIED_ROOM;
5715 			selectedSlot=UNSPECIFIED_ROOM;
5716 			return true;
5717 		}
5718 		else{
5719 			okh=getHomeRoom(conflActivities, level, act, ai, d, h, roomSlot, selectedSlot, localConflActivities, realRoomsList);
5720 			if(okh){
5721 				for(int t : qAsConst(localConflActivities)){
5722 					conflActivities.append(t);
5723 					nConflActivities++;
5724 				}
5725 				return okh;
5726 			}
5727 			else{
5728 				okh=skipRandom(activitiesHomeRoomsPercentage[ai]);
5729 				return okh;
5730 			}
5731 		}
5732 	}
5733 	else{
5734 		bool okp;
5735 		bool canBeUnspecifiedPreferredRoom;
5736 
5737 		okp=getPreferredRoom(conflActivities, level, act, ai, d, h, roomSlot, selectedSlot, localConflActivities, canBeUnspecifiedPreferredRoom, realRoomsList);
5738 		if(okp && localConflActivities.count()==0){
5739 			return okp;
5740 		}
5741 		else if(okp){
5742 			if(canBeUnspecifiedPreferredRoom){ //skipRandom(activitiesPreferredRoomsPercentage[ai])){
5743 				//get a home room
5744 				if(unspecifiedHomeRoom[ai]){
5745 					roomSlot=UNSPECIFIED_ROOM;
5746 					selectedSlot=UNSPECIFIED_ROOM;
5747 					return true;
5748 				}
5749 
5750 				okh=getHomeRoom(conflActivities, level, act, ai, d, h, roomSlot, selectedSlot, localConflActivities, realRoomsList);
5751 				if(okh){
5752 					for(int t : qAsConst(localConflActivities)){
5753 						conflActivities.append(t);
5754 						nConflActivities++;
5755 					}
5756 					return okh;
5757 				}
5758 				else{
5759 					okh=skipRandom(activitiesHomeRoomsPercentage[ai]);
5760 					return okh;
5761 				}
5762 			}
5763 			else{
5764 				for(int t : qAsConst(localConflActivities)){
5765 					conflActivities.append(t);
5766 					nConflActivities++;
5767 				}
5768 				assert(nConflActivities==conflActivities.count());
5769 				assert(okp==true);
5770 				return okp;
5771 				//get this preferred room
5772 			}
5773 		}
5774 		else{ //!ok from preferred room, search a home room
5775 			if(canBeUnspecifiedPreferredRoom){ //skipRandom(activitiesPreferredRoomsPercentage[ai])){
5776 				//get a home room
5777 				if(unspecifiedHomeRoom[ai]){
5778 					roomSlot=UNSPECIFIED_ROOM;
5779 					selectedSlot=UNSPECIFIED_ROOM;
5780 					return true;
5781 				}
5782 
5783 				okh=getHomeRoom(conflActivities, level, act, ai, d, h, roomSlot, selectedSlot, localConflActivities, realRoomsList);
5784 				if(okh){
5785 					for(int t : qAsConst(localConflActivities)){
5786 						conflActivities.append(t);
5787 						nConflActivities++;
5788 					}
5789 					return okh;
5790 				}
5791 				else{
5792 					okh=skipRandom(activitiesHomeRoomsPercentage[ai]);
5793 					return okh;
5794 				}
5795 			}
5796 			else{
5797 				assert(okp==false);
5798 				return okp;
5799 			}
5800 		}
5801 	}
5802 }
5803 
generate(int maxSeconds,bool & impossible,bool & timeExceeded,bool threaded,QTextStream * maxPlacedActivityStream)5804 void Generate::generate(int maxSeconds, bool& impossible, bool& timeExceeded, bool threaded, QTextStream* maxPlacedActivityStream)
5805 {
5806 	permutation.resize(gt.rules.nInternalActivities);
5807 	for(int i=0; i<gt.rules.nInternalActivities; i++)
5808 		permutation[i]=copyOfInitialPermutation[i];
5809 
5810 	this->isThreaded=threaded;
5811 
5812 	//2019-09-14 - begin for the maximum bipartite matching algorithm
5813 	int j=0; //the number of real rooms
5814 	int k=0; //the maximum number of sets for a virtual room
5815 	for(int i=0; i<gt.rules.nInternalRooms; i++){
5816 		if(gt.rules.internalRoomsList[i]->isVirtual==false){
5817 			j++;
5818 		}
5819 		else{
5820 			if(k<gt.rules.internalRoomsList[i]->rrsl.count()){
5821 				k=gt.rules.internalRoomsList[i]->rrsl.count();
5822 			}
5823 		}
5824 	}
5825 	if(k>0)
5826 		assert(j>0);
5827 	if(j<gt.rules.nInternalRooms)
5828 		assert(k>0);
5829 	if(j>0 && k>0){
5830 		adj.resize(j);
5831 		visited.resize(j);
5832 		//dist.resize(j+k+1); //+1 for the NIL_NODE, +k even if dist for them is not used, but so that dist[NIL_NODE] is accessible, since NIL_NODE can be maximum j+k.
5833 		semiRandomPermutation.resize(j);
5834 		pairNode.resize(j+k); //be careful: even if we would get rid of the assignment and checking of pairNode[u], we need to keep this array's size = j+k
5835 		nConflictingActivitiesBipartiteMatching.resize(j);
5836 		conflictingActivitiesBipartiteMatching.resize(j);
5837 	}
5838 	//end - for the maximum bipartite matching algorithm
5839 	restoreRealRoomsList.resize(2*gt.rules.nInternalActivities);
5840 
5841 	//2021-03-17
5842 	difficultActivities.resize(gt.rules.nInternalActivities);
5843 	//
5844 	buildings.resize(gt.rules.nHoursPerDay);
5845 	activities.resize(gt.rules.nHoursPerDay);
5846 	rooms.resize(gt.rules.nHoursPerDay);
5847 	activitiesx2.resize(2*gt.rules.nHoursPerDay);
5848 	roomsx2.resize(2*gt.rules.nHoursPerDay);
5849 	weekBuildings.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5850 	weekActivities.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5851 	weekRooms.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5852 	aminoCnt.resize(gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
5853 	aminsCnt.resize(gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
5854 	possibleToEmptySlot.resize(gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
5855 	//
5856 	occupiedDay.resize(gt.rules.nDaysPerWeek);
5857 	canEmptyDay.resize(gt.rules.nDaysPerWeek);
5858 	_nConflActivities.resize(gt.rules.nDaysPerWeek);
5859 	_activitiesForDay.resize(gt.rules.nDaysPerWeek);
5860 	_minWrong.resize(gt.rules.nDaysPerWeek);
5861 	_nWrong.resize(gt.rules.nDaysPerWeek);
5862 	_minIndexAct.resize(gt.rules.nDaysPerWeek);
5863 	occupiedIntervalDay.resize(gt.rules.nDaysPerWeek);
5864 	canEmptyIntervalDay.resize(gt.rules.nDaysPerWeek);
5865 	_activitiesForIntervalDay.resize(gt.rules.nDaysPerWeek);
5866 	sbgDayNHoursWithTag.resize(gt.rules.nDaysPerWeek);
5867 	possibleToEmptyDay.resize(gt.rules.nDaysPerWeek);
5868 	tchDayNHoursWithTag.resize(gt.rules.nDaysPerWeek);
5869 	//
5870 	assert(gt.rules.nTerms>=1);
5871 	assert(gt.rules.nTerms<=MAX_DAYS_PER_WEEK);
5872 	canEmptyTerm.resize(gt.rules.nTerms);
5873 	termActivities.resize(gt.rules.nTerms);
5874 	//
5875 	swappedActivities.resize(gt.rules.nInternalActivities);
5876 	restoreActIndex.resize(2*gt.rules.nInternalActivities);
5877 	restoreTime.resize(2*gt.rules.nInternalActivities);
5878 	restoreRoom.resize(2*gt.rules.nInternalActivities);
5879 	restoreRealRoomsList.resize(2*gt.rules.nInternalActivities);
5880 	invPermutation.resize(gt.rules.nInternalActivities);
5881 	//
5882 	nMinDaysBrokenL.resize(MAX_LEVEL, gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
5883 	selectedRoomL.resize(MAX_LEVEL, gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
5884 	permL.resize(MAX_LEVEL, gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
5885 	conflActivitiesL.resize(MAX_LEVEL, gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
5886 	nConflActivitiesL.resize(MAX_LEVEL, gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
5887 	roomSlotsL.resize(MAX_LEVEL, gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
5888 	realRoomsListL.resize(MAX_LEVEL, gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
5889 	//
5890 	slotActivity.resize(gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
5891 	////////////
5892 
5893 	l0nWrong.resize(gt.rules.nHoursPerWeek);
5894 	l0minWrong.resize(gt.rules.nHoursPerWeek);
5895 	l0minIndexAct.resize(gt.rules.nHoursPerWeek);
5896 
5897 	teachersTimetable.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5898 	subgroupsTimetable.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5899 	roomsTimetable.resize(gt.rules.nInternalRooms, gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5900 
5901 	newTeachersTimetable.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5902 	newSubgroupsTimetable.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5903 	newTeachersDayNHours.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek);
5904 	newTeachersDayNGaps.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek);
5905 	newTeachersDayNFirstGaps.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek);
5906 	newSubgroupsDayNHours.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek);
5907 	newSubgroupsDayNGaps.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek);
5908 	newSubgroupsDayNFirstGaps.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek);
5909 
5910 	newTeachersRealDayNHours.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek/2);
5911 	newTeachersRealDayNGaps.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek/2);
5912 
5913 	newSubgroupsRealDayNHours.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek/2);
5914 	newSubgroupsRealDayNGaps.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek/2);
5915 	newSubgroupsRealDayNFirstGaps.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek/2);
5916 
5917 	oldTeachersTimetable.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5918 	oldSubgroupsTimetable.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5919 	oldTeachersDayNHours.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek);
5920 	oldTeachersDayNGaps.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek);
5921 	oldTeachersDayNFirstGaps.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek);
5922 	oldSubgroupsDayNHours.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek);
5923 	oldSubgroupsDayNGaps.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek);
5924 	oldSubgroupsDayNFirstGaps.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek);
5925 
5926 	oldTeachersRealDayNHours.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek/2);
5927 	oldTeachersRealDayNGaps.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek/2);
5928 
5929 	oldSubgroupsRealDayNHours.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek/2);
5930 	oldSubgroupsRealDayNGaps.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek/2);
5931 	oldSubgroupsRealDayNFirstGaps.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek/2);
5932 
5933 	tchTimetable.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5934 	tchDayNHours.resize(gt.rules.nDaysPerWeek);
5935 	tchDayNGaps.resize(gt.rules.nDaysPerWeek);
5936 	tchDayNFirstGaps.resize(gt.rules.nDaysPerWeek);
5937 
5938 	tchRealDayNHours.resize(gt.rules.nDaysPerWeek/2);
5939 	tchRealDayNGaps.resize(gt.rules.nDaysPerWeek/2);
5940 
5941 	sbgTimetable.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5942 	sbgDayNHours.resize(gt.rules.nDaysPerWeek);
5943 	sbgDayNGaps.resize(gt.rules.nDaysPerWeek);
5944 	sbgDayNFirstGaps.resize(gt.rules.nDaysPerWeek);
5945 
5946 	sbgRealDayNHours.resize(gt.rules.nDaysPerWeek/2);
5947 	sbgRealDayNGaps.resize(gt.rules.nDaysPerWeek/2);
5948 	sbgRealDayNFirstGaps.resize(gt.rules.nDaysPerWeek/2);
5949 
5950 	teacherActivitiesOfTheDay.resize(gt.rules.nInternalTeachers, gt.rules.nDaysPerWeek);
5951 	subgroupActivitiesOfTheDay.resize(gt.rules.nInternalSubgroups, gt.rules.nDaysPerWeek);
5952 
5953 	//2011-09-30
5954 	if(haveActivitiesOccupyMaxConstraints || haveActivitiesMaxSimultaneousConstraints){
5955 		activitiesAtTime.resize(gt.rules.nHoursPerWeek);
5956 
5957 		slotSetOfActivities.resize(gt.rules.nHoursPerWeek);
5958 		slotCanEmpty.resize(gt.rules.nHoursPerWeek);
5959 	}
5960 
5961 	if(threaded){
5962 		myMutex.lock();
5963 	}
5964 	isRunning=true;
5965 	c.makeUnallocated(gt.rules);
5966 
5967 	nDifficultActivities=0;
5968 
5969 	impossible=false;
5970 	timeExceeded=false;
5971 
5972 	impossibleActivity=false;
5973 
5974 	maxActivitiesPlaced=0;
5975 
5976 	if(threaded){
5977 		myMutex.unlock();
5978 	}
5979 
5980 	triedRemovals.resize(gt.rules.nInternalActivities, gt.rules.nHoursPerWeek);
5981 	for(int i=0; i<gt.rules.nInternalActivities; i++)
5982 		for(int j=0; j<gt.rules.nHoursPerWeek; j++)
5983 			triedRemovals(i,j)=0;
5984 
5985 	////////init tabu
5986 	tabu_size=gt.rules.nInternalActivities*gt.rules.nHoursPerWeek;
5987 	//assert(tabu_size<=MAX_TABU);
5988 	crt_tabu_index=0;
5989 	/*qint16 tabu_activities[MAX_TABU];
5990 	qint16 tabu_times[MAX_TABU];*/
5991 	tabu_activities.resize(tabu_size);
5992 	tabu_times.resize(tabu_size);
5993 	for(int i=0; i<tabu_size; i++)
5994 		tabu_activities[i]=tabu_times[i]=-1;
5995 	/////////////////
5996 
5997 	//abortOptimization=false; you have to take care of this before calling this function
5998 
5999 	for(int i=0; i<gt.rules.nInternalActivities; i++)
6000 		invPermutation[permutation[i]]=i;
6001 
6002 	for(int i=0; i<gt.rules.nInternalActivities; i++)
6003 		swappedActivities[permutation[i]]=false;
6004 
6005 	time_t starting_time;
6006 	time(&starting_time);
6007 
6008 	if(threaded){
6009 		myMutex.lock();
6010 	}
6011 	timeToHighestStage=0;
6012 	searchTime=0;
6013 	if(threaded){
6014 		myMutex.unlock();
6015 	}
6016 
6017 	limitcallsrandomswap=2*gt.rules.nInternalActivities; //Value found practically
6018 
6019 	level_limit=14;
6020 
6021 	assert(level_limit<=MAX_LEVEL);
6022 
6023 	for(int added_act=0; added_act<gt.rules.nInternalActivities; added_act++){
6024 		prevvalue:
6025 
6026 		if(threaded){
6027 			myMutex.lock();
6028 		}
6029 		if(abortOptimization){
6030 			isRunning=false;
6031 
6032 			if(threaded){
6033 				myMutex.unlock();
6034 			}
6035 			return;
6036 		}
6037 		time_t crt_time;
6038 		time(&crt_time);
6039 		searchTime=int(difftime(crt_time, starting_time));
6040 
6041 		if(searchTime>=maxSeconds){
6042 			isRunning=false;
6043 
6044 			timeExceeded=true;
6045 
6046 			if(threaded){
6047 				myMutex.unlock();
6048 			}
6049 
6050 			return;
6051 		}
6052 
6053 		for(int i=0; i<=added_act; i++)
6054 			swappedActivities[permutation[i]]=false;
6055 		for(int i=added_act+1; i<gt.rules.nInternalActivities; i++)
6056 			assert(!swappedActivities[permutation[i]]);
6057 
6058 		if(VERBOSE){
6059 			cout<<endl<<"Trying to place activity number added_act=="<<added_act<<
6060 			 "\nwith id=="<<gt.rules.internalActivitiesList[permutation[added_act]].id<<
6061 			 ", from nInternalActivities=="<<gt.rules.nInternalActivities<<endl;
6062 		}
6063 		//verifyUnallocated(permutation[added_act]]);
6064 		//assert(c.times[permutation[added_act]]==UNALLOCATED_TIME);
6065 		//assert(c.rooms[permutation[added_act]]==UNALLOCATED_SPACE);
6066 		if(fixedTimeActivity[permutation[added_act]] && fixedSpaceActivity[permutation[added_act]]){
6067 			assert(c.times[permutation[added_act]]==UNALLOCATED_TIME);
6068 			assert(c.rooms[permutation[added_act]]==UNALLOCATED_SPACE);
6069 		}
6070 		else if(fixedTimeActivity[permutation[added_act]] && !fixedSpaceActivity[permutation[added_act]]){
6071 			assert(c.rooms[permutation[added_act]]==UNALLOCATED_SPACE);
6072 		}
6073 		else if(!fixedTimeActivity[permutation[added_act]]){
6074 			assert(c.times[permutation[added_act]]==UNALLOCATED_TIME);
6075 			assert(c.rooms[permutation[added_act]]==UNALLOCATED_SPACE);
6076 		}
6077 		else
6078 			assert(0);
6079 
6080 		for(int i=0; i<added_act; i++){
6081 			if(c.times[permutation[i]]==UNALLOCATED_TIME)
6082 				cout<<"ERROR: act with id=="<<gt.rules.internalActivitiesList[permutation[i]].id<<" has time unallocated"<<endl;
6083 			assert(c.times[permutation[i]]!=UNALLOCATED_TIME);
6084 			/*for(int j=0; j<gt.rules.internalActivitiesList[permutation[i]].duration; j++)
6085 				tlistSet[c.times[permutation[i]]+j*gt.rules.nDaysPerWeek].insert(permutation[i]);*/
6086 
6087 			if(c.rooms[permutation[i]]==UNALLOCATED_SPACE)
6088 				cout<<"ERROR: act with id=="<<gt.rules.internalActivitiesList[permutation[i]].id<<" has room unallocated"<<endl;
6089 			assert(c.rooms[permutation[i]]!=UNALLOCATED_SPACE);
6090 		}
6091 
6092 		///////////////rooms' timetable
6093 		for(int i=0; i<gt.rules.nInternalRooms; i++)
6094 			for(int j=0; j<gt.rules.nDaysPerWeek; j++)
6095 				for(int k=0; k<gt.rules.nHoursPerDay; k++)
6096 					roomsTimetable(i,j,k)=-1;
6097 		for(int j=0; j<added_act; j++){
6098 			int i=permutation[j];
6099 			assert(c.rooms[i]!=UNALLOCATED_SPACE);
6100 			if(c.rooms[i]!=UNSPECIFIED_ROOM){
6101 				int rm=c.rooms[i];
6102 
6103 				Activity* act=&gt.rules.internalActivitiesList[i];
6104 				int hour=c.times[i]/gt.rules.nDaysPerWeek;
6105 				int day=c.times[i]%gt.rules.nDaysPerWeek;
6106 				for(int dd=0; dd<act->duration && hour+dd<gt.rules.nHoursPerDay; dd++){
6107 					assert(roomsTimetable(rm,day,hour+dd)==-1);
6108 					if(gt.rules.internalRoomsList[rm]->isVirtual==false)
6109 						roomsTimetable(rm,day,hour+dd)=i;
6110 				}
6111 
6112 				if(gt.rules.internalRoomsList[rm]->isVirtual){
6113 					assert(gt.rules.internalRoomsList[rm]->rrsl.count()==c.realRoomsList[i].count());
6114 
6115 					int k=0;
6116 					for(int rr : qAsConst(c.realRoomsList[i])){
6117 						assert(gt.rules.internalRoomsList[rm]->rrsl.at(k).contains(rr));
6118 
6119 						for(int dd=0; dd<act->duration; dd++){
6120 							assert(hour+dd<gt.rules.nHoursPerDay);
6121 
6122 							assert(rr!=UNALLOCATED_SPACE);
6123 							assert(roomsTimetable(rr,day,hour+dd)==-1);
6124 							roomsTimetable(rr,day,hour+dd)=i;
6125 						}
6126 
6127 						k++;
6128 					}
6129 				}
6130 			}
6131 		}
6132 		///////////////////////////////
6133 
6134 		//subgroups' timetable
6135 		for(int i=0; i<gt.rules.nInternalSubgroups; i++)
6136 			for(int j=0; j<gt.rules.nDaysPerWeek; j++)
6137 				for(int k=0; k<gt.rules.nHoursPerDay; k++){
6138 					subgroupsTimetable(i,j,k)=-1;
6139 				}
6140 		for(int j=0; j<gt.rules.nInternalActivities/*added_act*/; j++){
6141 			int i=permutation[j];
6142 			if(j<added_act){
6143 				assert(c.times[i]!=UNALLOCATED_TIME);
6144 			}
6145 			else{
6146 				if(c.times[i]==UNALLOCATED_TIME)
6147 					continue;
6148 			}
6149 			assert(c.times[i]!=UNALLOCATED_TIME);
6150 			Activity* act=&gt.rules.internalActivitiesList[i];
6151 			int hour=c.times[i]/gt.rules.nDaysPerWeek;
6152 			int day=c.times[i]%gt.rules.nDaysPerWeek;
6153 			for(int sb : qAsConst(act->iSubgroupsList)){
6154 				for(int dd=0; dd<act->duration && hour+dd<gt.rules.nHoursPerDay; dd++){
6155 					assert(subgroupsTimetable(sb,day,hour+dd)==-1);
6156 					subgroupsTimetable(sb,day,hour+dd)=i;
6157 				}
6158 			}
6159 		}
6160 
6161 		//new
6162 		for(int i=0; i<gt.rules.nInternalSubgroups; i++)
6163 			for(int j=0; j<gt.rules.nDaysPerWeek; j++)
6164 				for(int k=0; k<gt.rules.nHoursPerDay; k++){
6165 					newSubgroupsTimetable(i,j,k)=-1;
6166 				}
6167 		for(int j=0; j<gt.rules.nInternalActivities/*added_act*/; j++){
6168 			int i=permutation[j];
6169 			if(j<added_act){
6170 				assert(c.times[i]!=UNALLOCATED_TIME);
6171 			}
6172 			else{
6173 				if(c.times[i]==UNALLOCATED_TIME)
6174 					continue;
6175 			}
6176 			assert(c.times[i]!=UNALLOCATED_TIME);
6177 			Activity* act=&gt.rules.internalActivitiesList[i];
6178 			int hour=c.times[i]/gt.rules.nDaysPerWeek;
6179 			int day=c.times[i]%gt.rules.nDaysPerWeek;
6180 			for(int sb : qAsConst(act->iSubgroupsList)){
6181 				for(int dd=0; dd<act->duration && hour+dd<gt.rules.nHoursPerDay; dd++){
6182 					assert(newSubgroupsTimetable(sb,day,hour+dd)==-1);
6183 					newSubgroupsTimetable(sb,day,hour+dd)=i;
6184 				}
6185 			}
6186 		}
6187 
6188 		for(int i=0; i<gt.rules.nInternalSubgroups; i++)
6189 			subgroupGetNHoursGaps(i);
6190 
6191 		//////////////care for students max days/mornings/afternoons per week
6192 		for(int i=0; i<gt.rules.nInternalSubgroups; i++)
6193 			for(int j=0; j<gt.rules.nDaysPerWeek; j++)
6194 				subgroupActivitiesOfTheDay(i,j).clear();
6195 
6196 		for(int i=0; i<gt.rules.nInternalActivities/*added_act*/; i++){
6197 			if(i<added_act){
6198 			}
6199 			else{
6200 				if(c.times[permutation[i]]==UNALLOCATED_TIME)
6201 					continue;
6202 			}
6203 			//Activity* act=&gt.rules.internalActivitiesList[permutation[i]];
6204 			int d=c.times[permutation[i]]%gt.rules.nDaysPerWeek;
6205 
6206 			if(gt.rules.mode!=MORNINGS_AFTERNOONS){
6207 				for(int j : qAsConst(subgroupsWithMaxDaysPerWeekForActivities[permutation[i]])){
6208 					assert(subgroupActivitiesOfTheDay(j,d).indexOf(permutation[i])==-1);
6209 					subgroupActivitiesOfTheDay(j,d).append(permutation[i]);
6210 				}
6211 			}
6212 			else{
6213 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
6214 				QSet<int> st_smhd=QSet<int>(subgroupsWithMaxDaysPerWeekForActivities[permutation[i]].constBegin(), subgroupsWithMaxDaysPerWeekForActivities[permutation[i]].constEnd());
6215 				QSet<int> st_smd=QSet<int>(subgroupsWithMaxRealDaysPerWeekForActivities[permutation[i]].constBegin(), subgroupsWithMaxRealDaysPerWeekForActivities[permutation[i]].constEnd());
6216 				QSet<int> st_sma=QSet<int>(subgroupsWithMaxAfternoonsPerWeekForActivities[permutation[i]].constBegin(), subgroupsWithMaxAfternoonsPerWeekForActivities[permutation[i]].constEnd());
6217 				QSet<int> st_smm=QSet<int>(subgroupsWithMaxMorningsPerWeekForActivities[permutation[i]].constBegin(), subgroupsWithMaxMorningsPerWeekForActivities[permutation[i]].constEnd());
6218 #else
6219 				QSet<int> st_smhd=subgroupsWithMaxDaysPerWeekForActivities[permutation[i]].toSet();
6220 				QSet<int> st_smd=subgroupsWithMaxRealDaysPerWeekForActivities[permutation[i]].toSet();
6221 				QSet<int> st_sma=subgroupsWithMaxAfternoonsPerWeekForActivities[permutation[i]].toSet();
6222 				QSet<int> st_smm=subgroupsWithMaxMorningsPerWeekForActivities[permutation[i]].toSet();
6223 #endif
6224 				QSet<int> st_smda=st_smhd+st_smd+st_sma;
6225 				QSet<int> st_smdm=st_smhd+st_smd+st_smm;
6226 
6227 				if(d%2==1){
6228 					for(int j : qAsConst(st_smda)){
6229 						assert(subgroupActivitiesOfTheDay(j,d).indexOf(permutation[i])==-1);
6230 						subgroupActivitiesOfTheDay(j,d).append(permutation[i]);
6231 					}
6232 				}
6233 				else{
6234 					for(int j : qAsConst(st_smdm)){
6235 						assert(subgroupActivitiesOfTheDay(j,d).indexOf(permutation[i])==-1);
6236 						subgroupActivitiesOfTheDay(j,d).append(permutation[i]);
6237 					}
6238 				}
6239 			}
6240 		}
6241 		//////////////
6242 
6243 		//teachers' timetable
6244 		for(int i=0; i<gt.rules.nInternalTeachers; i++)
6245 			for(int j=0; j<gt.rules.nDaysPerWeek; j++)
6246 				for(int k=0; k<gt.rules.nHoursPerDay; k++){
6247 					teachersTimetable(i,j,k)=-1;
6248 				}
6249 		for(int j=0; j<gt.rules.nInternalActivities/*added_act*/; j++){
6250 			int i=permutation[j];
6251 			if(j<added_act){
6252 				assert(c.times[i]!=UNALLOCATED_TIME);
6253 			}
6254 			else{
6255 				if(c.times[i]==UNALLOCATED_TIME)
6256 					continue;
6257 			}
6258 			assert(c.times[i]!=UNALLOCATED_TIME);
6259 			Activity* act=&gt.rules.internalActivitiesList[i];
6260 			int hour=c.times[i]/gt.rules.nDaysPerWeek;
6261 			int day=c.times[i]%gt.rules.nDaysPerWeek;
6262 			for(int tc : qAsConst(act->iTeachersList)){
6263 				for(int dd=0; dd<act->duration && hour+dd<gt.rules.nHoursPerDay; dd++){
6264 					assert(teachersTimetable(tc,day,hour+dd)==-1);
6265 					teachersTimetable(tc,day,hour+dd)=i;
6266 				}
6267 			}
6268 		}
6269 
6270 		//new
6271 		for(int i=0; i<gt.rules.nInternalTeachers; i++)
6272 			for(int j=0; j<gt.rules.nDaysPerWeek; j++)
6273 				for(int k=0; k<gt.rules.nHoursPerDay; k++){
6274 					newTeachersTimetable(i,j,k)=-1;
6275 				}
6276 		for(int j=0; j<gt.rules.nInternalActivities/*added_act*/; j++){
6277 			int i=permutation[j];
6278 			if(j<added_act){
6279 				assert(c.times[i]!=UNALLOCATED_TIME);
6280 			}
6281 			else{
6282 				if(c.times[i]==UNALLOCATED_TIME)
6283 					continue;
6284 			}
6285 			assert(c.times[i]!=UNALLOCATED_TIME);
6286 			Activity* act=&gt.rules.internalActivitiesList[i];
6287 			int hour=c.times[i]/gt.rules.nDaysPerWeek;
6288 			int day=c.times[i]%gt.rules.nDaysPerWeek;
6289 			for(int tc : qAsConst(act->iTeachersList)){
6290 				for(int dd=0; dd<act->duration && hour+dd<gt.rules.nHoursPerDay; dd++){
6291 					assert(newTeachersTimetable(tc,day,hour+dd)==-1);
6292 					newTeachersTimetable(tc,day,hour+dd)=i;
6293 				}
6294 			}
6295 		}
6296 
6297 		for(int i=0; i<gt.rules.nInternalTeachers; i++)
6298 			teacherGetNHoursGaps(i);
6299 
6300 		//////////////care for teachers max days/afternoons/mornings per week
6301 		for(int i=0; i<gt.rules.nInternalTeachers; i++)
6302 			for(int j=0; j<gt.rules.nDaysPerWeek; j++)
6303 				teacherActivitiesOfTheDay(i,j).clear();
6304 
6305 		for(int i=0; i<gt.rules.nInternalActivities/*added_act*/; i++){
6306 			if(i<added_act){
6307 			}
6308 			else{
6309 				if(c.times[permutation[i]]==UNALLOCATED_TIME)
6310 					continue;
6311 			}
6312 			//Activity* act=&gt.rules.internalActivitiesList[permutation[i]];
6313 			int d=c.times[permutation[i]]%gt.rules.nDaysPerWeek;
6314 
6315 			if(gt.rules.mode!=MORNINGS_AFTERNOONS){
6316 				for(int j : qAsConst(teachersWithMaxDaysPerWeekForActivities[permutation[i]])){
6317 					assert(teacherActivitiesOfTheDay(j,d).indexOf(permutation[i])==-1);
6318 					teacherActivitiesOfTheDay(j,d).append(permutation[i]);
6319 				}
6320 			}
6321 			else{
6322 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
6323 				QSet<int> smhd=QSet<int>(teachersWithMaxDaysPerWeekForActivities[permutation[i]].constBegin(), teachersWithMaxDaysPerWeekForActivities[permutation[i]].constEnd());
6324 				QSet<int> smtd=QSet<int>(teachersWithMaxThreeConsecutiveDaysForActivities[permutation[i]].constBegin(), teachersWithMaxThreeConsecutiveDaysForActivities[permutation[i]].constEnd());
6325 				QSet<int> smd=QSet<int>(teachersWithMaxRealDaysPerWeekForActivities[permutation[i]].constBegin(), teachersWithMaxRealDaysPerWeekForActivities[permutation[i]].constEnd());
6326 				QSet<int> smn1n2n3=QSet<int>(teachersWithN1N2N3ForActivities[permutation[i]].constBegin(), teachersWithN1N2N3ForActivities[permutation[i]].constEnd());
6327 				QSet<int> sma=QSet<int>(teachersWithMaxAfternoonsPerWeekForActivities[permutation[i]].constBegin(), teachersWithMaxAfternoonsPerWeekForActivities[permutation[i]].constEnd());
6328 				QSet<int> smm=QSet<int>(teachersWithMaxMorningsPerWeekForActivities[permutation[i]].constBegin(), teachersWithMaxMorningsPerWeekForActivities[permutation[i]].constEnd());
6329 #else
6330 				QSet<int> smhd=teachersWithMaxDaysPerWeekForActivities[permutation[i]].toSet();
6331 				QSet<int> smtd=teachersWithMaxThreeConsecutiveDaysForActivities[permutation[i]].toSet();
6332 				QSet<int> smd=teachersWithMaxRealDaysPerWeekForActivities[permutation[i]].toSet();
6333 				QSet<int> smn1n2n3=teachersWithN1N2N3ForActivities[permutation[i]].toSet();
6334 				QSet<int> sma=teachersWithMaxAfternoonsPerWeekForActivities[permutation[i]].toSet();
6335 				QSet<int> smm=teachersWithMaxMorningsPerWeekForActivities[permutation[i]].toSet();
6336 #endif
6337 				QSet<int> smda=smhd+smtd+smd+sma+smn1n2n3;
6338 				QSet<int> smdm=smhd+smtd+smd+smm+smn1n2n3;
6339 
6340 				if(d%2==1){
6341 					for(int j : qAsConst(smda)){
6342 						assert(teacherActivitiesOfTheDay(j,d).indexOf(permutation[i])==-1);
6343 						teacherActivitiesOfTheDay(j,d).append(permutation[i]);
6344 					}
6345 				}
6346 				else{
6347 					for(int j : qAsConst(smdm)){
6348 						assert(teacherActivitiesOfTheDay(j,d).indexOf(permutation[i])==-1);
6349 						teacherActivitiesOfTheDay(j,d).append(permutation[i]);
6350 					}
6351 				}
6352 			}
6353 		}
6354 		//////////////
6355 
6356 		//2011-09-30
6357 		if(haveActivitiesOccupyMaxConstraints || haveActivitiesMaxSimultaneousConstraints){
6358 			for(int t=0; t<gt.rules.nHoursPerWeek; t++)
6359 				activitiesAtTime[t].clear();
6360 
6361 			for(int j=0; j<gt.rules.nInternalActivities/*added_act*/; j++){
6362 				int i=permutation[j];
6363 
6364 				if(!activityHasOccupyMaxConstraints[i] && !activityHasMaxSimultaneousConstraints[i])
6365 					continue;
6366 
6367 				if(j<added_act){
6368 					assert(c.times[i]!=UNALLOCATED_TIME);
6369 				}
6370 				else{
6371 					if(c.times[i]==UNALLOCATED_TIME)
6372 						continue;
6373 				}
6374 				assert(c.times[i]!=UNALLOCATED_TIME);
6375 
6376 				Activity* act=&gt.rules.internalActivitiesList[i];
6377 
6378 				for(int t=c.times[i]; t<c.times[i]+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
6379 					assert(!activitiesAtTime[t].contains(i));
6380 					activitiesAtTime[t].insert(i);
6381 				}
6382 			}
6383 		}
6384 		//////////////
6385 
6386 		foundGoodSwap=false;
6387 
6388 		assert(!swappedActivities[permutation[added_act]]);
6389 		swappedActivities[permutation[added_act]]=true;
6390 
6391 		nRestore=0;
6392 		ncallsrandomswap=0;
6393 		randomSwap(permutation[added_act], 0);
6394 
6395 		if(!foundGoodSwap){
6396 			if(impossibleActivity){
6397 				isRunning=false;
6398 
6399 				nDifficultActivities=1;
6400 				difficultActivities[0]=permutation[added_act];
6401 
6402 				impossible=true;
6403 
6404 				if(threaded){
6405 					myMutex.unlock();
6406 				}
6407 
6408 				emit(impossibleToSolve());
6409 
6410 				return;
6411 			}
6412 
6413 			//update difficult activities (activities which are placed correctly so far, together with added_act)
6414 			nDifficultActivities=added_act+1;
6415 			if(VERBOSE){
6416 				cout<<"nDifficultActivities=="<<nDifficultActivities<<endl;
6417 			}
6418 			for(int j=0; j<=added_act; j++)
6419 				difficultActivities[j]=permutation[j];
6420 
6421 //////////////////////
6422 			assert(conflActivitiesTimeSlot.count()>0);
6423 
6424 			if(VERBOSE){
6425 				cout<<"conflActivitiesTimeSlot.count()=="<<conflActivitiesTimeSlot.count()<<endl;
6426 				for(int i : qAsConst(conflActivitiesTimeSlot)){
6427 					cout<<"Confl activity id:"<<gt.rules.internalActivitiesList[i].id;
6428 					cout<<" time of this activity:"<<c.times[i];
6429 					if(c.rooms[i]!=UNSPECIFIED_ROOM)
6430 						cout<<" room of this activity:"<<qPrintable(gt.rules.internalRoomsList[c.rooms[i]]->name)<<endl;
6431 					else
6432 						cout<<" room of this activity: UNSPECIFIED_ROOM"<<endl;
6433 				}
6434 				//cout<<endl;
6435 				cout<<"timeSlot=="<<timeSlot<<endl;
6436 				if(roomSlot!=UNSPECIFIED_ROOM)
6437 					cout<<"roomSlot=="<<qPrintable(gt.rules.internalRoomsList[roomSlot]->name)<<endl;
6438 				else
6439 					cout<<"roomSlot==UNSPECIFIED_ROOM"<<endl;
6440 			}
6441 
6442 			QList<int> ok;
6443 			QList<int> confl;
6444 			for(int j=0; j<added_act; j++){
6445 				if(conflActivitiesTimeSlot.indexOf(permutation[j])!=-1){
6446 					if(VERBOSE){
6447 						if(triedRemovals(permutation[j],c.times[permutation[j]])>0){
6448 							cout<<"Warning - explored removal: id=="<<
6449 							 gt.rules.internalActivitiesList[permutation[j]].id<<", time=="<<c.times[permutation[j]]
6450 							 <<", times=="<<triedRemovals(permutation[j],c.times[permutation[j]])<<endl;
6451 						}
6452 					}
6453 					triedRemovals(permutation[j],c.times[permutation[j]])++;
6454 
6455 					/////update tabu
6456 					int a=tabu_activities[crt_tabu_index];
6457 					int t=tabu_times[crt_tabu_index];
6458 					if(a>=0 && t>=0){
6459 						assert(triedRemovals(a,t)>0);
6460 						triedRemovals(a,t)--;
6461 						//cout<<"Removing activity with id=="<<gt.rules.internalActivitiesList[a].id<<", time=="<<t<<endl;
6462 					}
6463 					tabu_activities[crt_tabu_index]=permutation[j];
6464 					tabu_times[crt_tabu_index]=c.times[permutation[j]];
6465 					//cout<<"Inserting activity with id=="<<gt.rules.internalActivitiesList[permutation[j]].id<<", time=="<<c.times[permutation[j]]<<endl;
6466 					crt_tabu_index=(crt_tabu_index+1)%tabu_size;
6467 					////////////////
6468 
6469 					confl.append(permutation[j]);
6470 				}
6471 				else
6472 					ok.append(permutation[j]);
6473 			}
6474 
6475 			assert(confl.count()==conflActivitiesTimeSlot.count());
6476 
6477 			int j=0;
6478 			int tmp=permutation[added_act];
6479 			for(int k : qAsConst(ok)){
6480 				permutation[j]=k;
6481 				invPermutation[k]=j;
6482 				j++;
6483 			}
6484 			int q=j;
6485 			permutation[j]=tmp;
6486 			invPermutation[tmp]=j;
6487 			j++;
6488 			if(VERBOSE){
6489 				cout<<"id of permutation[j=="<<j-1<<"]=="<<gt.rules.internalActivitiesList[permutation[j-1]].id<<endl;
6490 				cout<<"conflicting:"<<endl;
6491 			}
6492 			for(int k : qAsConst(confl)){
6493 				permutation[j]=k;
6494 				invPermutation[k]=j;
6495 				j++;
6496 				if(VERBOSE){
6497 					cout<<"id of permutation[j=="<<j-1<<"]=="<<gt.rules.internalActivitiesList[permutation[j-1]].id<<endl;
6498 				}
6499 			}
6500 			assert(j==added_act+1);
6501 
6502 			if(VERBOSE){
6503 				cout<<"tmp represents activity with id=="<<gt.rules.internalActivitiesList[tmp].id;
6504 				cout<<" initial time: "<<c.times[tmp];
6505 				cout<<" final time: "<<timeSlot<<endl;
6506 			}
6507 			c.times[tmp]=timeSlot;
6508 			c.rooms[tmp]=roomSlot;
6509 			c.realRoomsList[tmp]=realRoomsSlot;
6510 
6511 			for(int i=q+1; i<=added_act; i++){
6512 				if(!fixedTimeActivity[permutation[i]])
6513 					c.times[permutation[i]]=UNALLOCATED_TIME;
6514 				c.rooms[permutation[i]]=UNALLOCATED_SPACE;
6515 			}
6516 			c._fitness=-1;
6517 			c.changedForMatrixCalculation=true;
6518 
6519 			added_act=q+1;
6520 			/*if(threaded){
6521 				myMutex.unlock();
6522 			}*/
6523 
6524 			//if(semaphorePlacedActivity){
6525 			emit(activityPlaced(nThread, q+1));
6526 			if(threaded){
6527 				//std::mutex mtx;
6528 				//std::unique_lock<std::mutex> lck(mtx);
6529 				//semaphorePlacedActivity.acquire();
6530 				//cvForPlacedActivity.wait(lck);
6531 				myMutex.unlock();
6532 				semaphorePlacedActivity.acquire();
6533 			}
6534 			//}
6535 
6536 			goto prevvalue;
6537 //////////////////////
6538 		}
6539 		else{ //if foundGoodSwap==true
6540 			nPlacedActivities=added_act+1;
6541 
6542 			if(maxActivitiesPlaced<added_act+1){
6543 				time_t tmp;
6544 				time(&tmp);
6545 				timeToHighestStage=int(difftime(tmp, starting_time));
6546 
6547 				highestStageSolution.copy(gt.rules, c);
6548 
6549 				maxActivitiesPlaced=added_act+1;
6550 
6551 				if(maxPlacedActivityStream!=nullptr){
6552 					int sec=timeToHighestStage;
6553 					int hh=sec/3600;
6554 					sec%=3600;
6555 					int mm=sec/60;
6556 					sec%=60;
6557 					QString s=tr("At time %1 h %2 m %3 s, FET reached %4 activities placed", "h=hours, m=minutes, s=seconds. Please leave spaces between 'time', %1, h, %2, m, %3, s, so they are visible")
6558 						.arg(hh).arg(mm).arg(sec).arg(maxActivitiesPlaced);
6559 
6560 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
6561 					(*maxPlacedActivityStream)<<s<<Qt::endl;
6562 #else
6563 					(*maxPlacedActivityStream)<<s<<endl;
6564 #endif
6565 				}
6566 			}
6567 
6568 			/*if(threaded){
6569 				myMutex.unlock();
6570 			}*/
6571 			emit(activityPlaced(nThread, added_act+1));
6572 			if(threaded){
6573 				//std::mutex mtx;
6574 				//std::unique_lock<std::mutex> lck(mtx);
6575 				//semaphorePlacedActivity.acquire();
6576 				//cvForPlacedActivity.wait(lck);
6577 				myMutex.unlock();
6578 				semaphorePlacedActivity.acquire();
6579 
6580 				//semaphorePlacedActivity.acquire();
6581 				myMutex.lock();
6582 			}
6583 			if(added_act==gt.rules.nInternalActivities && foundGoodSwap){
6584 				isRunning=false;
6585 
6586 				if(threaded){
6587 					myMutex.unlock();
6588 				}
6589 				break;
6590 			}
6591 
6592 			bool ok=true;
6593 			for(int i=0; i<=added_act; i++){
6594 				if(c.times[permutation[i]]==UNALLOCATED_TIME){
6595 					cout<<"ERROR: act with id=="<<gt.rules.internalActivitiesList[permutation[i]].id<<" has time unallocated"<<endl;
6596 					ok=false;
6597 				}
6598 				if(c.rooms[permutation[i]]==UNALLOCATED_SPACE){
6599 					cout<<"ERROR: act with id=="<<gt.rules.internalActivitiesList[permutation[i]].id<<" has room unallocated"<<endl;
6600 					ok=false;
6601 				}
6602 			}
6603 			assert(ok);
6604 		}
6605 
6606 		if(threaded){
6607 			myMutex.unlock();
6608 		}
6609 	}
6610 
6611 	time_t end_time;
6612 	time(&end_time);
6613 	searchTime=int(difftime(end_time, starting_time));
6614 #ifdef FET_COMMAND_LINE
6615 	cout<<"Total searching time (seconds): "<<int(difftime(end_time, starting_time))<<endl;
6616 #else
6617 	if(VERBOSE){
6618 		cout<<"Total searching time (seconds): "<<int(difftime(end_time, starting_time))<<endl;
6619 	}
6620 #endif
6621 
6622 	emit(simulationFinished());
6623 
6624 	//if(threaded)
6625 	//	finishedSemaphore.release();
6626 }
6627 
moveActivity(int ai,int fromslot,int toslot,int fromroom,int toroom,const QList<int> & fromRealRoomsList,const QList<int> & toRealRoomsList)6628 void Generate::moveActivity(int ai, int fromslot, int toslot, int fromroom, int toroom, const QList<int>& fromRealRoomsList, const QList<int>& toRealRoomsList)
6629 {
6630 	Activity* act=&gt.rules.internalActivitiesList[ai];
6631 
6632 	assert(fromslot==c.times[ai]);
6633 	assert(fromroom==c.rooms[ai]);
6634 	assert(fromRealRoomsList==c.realRoomsList[ai]);
6635 
6636 	if(!fixedTimeActivity[ai] && (fromslot==UNALLOCATED_TIME || fromroom==UNALLOCATED_SPACE))
6637 		assert(fromslot==UNALLOCATED_TIME && fromroom==UNALLOCATED_SPACE);
6638 	if(!fixedTimeActivity[ai] && (toslot==UNALLOCATED_TIME || toroom==UNALLOCATED_SPACE))
6639 		assert(toslot==UNALLOCATED_TIME && toroom==UNALLOCATED_SPACE);
6640 
6641 	if(fromslot!=UNALLOCATED_TIME){
6642 		int d=fromslot%gt.rules.nDaysPerWeek;
6643 		int h=fromslot/gt.rules.nDaysPerWeek;
6644 
6645 		////////////////rooms
6646 		int rm=c.rooms[ai];
6647 		if(rm!=UNSPECIFIED_ROOM && rm!=UNALLOCATED_SPACE){
6648 			for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6649 				assert(dd+h<gt.rules.nHoursPerDay);
6650 				if(gt.rules.internalRoomsList[rm]->isVirtual==false){
6651 					if(roomsTimetable(rm,d,h+dd)==ai)
6652 						roomsTimetable(rm,d,h+dd)=-1;
6653 					else
6654 						assert(0);
6655 				}
6656 				else{
6657 					assert(roomsTimetable(rm,d,h+dd)==-1);
6658 				}
6659 			}
6660 
6661 			if(gt.rules.internalRoomsList[rm]->isVirtual){
6662 				assert(rm==fromroom); //this test could also be done for real rooms, above?
6663 				assert(fromRealRoomsList.count()==gt.rules.internalRoomsList[rm]->rrsl.count());
6664 				int i=0;
6665 				for(int rr : qAsConst(fromRealRoomsList)){
6666 					assert(gt.rules.internalRoomsList[rm]->rrsl.at(i).contains(rr));
6667 
6668 					for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6669 						assert(dd+h<gt.rules.nHoursPerDay);
6670 						if(roomsTimetable(rr,d,h+dd)==ai)
6671 							roomsTimetable(rr,d,h+dd)=-1;
6672 						else
6673 							assert(0);
6674 					}
6675 
6676 					i++;
6677 				}
6678 			}
6679 		}
6680 		/////////////////////
6681 
6682 		if(fromslot!=toslot){
6683 			//timetable of students
6684 			for(int q=0; q<act->iSubgroupsList.count(); q++){
6685 				int sb=act->iSubgroupsList.at(q);
6686 				for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6687 					assert(dd+h<gt.rules.nHoursPerDay);
6688 
6689 					assert(subgroupsTimetable(sb,d,h+dd)==ai);
6690 					subgroupsTimetable(sb,d,h+dd)=-1;
6691 				}
6692 			}
6693 
6694 			for(int sb : qAsConst(mustComputeTimetableSubgroups[ai])){
6695 				for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6696 					assert(dd+h<gt.rules.nHoursPerDay);
6697 
6698 					assert(newSubgroupsTimetable(sb,d,h+dd)==ai);
6699 					newSubgroupsTimetable(sb,d,h+dd)=-1;
6700 				}
6701 			}
6702 
6703 			if(gt.rules.mode!=MORNINGS_AFTERNOONS){
6704 				updateSubgroupsNHoursGaps(ai, d);
6705 
6706 				//update students' list of activities for each day
6707 				/////////////////
6708 				for(int st : qAsConst(subgroupsWithMaxDaysPerWeekForActivities[ai])){
6709 					int tt=subgroupActivitiesOfTheDay(st,d).removeAll(ai);
6710 					assert(tt==1);
6711 				}
6712 				/////////////////
6713 			}
6714 			else{
6715 				updateSubgroupsNHoursGaps(ai, d);
6716 				if(haveStudentsMaxGapsPerRealDay)
6717 					updateSubgroupsNHoursGapsRealDay(ai, d/2);
6718 
6719 				//update students' list of activities for each day
6720 				/////////////////
6721 
6722 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
6723 				QSet<int> st_smhd=QSet<int>(subgroupsWithMaxDaysPerWeekForActivities[ai].constBegin(), subgroupsWithMaxDaysPerWeekForActivities[ai].constEnd());
6724 				QSet<int> st_smd=QSet<int>(subgroupsWithMaxRealDaysPerWeekForActivities[ai].constBegin(), subgroupsWithMaxRealDaysPerWeekForActivities[ai].constEnd());
6725 				QSet<int> st_sma=QSet<int>(subgroupsWithMaxAfternoonsPerWeekForActivities[ai].constBegin(), subgroupsWithMaxAfternoonsPerWeekForActivities[ai].constEnd());
6726 				QSet<int> st_smm=QSet<int>(subgroupsWithMaxMorningsPerWeekForActivities[ai].constBegin(), subgroupsWithMaxMorningsPerWeekForActivities[ai].constEnd());
6727 #else
6728 				QSet<int> st_smhd=subgroupsWithMaxDaysPerWeekForActivities[ai].toSet();
6729 				QSet<int> st_smd=subgroupsWithMaxRealDaysPerWeekForActivities[ai].toSet();
6730 				QSet<int> st_sma=subgroupsWithMaxAfternoonsPerWeekForActivities[ai].toSet();
6731 				QSet<int> st_smm=subgroupsWithMaxMorningsPerWeekForActivities[ai].toSet();
6732 #endif
6733 				QSet<int> st_smda=st_smhd+st_smd+st_sma;
6734 				QSet<int> st_smdm=st_smhd+st_smd+st_smm;
6735 
6736 				if(d%2==1){
6737 					for(int sbg : qAsConst(st_smda)){
6738 						int tt=subgroupActivitiesOfTheDay(sbg,d).removeAll(ai);
6739 						assert(tt==1);
6740 					}
6741 				}
6742 				else{
6743 					for(int sbg : qAsConst(st_smdm)){
6744 						int tt=subgroupActivitiesOfTheDay(sbg,d).removeAll(ai);
6745 						assert(tt==1);
6746 					}
6747 				}
6748 
6749 				/*for(int st : qAsConst(subgroupsWithMaxRealDaysPerWeekForActivities[ai])){
6750 					int tt=subgroupActivitiesOfTheDay(st,d).removeAll(ai);
6751 					assert(tt==1);
6752 				}*/
6753 				/////////////////
6754 			}
6755 
6756 			//teachers' timetable
6757 			for(int q=0; q<act->iTeachersList.count(); q++){
6758 				int tch=act->iTeachersList.at(q);
6759 				for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6760 					assert(dd+h<gt.rules.nHoursPerDay);
6761 
6762 					assert(teachersTimetable(tch,d,h+dd)==ai);
6763 					teachersTimetable(tch,d,h+dd)=-1;
6764 				}
6765 			}
6766 
6767 			for(int tch : qAsConst(mustComputeTimetableTeachers[ai])){
6768 				for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6769 					assert(dd+h<gt.rules.nHoursPerDay);
6770 
6771 					assert(newTeachersTimetable(tch,d,h+dd)==ai);
6772 					newTeachersTimetable(tch,d,h+dd)=-1;
6773 				}
6774 			}
6775 
6776 			if(gt.rules.mode!=MORNINGS_AFTERNOONS){
6777 				updateTeachersNHoursGaps(ai, d);
6778 
6779 				//update teachers' list of activities for each day
6780 				/////////////////
6781 				for(int tch : qAsConst(teachersWithMaxDaysPerWeekForActivities[ai])){
6782 					int tt=teacherActivitiesOfTheDay(tch,d).removeAll(ai);
6783 					assert(tt==1);
6784 				}
6785 				/////////////////
6786 			}
6787 			else{
6788 				updateTeachersNHoursGaps(ai, d);
6789 				if(haveTeachersMaxGapsPerRealDay)
6790 					updateTeachersNHoursGapsRealDay(ai, d/2);
6791 
6792 				//update teachers' list of activities for each day
6793 				/////////////////
6794 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
6795 				QSet<int> smhd=QSet<int>(teachersWithMaxDaysPerWeekForActivities[ai].constBegin(), teachersWithMaxDaysPerWeekForActivities[ai].constEnd());
6796 				QSet<int> smtd=QSet<int>(teachersWithMaxThreeConsecutiveDaysForActivities[ai].constBegin(), teachersWithMaxThreeConsecutiveDaysForActivities[ai].constEnd());
6797 				QSet<int> smd=QSet<int>(teachersWithMaxRealDaysPerWeekForActivities[ai].constBegin(), teachersWithMaxRealDaysPerWeekForActivities[ai].constEnd());
6798 				QSet<int> smn1n2n3=QSet<int>(teachersWithN1N2N3ForActivities[ai].constBegin(), teachersWithN1N2N3ForActivities[ai].constEnd());
6799 				QSet<int> sma=QSet<int>(teachersWithMaxAfternoonsPerWeekForActivities[ai].constBegin(), teachersWithMaxAfternoonsPerWeekForActivities[ai].constEnd());
6800 				QSet<int> smm=QSet<int>(teachersWithMaxMorningsPerWeekForActivities[ai].constBegin(), teachersWithMaxMorningsPerWeekForActivities[ai].constEnd());
6801 #else
6802 				QSet<int> smhd=teachersWithMaxDaysPerWeekForActivities[ai].toSet();
6803 				QSet<int> smtd=teachersWithMaxThreeConsecutiveDaysForActivities[ai].toSet();
6804 				QSet<int> smd=teachersWithMaxRealDaysPerWeekForActivities[ai].toSet();
6805 				QSet<int> smn1n2n3=teachersWithN1N2N3ForActivities[ai].toSet();
6806 				QSet<int> sma=teachersWithMaxAfternoonsPerWeekForActivities[ai].toSet();
6807 				QSet<int> smm=teachersWithMaxMorningsPerWeekForActivities[ai].toSet();
6808 #endif
6809 				QSet<int> smda=smhd+smtd+smd+sma+smn1n2n3;
6810 				QSet<int> smdm=smhd+smtd+smd+smm+smn1n2n3;
6811 
6812 				if(d%2==1){
6813 					for(int tch : qAsConst(smda)){
6814 						int tt=teacherActivitiesOfTheDay(tch,d).removeAll(ai);
6815 						assert(tt==1);
6816 					}
6817 				}
6818 				else{
6819 					for(int tch : qAsConst(smdm)){
6820 						int tt=teacherActivitiesOfTheDay(tch,d).removeAll(ai);
6821 						assert(tt==1);
6822 					}
6823 				}
6824 
6825 				/*for(int tch : qAsConst(teachersWithMaxRealDaysPerWeekForActivities[ai])){
6826 					int tt=teacherActivitiesOfTheDay(tch,d).removeAll(ai);
6827 					assert(tt==1);
6828 				}*/
6829 				/////////////////
6830 			}
6831 
6832 			//2011-09-30
6833 			if(activityHasOccupyMaxConstraints[ai] || activityHasMaxSimultaneousConstraints[ai]){
6834 				for(int t=fromslot; t<fromslot+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
6835 					assert(activitiesAtTime[t].contains(ai));
6836 					activitiesAtTime[t].remove(ai);
6837 				}
6838 			}
6839 		}
6840 	}
6841 
6842 	if(toroom>=0 && toroom<gt.rules.nInternalRooms)
6843 		if(gt.rules.internalRoomsList[toroom]->isVirtual==true)
6844 			assert(gt.rules.internalRoomsList[toroom]->rrsl.count()==toRealRoomsList.count());
6845 
6846 	c.times[ai]=toslot;
6847 	c.rooms[ai]=toroom;
6848 	c.realRoomsList[ai]=toRealRoomsList;
6849 	c._fitness=-1;
6850 	c.changedForMatrixCalculation=true;
6851 
6852 	if(toslot!=UNALLOCATED_TIME){
6853 		int d=toslot%gt.rules.nDaysPerWeek;
6854 		int h=toslot/gt.rules.nDaysPerWeek;
6855 
6856 		////////////////rooms
6857 		int rm=c.rooms[ai];
6858 		if(rm!=UNSPECIFIED_ROOM && rm!=UNALLOCATED_SPACE){
6859 			for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6860 				assert(dd+h<gt.rules.nHoursPerDay);
6861 
6862 				assert(rm!=UNALLOCATED_SPACE);
6863 
6864 				if(gt.rules.internalRoomsList[rm]->isVirtual==false){
6865 					assert(roomsTimetable(rm,d,h+dd)==-1);
6866 					roomsTimetable(rm,d,h+dd)=ai;
6867 				}
6868 				else{
6869 					assert(roomsTimetable(rm,d,h+dd)==-1);
6870 				}
6871 			}
6872 
6873 			if(gt.rules.internalRoomsList[rm]->isVirtual){
6874 				assert(rm==toroom); //this test could also be done for real rooms, above?
6875 				assert(toRealRoomsList.count()==gt.rules.internalRoomsList[rm]->rrsl.count());
6876 				int i=0;
6877 				for(int rr : qAsConst(toRealRoomsList)){
6878 					assert(gt.rules.internalRoomsList[rm]->rrsl.at(i).contains(rr));
6879 
6880 					for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6881 						assert(dd+h<gt.rules.nHoursPerDay);
6882 
6883 						assert(rr!=UNALLOCATED_SPACE);
6884 						assert(roomsTimetable(rr,d,h+dd)==-1);
6885 						roomsTimetable(rr,d,h+dd)=ai;
6886 					}
6887 
6888 					i++;
6889 				}
6890 			}
6891 		}
6892 		/////////////////////
6893 
6894 		if(fromslot!=toslot){
6895 			//compute timetable of subgroups
6896 			for(int q=0; q<act->iSubgroupsList.count(); q++){
6897 				int sb=act->iSubgroupsList.at(q);
6898 				for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6899 					assert(dd+h<gt.rules.nHoursPerDay);
6900 
6901 					assert(subgroupsTimetable(sb,d,h+dd)==-1);
6902 					subgroupsTimetable(sb,d,h+dd)=ai;
6903 				}
6904 			}
6905 
6906 			for(int sb : qAsConst(mustComputeTimetableSubgroups[ai])){
6907 				for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6908 					assert(dd+h<gt.rules.nHoursPerDay);
6909 
6910 					assert(newSubgroupsTimetable(sb,d,h+dd)==-1);
6911 					newSubgroupsTimetable(sb,d,h+dd)=ai;
6912 				}
6913 			}
6914 
6915 			if(gt.rules.mode!=MORNINGS_AFTERNOONS){
6916 				updateSubgroupsNHoursGaps(ai, d);
6917 
6918 				//update students' list of activities for each day
6919 				/////////////////
6920 				for(int st : qAsConst(subgroupsWithMaxDaysPerWeekForActivities[ai])){
6921 					assert(subgroupActivitiesOfTheDay(st,d).indexOf(ai)==-1);
6922 					subgroupActivitiesOfTheDay(st,d).append(ai);
6923 				}
6924 				/////////////////
6925 			}
6926 			else{
6927 				updateSubgroupsNHoursGaps(ai, d);
6928 				if(haveStudentsMaxGapsPerRealDay)
6929 					updateSubgroupsNHoursGapsRealDay(ai, d/2);
6930 
6931 				//update students' list of activities for each day
6932 				/////////////////
6933 
6934 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
6935 				QSet<int> st_smhd=QSet<int>(subgroupsWithMaxDaysPerWeekForActivities[ai].constBegin(), subgroupsWithMaxDaysPerWeekForActivities[ai].constEnd());
6936 				QSet<int> st_smd=QSet<int>(subgroupsWithMaxRealDaysPerWeekForActivities[ai].constBegin(), subgroupsWithMaxRealDaysPerWeekForActivities[ai].constEnd());
6937 				QSet<int> st_sma=QSet<int>(subgroupsWithMaxAfternoonsPerWeekForActivities[ai].constBegin(), subgroupsWithMaxAfternoonsPerWeekForActivities[ai].constEnd());
6938 				QSet<int> st_smm=QSet<int>(subgroupsWithMaxMorningsPerWeekForActivities[ai].constBegin(), subgroupsWithMaxMorningsPerWeekForActivities[ai].constEnd());
6939 #else
6940 				QSet<int> st_smhd=subgroupsWithMaxDaysPerWeekForActivities[ai].toSet();
6941 				QSet<int> st_smd=subgroupsWithMaxRealDaysPerWeekForActivities[ai].toSet();
6942 				QSet<int> st_sma=subgroupsWithMaxAfternoonsPerWeekForActivities[ai].toSet();
6943 				QSet<int> st_smm=subgroupsWithMaxMorningsPerWeekForActivities[ai].toSet();
6944 #endif
6945 				QSet<int> st_smda=st_smhd+st_smd+st_sma;
6946 				QSet<int> st_smdm=st_smhd+st_smd+st_smm;
6947 
6948 				if(d%2==1){
6949 					for(int sbg : qAsConst(st_smda)){
6950 						assert(subgroupActivitiesOfTheDay(sbg,d).indexOf(ai)==-1);
6951 						subgroupActivitiesOfTheDay(sbg,d).append(ai);
6952 					}
6953 				}
6954 				else{
6955 					for(int sbg : qAsConst(st_smdm)){
6956 						assert(subgroupActivitiesOfTheDay(sbg,d).indexOf(ai)==-1);
6957 						subgroupActivitiesOfTheDay(sbg,d).append(ai);
6958 					}
6959 				}
6960 
6961 				/*for(int st : qAsConst(subgroupsWithMaxRealDaysPerWeekForActivities[ai])){
6962 					assert(subgroupActivitiesOfTheDay(st,d).indexOf(ai)==-1);
6963 					subgroupActivitiesOfTheDay(st,d).append(ai);
6964 				}*/
6965 				/////////////////
6966 			}
6967 
6968 			//teachers' timetable
6969 			for(int q=0; q<act->iTeachersList.count(); q++){
6970 				int tch=act->iTeachersList.at(q);
6971 				for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6972 					assert(dd+h<gt.rules.nHoursPerDay);
6973 
6974 					assert(teachersTimetable(tch,d,h+dd)==-1);
6975 					teachersTimetable(tch,d,h+dd)=ai;
6976 				}
6977 			}
6978 
6979 			for(int tch : qAsConst(mustComputeTimetableTeachers[ai])){
6980 				for(int dd=0; dd<gt.rules.internalActivitiesList[ai].duration; dd++){
6981 					assert(dd+h<gt.rules.nHoursPerDay);
6982 
6983 					assert(newTeachersTimetable(tch,d,h+dd)==-1);
6984 					newTeachersTimetable(tch,d,h+dd)=ai;
6985 				}
6986 			}
6987 
6988 			if(gt.rules.mode!=MORNINGS_AFTERNOONS){
6989 				//////////
6990 				updateTeachersNHoursGaps(ai, d);
6991 
6992 				//update teachers' list of activities for each day
6993 				/////////////////
6994 				for(int tch : qAsConst(teachersWithMaxDaysPerWeekForActivities[ai])){
6995 					assert(teacherActivitiesOfTheDay(tch,d).indexOf(ai)==-1);
6996 					teacherActivitiesOfTheDay(tch,d).append(ai);
6997 				}
6998 				/////////////////
6999 			}
7000 			else{
7001 				//////////
7002 				updateTeachersNHoursGaps(ai, d);
7003 				if(haveTeachersMaxGapsPerRealDay)
7004 					updateTeachersNHoursGapsRealDay(ai, d/2);
7005 
7006 				//update teachers' list of activities for each day
7007 				/////////////////
7008 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
7009 				QSet<int> smhd=QSet<int>(teachersWithMaxDaysPerWeekForActivities[ai].constBegin(), teachersWithMaxDaysPerWeekForActivities[ai].constEnd());
7010 				QSet<int> smtd=QSet<int>(teachersWithMaxThreeConsecutiveDaysForActivities[ai].constBegin(), teachersWithMaxThreeConsecutiveDaysForActivities[ai].constEnd());
7011 				QSet<int> smd=QSet<int>(teachersWithMaxRealDaysPerWeekForActivities[ai].constBegin(), teachersWithMaxRealDaysPerWeekForActivities[ai].constEnd());
7012 				QSet<int> smn1n2n3=QSet<int>(teachersWithN1N2N3ForActivities[ai].constBegin(), teachersWithN1N2N3ForActivities[ai].constEnd());
7013 				QSet<int> sma=QSet<int>(teachersWithMaxAfternoonsPerWeekForActivities[ai].constBegin(), teachersWithMaxAfternoonsPerWeekForActivities[ai].constEnd());
7014 				QSet<int> smm=QSet<int>(teachersWithMaxMorningsPerWeekForActivities[ai].constBegin(), teachersWithMaxMorningsPerWeekForActivities[ai].constEnd());
7015 #else
7016 				QSet<int> smhd=teachersWithMaxDaysPerWeekForActivities[ai].toSet();
7017 				QSet<int> smtd=teachersWithMaxThreeConsecutiveDaysForActivities[ai].toSet();
7018 				QSet<int> smd=teachersWithMaxRealDaysPerWeekForActivities[ai].toSet();
7019 				QSet<int> smn1n2n3=teachersWithN1N2N3ForActivities[ai].toSet();
7020 				QSet<int> sma=teachersWithMaxAfternoonsPerWeekForActivities[ai].toSet();
7021 				QSet<int> smm=teachersWithMaxMorningsPerWeekForActivities[ai].toSet();
7022 #endif
7023 				QSet<int> smda=smhd+smtd+smd+sma+smn1n2n3;
7024 				QSet<int> smdm=smhd+smtd+smd+smm+smn1n2n3;
7025 
7026 				if(d%2==1){
7027 					for(int tch : qAsConst(smda)){
7028 						assert(teacherActivitiesOfTheDay(tch,d).indexOf(ai)==-1);
7029 						teacherActivitiesOfTheDay(tch,d).append(ai);
7030 					}
7031 				}
7032 				else{
7033 					for(int tch : qAsConst(smdm)){
7034 						assert(teacherActivitiesOfTheDay(tch,d).indexOf(ai)==-1);
7035 						teacherActivitiesOfTheDay(tch,d).append(ai);
7036 					}
7037 				}
7038 
7039 				/*for(int tch : qAsConst(teachersWithMaxRealDaysPerWeekForActivities[ai])){
7040 					assert(teacherActivitiesOfTheDay(tch,d).indexOf(ai)==-1);
7041 					teacherActivitiesOfTheDay(tch,d).append(ai);
7042 				}*/
7043 				/////////////////
7044 			}
7045 
7046 			//2011-09-30
7047 			if(activityHasOccupyMaxConstraints[ai] || activityHasMaxSimultaneousConstraints[ai]){
7048 				for(int t=toslot; t<toslot+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
7049 					assert(!activitiesAtTime[t].contains(ai));
7050 					activitiesAtTime[t].insert(ai);
7051 				}
7052 			}
7053 		}
7054 	}
7055 }
7056 
7057 //faster: (to avoid allocating memory at each call)
7058 /*static double nMinDaysBrokenL[MAX_LEVEL][MAX_HOURS_PER_WEEK];
7059 static int selectedRoomL[MAX_LEVEL][MAX_HOURS_PER_WEEK];
7060 static int permL[MAX_LEVEL][MAX_HOURS_PER_WEEK];
7061 static QList<int> conflActivitiesL[MAX_LEVEL][MAX_HOURS_PER_WEEK];
7062 static int nConflActivitiesL[MAX_LEVEL][MAX_HOURS_PER_WEEK];
7063 static int roomSlotsL[MAX_LEVEL][MAX_HOURS_PER_WEEK];
7064 static QList<int> realRoomsListL[MAX_LEVEL][MAX_HOURS_PER_WEEK]; //the chosen real rooms, in the order of the sets, one for each set, if the room is virtual
7065 
7066 static int currentLevel;
7067 
7068 static QSet<int> conflActivitiesSet;*/
7069 
compareFunctionGenerate(int i,int j)7070 inline bool Generate::compareFunctionGenerate(int i, int j)
7071 {
7072 	if(nConflActivitiesL[currentLevel][i] < nConflActivitiesL[currentLevel][j] ||
7073 	 (nConflActivitiesL[currentLevel][i] == nConflActivitiesL[currentLevel][j] &&
7074 	 nMinDaysBrokenL[currentLevel][i] < nMinDaysBrokenL[currentLevel][j]))
7075 		return true;
7076 
7077 	return false;
7078 }
7079 
7080 
7081 #define nMinDaysBroken			(nMinDaysBrokenL[level])
7082 #define selectedRoom			(selectedRoomL[level])
7083 #define perm					(permL[level])
7084 #define conflActivities			(conflActivitiesL[level])
7085 #define nConflActivities		(nConflActivitiesL[level])
7086 #define roomSlots				(roomSlotsL[level])
7087 
7088 #define realRoomsListLevel		(realRoomsListL[level])
7089 
randomSwap(int ai,int level)7090 void Generate::randomSwap(int ai, int level){
7091 	if(level==0){
7092 		conflActivitiesTimeSlot.clear();
7093 		timeSlot=-1;
7094 	}
7095 
7096 	if(level>=level_limit){
7097 		return;
7098 	}
7099 
7100 	if(ncallsrandomswap>=limitcallsrandomswap)
7101 		return;
7102 
7103 	ncallsrandomswap++;
7104 
7105 	Activity* act=&gt.rules.internalActivitiesList[ai];
7106 
7107 	bool updateSubgroups=(mustComputeTimetableSubgroups[ai].count()>0);
7108 	bool updateTeachers=(mustComputeTimetableTeachers[ai].count()>0);
7109 
7110 	int activity_count_impossible_tries=1;
7111 
7112 again_if_impossible_activity:
7113 
7114 	//generate a random permutation in linear time like in CLR (Cormen, Leiserson, and Rivest - Introduction to algorithms).
7115 	//this is used to scan the times in a random order
7116 	for(int i=0; i<gt.rules.nHoursPerWeek; i++)
7117 		perm[i]=i;
7118 	for(int i=0; i<gt.rules.nHoursPerWeek; i++){
7119 		int t=perm[i];
7120 		int r=rng.intMRG32k3a(gt.rules.nHoursPerWeek-i);
7121 		perm[i]=perm[i+r];
7122 		perm[i+r]=t;
7123 	}
7124 
7125 	for(int n=0; n<gt.rules.nHoursPerWeek; n++){
7126 		int newtime=perm[n];
7127 
7128 		 //bug corrected on 2021-04-13, this instruction must be executed before the if-s/continue below, because otherwise nMinDaysBroken[newtime] might remain uninitialized
7129 		 //and give a crash lower in the code. For instance, for a Hungary/Bethlen locked timetable, it crashes after the std::stable_sort near the end of generate.cpp
7130 		 //( std::stable_sort(perm+0, perm+gt.rules.nHoursPerWeek, [this](int i, int j){return compareFunctionGenerate(i, j);}); )
7131 		 //when asserting that if nConflActivities[perm[i-1]] == nConflActivities[perm[i]] then it must be nMinDaysBroken[perm[i-1]] <= nMinDaysBroken[perm[i]],
7132 		 //which sometimes seems to result in comparing -nan <= -nan, which seems not to be true. This bug appeared only
7133 		 //with Qt 6.0.3 (not with Qt 5) and only in the fet-cl (command-line) version.
7134 		nMinDaysBroken[newtime]=0.0;
7135 
7136 		if(c.times[ai]!=UNALLOCATED_TIME){
7137 			if(c.times[ai]!=newtime){
7138 				nConflActivities[newtime]=MAX_ACTIVITIES;
7139 				continue;
7140 			}
7141 		}
7142 
7143 		nConflActivities[newtime]=0;
7144 		conflActivities[newtime].clear();
7145 
7146 		int d=newtime%gt.rules.nDaysPerWeek;
7147 		int h=newtime/gt.rules.nDaysPerWeek;
7148 
7149 		//For mornings-afternoons
7150 		int d_first, d_last;
7151 		int d_pre, d_after;
7152 
7153 		bool okteachersmorningsafternoonsbehavior;
7154 		bool ok_max_two_consecutive_mornings_afternoons;
7155 
7156 		bool okbasictime;
7157 		bool okmindays;
7158 		bool okmaxdays;
7159 		bool oksamestartingtime;
7160 		bool oksamestartinghour;
7161 		bool oksamestartingday;
7162 		bool oknotoverlapping;
7163 		bool oktwoactivitiesconsecutive;
7164 		bool oktwoactivitiesgrouped;
7165 		bool okthreeactivitiesgrouped;
7166 		bool oktwoactivitiesordered;
7167 		bool oktwoactivitiesorderedifsameday;
7168 		bool okactivityendsstudentsday;
7169 		bool okactivityendsteachersday;
7170 
7171 		bool okstudentsmaxdaysperweek;
7172 		bool okstudentsmaxrealdaysperweek;
7173 		bool okstudentsintervalmaxdaysperweek;
7174 
7175 		bool okstudentsmaxspanperday;
7176 		bool okstudentsminrestinghours;
7177 
7178 		bool okstudentsminrestinghoursbetweenmorningandafternoon;
7179 		bool okstudentsmaxspanperrealday;
7180 
7181 		bool okstudentsmaxgapsperweekforrealdays;
7182 		bool okstudentsmaxgapsperrealday;
7183 
7184 		bool okstudentsearlymaxbeginningsatsecondhour;
7185 		bool okstudentsmaxgapsperweek;
7186 		bool okstudentsmaxgapsperday;
7187 		bool okstudentsmaxhoursdaily;
7188 		bool okstudentsmaxhourscontinuously;
7189 		bool okstudentsminhoursdaily;
7190 
7191 		bool okstudentsmaxhoursdailyrealdays;
7192 
7193 		bool okstudentsactivitytagmaxhoursdailyrealdays;
7194 		bool okstudentsmorningintervalmaxdaysperweek;
7195 		bool okstudentsafternoonintervalmaxdaysperweek;
7196 
7197 		bool okstudentsafternoonsearlymaxbeginningsatsecondhour;
7198 		bool okstudentsmorningsearlymaxbeginningsatsecondhour;
7199 		bool okstudentsmaxafternoonsperweek;
7200 		bool okstudentsmaxmorningsperweek;
7201 
7202 		bool okstudentsminmorningsafternoonsperweek;
7203 		bool okstudentsmaxhoursperallafternoons;
7204 
7205 		bool okteachersmaxdaysperweek;
7206 		bool okteachersmaxthreeconsecutivedays;
7207 		bool okteachersintervalmaxdaysperweek;
7208 
7209 		bool okteachersafternoonsearlymaxbeginningsatsecondhour;
7210 		bool okteachersmorningsearlymaxbeginningsatsecondhour;
7211 		bool okteachersmorningsafternoonsearlymaxbeginningsatsecondhour;
7212 		bool okteachermaxafternoonsperweek;
7213 		bool okteachermaxmorningsperweek;
7214 		bool okteachersmorningintervalmaxdaysperweek;
7215 		bool okteachersafternoonintervalmaxdaysperweek;
7216 
7217 		bool okteachermaxrealdaysperweek;
7218 
7219 		bool okteachersmaxspanperday;
7220 		bool okteachersminrestinghours;
7221 
7222 		bool okteachersmaxspanperrealday;
7223 		bool okteachersminrestinghoursbetweenmorningandafternoon;
7224 
7225 		bool okteachersmaxgapsperweek;
7226 		bool okteachersmaxgapsperday;
7227 		bool okteachersmaxgapspermorningandafternoon;
7228 		bool okteachersmaxhoursdaily;
7229 		bool okteachersmaxhourscontinuously;
7230 		bool okteachersminhoursdaily;
7231 		bool okteachersmindaysperweek;
7232 
7233 		bool okteachersminrealdaysperweek;
7234 
7235 		bool okteachersmaxgapsperweekforrealdays;
7236 		bool okteachersmaxgapsperrealday;
7237 		bool okteachersmax0gapsperafternoon;
7238 		bool okteachersmaxhoursdailyrealdays;
7239 		bool okteachersminhoursdailyrealdays;
7240 		bool okteachersminmorningsafternoonsperweek;
7241 
7242 		bool okteachersactivitytagmaxhoursdailyrealdays;
7243 
7244 		bool okmingapsbetweenactivities;
7245 		bool okmaxgapsbetweenactivities;
7246 
7247 		bool okteachersactivitytagmaxhourscontinuously;
7248 		bool okstudentsactivitytagmaxhourscontinuously;
7249 
7250 		bool okteachersactivitytagmaxhoursdaily;
7251 		bool okstudentsactivitytagmaxhoursdaily;
7252 
7253 		bool okteachersactivitytagminhoursdaily;
7254 		bool okstudentsactivitytagminhoursdaily;
7255 
7256 		bool okstudentsmingapsbetweenorderedpairofactivitytags;
7257 		bool okteachersmingapsbetweenorderedpairofactivitytags;
7258 
7259 		bool okteachersmaxtwoactivitytagsperdayfromn1n2n3;
7260 		bool okteachersmaxhoursperallafternoons;
7261 
7262 		//2011-09-25
7263 		bool okactivitiesoccupymaxtimeslotsfromselection;
7264 
7265 		//2020-04-30
7266 		bool okmaxtotalactivitiesfromsetinselectedtimeslots;
7267 
7268 		//2019-11-16
7269 		bool okactivitiesoccupymintimeslotsfromselection;
7270 
7271 		//2011-09-30
7272 		bool okactivitiesmaxsimultaneousinselectedtimeslots;
7273 
7274 		//2019-11-16
7275 		bool okactivitiesminsimultaneousinselectedtimeslots;
7276 
7277 		//for terms
7278 		//2020-01-14
7279 		bool okactivitiesmaxinaterm;
7280 		bool okactivitiesoccupymaxterms;
7281 
7282 		if(c.times[ai]!=UNALLOCATED_TIME)
7283 			goto skip_here_if_already_allocated_in_time;
7284 
7285 /////////////////////////////////////////////////////////////////////////////////////////////
7286 
7287 		//Old comment below
7288 		//not too late
7289 		//unneeded code, because notAllowedTimesPercentages(ai,newtime)==100 now
7290 		//you can comment this code, but you cannot put an assert failed, because the test is done in the next section (see 13 lines below).
7291 		/*if(h+act->duration>gt.rules.nHoursPerDay){
7292 			//if(updateSubgroups || updateTeachers)
7293 			//	removeAiFromNewTimetable(ai, act, d, h);
7294 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7295 
7296 			nConflActivities[newtime]=MAX_ACTIVITIES;
7297 			continue;
7298 		}*/
7299 
7300 /////////////////////////////////////////////////////////////////////////////////////////////
7301 
7302 		//allowed (tch&st not available, break, act(s) preferred time(s))
7303 		if(!skipRandom(notAllowedTimesPercentages(ai,newtime))){
7304 			//if(updateSubgroups || updateTeachers)
7305 			//	removeAiFromNewTimetable(ai, act, d, h);
7306 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7307 
7308 			nConflActivities[newtime]=MAX_ACTIVITIES;
7309 			continue;
7310 		}
7311 
7312 /////////////////////////////////////////////////////////////////////////////////////////////
7313 
7314 		//care about basic time constraints
7315 		okbasictime=true;
7316 
7317 		///////////////////////////////////
7318 		//added in 5.0.0-preview30
7319 		//same teacher?
7320 		for(int dur=0; dur<act->duration; dur++){
7321 			assert(h+dur<gt.rules.nHoursPerDay);
7322 			for(int tch : qAsConst(act->iTeachersList)){
7323 				if(teachersTimetable(tch,d,h+dur)>=0){
7324 					int ai2=teachersTimetable(tch,d,h+dur);
7325 					assert(ai2!=ai);
7326 
7327 					//assert(activitiesConflictingPercentage(ai,ai2)==100);
7328 					assert(activitiesConflictingPercentage[ai].value(ai2, -1)==100);
7329 
7330 					if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7331 						okbasictime=false;
7332 						goto impossiblebasictime;
7333 					}
7334 
7335 					if(!conflActivities[newtime].contains(ai2)){
7336 						conflActivities[newtime].append(ai2);
7337 						nConflActivities[newtime]++;
7338 						assert(nConflActivities[newtime]==conflActivities[newtime].count());
7339 					}
7340 				}
7341 			}
7342 		}
7343 		//same subgroup?
7344 		if(gt.rules.mode!=BLOCK_PLANNING){
7345 			for(int dur=0; dur<act->duration; dur++){
7346 				assert(h+dur<gt.rules.nHoursPerDay);
7347 				for(int sbg : qAsConst(act->iSubgroupsList)){
7348 					if(subgroupsTimetable(sbg,d,h+dur)>=0){
7349 						int ai2=subgroupsTimetable(sbg,d,h+dur);
7350 						assert(ai2!=ai);
7351 
7352 						//assert(activitiesConflictingPercentage(ai,ai2)==100);
7353 						assert(activitiesConflictingPercentage[ai].value(ai2, -1)==100);
7354 
7355 						if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7356 							okbasictime=false;
7357 							goto impossiblebasictime;
7358 						}
7359 
7360 						if(!conflActivities[newtime].contains(ai2)){
7361 							conflActivities[newtime].append(ai2);
7362 							nConflActivities[newtime]++;
7363 							assert(nConflActivities[newtime]==conflActivities[newtime].count());
7364 						}
7365 					}
7366 				}
7367 			}
7368 		}
7369 		else{
7370 			//same subgroup in real life time slots?
7371 			for(int dur=0; dur<act->duration; dur++){
7372 				assert(h+dur<gt.rules.nHoursPerDay);
7373 				for(int sbg : qAsConst(act->iSubgroupsList)){
7374 					for(int day_tch=0; day_tch<gt.rules.nDaysPerWeek; day_tch++){
7375 						if(subgroupsTimetable(sbg,day_tch,h+dur)>=0){
7376 							int ai2=subgroupsTimetable(sbg,day_tch,h+dur);
7377 							assert(ai2!=ai);
7378 
7379 							//assert(activitiesConflictingPercentage(ai,ai2)==100);
7380 							assert(activitiesConflictingPercentage[ai].value(ai2, -1)==100);
7381 							if(true){
7382 								if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7383 									okbasictime=false;
7384 									goto impossiblebasictime;
7385 								}
7386 
7387 								if(!conflActivities[newtime].contains(ai2)){
7388 									conflActivities[newtime].append(ai2);
7389 									nConflActivities[newtime]++;
7390 									assert(nConflActivities[newtime]==conflActivities[newtime].count());
7391 								}
7392 							}
7393 						}
7394 					}
7395 				}
7396 			}
7397 		}
7398 		///////////////////////////////////
7399 
7400 impossiblebasictime:
7401 		if(!okbasictime){
7402 			//if(updateSubgroups || updateTeachers)
7403 			//	removeAiFromNewTimetable(ai, act, d, h);
7404 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7405 
7406 			nConflActivities[newtime]=MAX_ACTIVITIES;
7407 			continue;
7408 		}
7409 
7410 /////////////////////////////////////////////////////////////////////////////////////////////
7411 
7412 		//care about min days between activities
7413 		okmindays=true;
7414 
7415 		for(int i=0; i<minDaysListOfActivities[ai].count(); i++){
7416 			int ai2=minDaysListOfActivities[ai].at(i);
7417 			int md=minDaysListOfMinDays[ai].at(i);
7418 			int ai2time=c.times[ai2];
7419 			if(ai2time!=UNALLOCATED_TIME){
7420 				int d2=ai2time%gt.rules.nDaysPerWeek;
7421 				int h2=ai2time/gt.rules.nDaysPerWeek;
7422 				if((gt.rules.mode!=MORNINGS_AFTERNOONS && md>abs(d-d2)) || (gt.rules.mode==MORNINGS_AFTERNOONS && md>abs(d/2-d2/2))){
7423 					bool okrand=skipRandom(minDaysListOfWeightPercentages[ai].at(i));
7424 					//if(fixedTimeActivity[ai] && minDaysListOfWeightPercentages[ai].at(i)<100.0)
7425 					//	okrand=true;
7426 
7427 					if(minDaysListOfConsecutiveIfSameDay[ai].at(i)==true){ //must place them adjacent if on same day
7428 						if(okrand &&
7429 						 ((gt.rules.mode!=MORNINGS_AFTERNOONS && ( (d==d2 && (h+act->duration==h2 || h2+gt.rules.internalActivitiesList[ai2].duration==h)) || d!=d2 ))
7430 						 ||
7431 						 (gt.rules.mode==MORNINGS_AFTERNOONS && ( (d==d2 && (h+act->duration==h2 || h2+gt.rules.internalActivitiesList[ai2].duration==h)) || d/2!=d2/2 ))
7432 						 )){
7433 							//nMinDaysBroken[newtime]++;
7434 							nMinDaysBroken[newtime]+=minDaysListOfWeightPercentages[ai].at(i)/100.0;
7435 						}
7436 						else{
7437 							if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7438 								okmindays=false;
7439 								goto impossiblemindays;
7440 							}
7441 
7442 							if(!conflActivities[newtime].contains(ai2)){
7443 								conflActivities[newtime].append(ai2);
7444 								nConflActivities[newtime]++;
7445 								assert(nConflActivities[newtime]==conflActivities[newtime].count());
7446 							}
7447 						}
7448 					}
7449 					else{ //can place them anywhere
7450 						if(okrand){
7451 						 	//nMinDaysBroken[newtime]++;
7452 						 	nMinDaysBroken[newtime]+=minDaysListOfWeightPercentages[ai].at(i)/100.0;
7453 						}
7454 						else{
7455 							if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7456 								okmindays=false;
7457 								goto impossiblemindays;
7458 							}
7459 
7460 							if(!conflActivities[newtime].contains(ai2)){
7461 								conflActivities[newtime].append(ai2);
7462 								nConflActivities[newtime]++;
7463 								assert(nConflActivities[newtime]==conflActivities[newtime].count());
7464 							}
7465 						}
7466 					}
7467 				}
7468 			}
7469 		}
7470 impossiblemindays:
7471 		if(!okmindays){
7472 			//if(updateSubgroups || updateTeachers)
7473 			//	removeAiFromNewTimetable(ai, act, d, h);
7474 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7475 
7476 			nConflActivities[newtime]=MAX_ACTIVITIES;
7477 			continue;
7478 		}
7479 
7480 /////////////////////////////////////////////////////////////////////////////////////////////
7481 
7482 		//care about max days between activities
7483 		okmaxdays=true;
7484 
7485 		for(int i=0; i<maxDaysListOfActivities[ai].count(); i++){
7486 			int ai2=maxDaysListOfActivities[ai].at(i);
7487 			int md=maxDaysListOfMaxDays[ai].at(i);
7488 			int ai2time=c.times[ai2];
7489 			if(ai2time!=UNALLOCATED_TIME){
7490 				int d2=ai2time%gt.rules.nDaysPerWeek;
7491 				//int h2=ai2time/gt.rules.nDaysPerWeek;
7492 				if((gt.rules.mode!=MORNINGS_AFTERNOONS && md<abs(d-d2)) || (gt.rules.mode==MORNINGS_AFTERNOONS && md<abs(d/2-d2/2))){
7493 					bool okrand=skipRandom(maxDaysListOfWeightPercentages[ai].at(i));
7494 					if(!okrand){
7495 						if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7496 							okmaxdays=false;
7497 							goto impossiblemaxdays;
7498 						}
7499 
7500 						if(!conflActivities[newtime].contains(ai2)){
7501 							conflActivities[newtime].append(ai2);
7502 
7503 							nConflActivities[newtime]++;
7504 							assert(nConflActivities[newtime]==conflActivities[newtime].count());
7505 						}
7506 					}
7507 				}
7508 			}
7509 		}
7510 impossiblemaxdays:
7511 		if(!okmaxdays){
7512 			nConflActivities[newtime]=MAX_ACTIVITIES;
7513 			continue;
7514 		}
7515 
7516 /////////////////////////////////////////////////////////////////////////////////////////////
7517 		//care about min gaps between activities
7518 		okmingapsbetweenactivities=true;
7519 
7520 		for(int i=0; i<minGapsBetweenActivitiesListOfActivities[ai].count(); i++){
7521 			int ai2=minGapsBetweenActivitiesListOfActivities[ai].at(i);
7522 			int mg=minGapsBetweenActivitiesListOfMinGaps[ai].at(i);
7523 			int ai2time=c.times[ai2];
7524 			if(ai2time!=UNALLOCATED_TIME){
7525 				int d2=ai2time%gt.rules.nDaysPerWeek;
7526 				int h2=ai2time/gt.rules.nDaysPerWeek;
7527 				int duration2=gt.rules.internalActivitiesList[ai2].duration;
7528 				bool oktmp=true;
7529 				if(d==d2){
7530 					if(h2>=h){
7531 						if(h+act->duration+mg > h2){
7532 							oktmp=false;
7533 						}
7534 					}
7535 					else{
7536 						if(h2+duration2+mg > h){
7537 							oktmp=false;
7538 						}
7539 					}
7540 				}
7541 
7542 				if(!oktmp){
7543 					bool okrand=skipRandom(minGapsBetweenActivitiesListOfWeightPercentages[ai].at(i));
7544 
7545 					//if(fixedTimeActivity[ai] && minGapsBetweenActivitiesListOfWeightPercentages[ai].at(i)<100.0)
7546 					//	okrand=true;
7547 
7548 					if(!okrand){
7549 						if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7550 							okmingapsbetweenactivities=false;
7551 							goto impossiblemingapsbetweenactivities;
7552 						}
7553 
7554 						if(!conflActivities[newtime].contains(ai2)){
7555 							conflActivities[newtime].append(ai2);
7556 							nConflActivities[newtime]++;
7557 							assert(nConflActivities[newtime]==conflActivities[newtime].count());
7558 						}
7559 					}
7560 				}
7561 			}
7562 		}
7563 impossiblemingapsbetweenactivities:
7564 		if(!okmingapsbetweenactivities){
7565 			//if(updateSubgroups || updateTeachers)
7566 			//	removeAiFromNewTimetable(ai, act, d, h);
7567 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7568 
7569 			nConflActivities[newtime]=MAX_ACTIVITIES;
7570 			continue;
7571 		}
7572 
7573 /////////////////////////////////////////////////////////////////////////////////////////////
7574 
7575 		//care about max gaps between activities
7576 		okmaxgapsbetweenactivities=true;
7577 
7578 		//used in block-planning mode.
7579 		if(gt.rules.mode==BLOCK_PLANNING){
7580 			for(int i=0; i<maxGapsBetweenActivitiesListOfActivities[ai].count(); i++){
7581 				int ai2=maxGapsBetweenActivitiesListOfActivities[ai].at(i);
7582 				int mg=maxGapsBetweenActivitiesListOfMaxGaps[ai].at(i);
7583 				int ai2time=c.times[ai2];
7584 				if(ai2time!=UNALLOCATED_TIME){
7585 					int d2=ai2time%gt.rules.nDaysPerWeek;
7586 					int h2=ai2time/gt.rules.nDaysPerWeek;
7587 					int duration2=gt.rules.internalActivitiesList[ai2].duration;
7588 					bool oktmp=true;
7589 					if(d==d2){
7590 						if(h2>=h){
7591 							if(h+act->duration+mg < h2){
7592 								oktmp=false;
7593 							}
7594 						}
7595 						else{
7596 							if(h2+duration2+mg < h){
7597 								oktmp=false;
7598 							}
7599 						}
7600 					}
7601 
7602 					if(!oktmp){
7603 						bool okrand=skipRandom(maxGapsBetweenActivitiesListOfWeightPercentages[ai].at(i));
7604 
7605 						//if(fixedTimeActivity[ai] && maxGapsBetweenActivitiesListOfWeightPercentages[ai].at(i)<100.0)
7606 						//	okrand=true;
7607 
7608 						if(!okrand){
7609 							if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7610 								okmaxgapsbetweenactivities=false;
7611 								goto impossiblemaxgapsbetweenactivities;
7612 							}
7613 
7614 							if(!conflActivities[newtime].contains(ai2)){
7615 								conflActivities[newtime].append(ai2);
7616 								nConflActivities[newtime]++;
7617 								assert(nConflActivities[newtime]==conflActivities[newtime].count());
7618 							}
7619 						}
7620 					}
7621 				}
7622 			}
7623 		}
7624 impossiblemaxgapsbetweenactivities:
7625 		if(!okmaxgapsbetweenactivities){
7626 			//if(updateSubgroups || updateTeachers)
7627 			//	removeAiFromNewTimetable(ai, act, d, h);
7628 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7629 
7630 			nConflActivities[newtime]=MAX_ACTIVITIES;
7631 			continue;
7632 		}
7633 
7634 /////////////////////////////////////////////////////////////////////////////////////////////
7635 
7636 		//allowed from same starting time
7637 		oksamestartingtime=true;
7638 
7639 		for(int i=0; i<activitiesSameStartingTimeActivities[ai].count(); i++){
7640 			int ai2=activitiesSameStartingTimeActivities[ai].at(i);
7641 			double perc=activitiesSameStartingTimePercentages[ai].at(i);
7642 			if(c.times[ai2]!=UNALLOCATED_TIME){
7643 				bool sR=skipRandom(perc);
7644 
7645 				//if(fixedTimeActivity[ai] && perc<100.0)
7646 				//	sR=true;
7647 
7648 				if(newtime!=c.times[ai2] && !sR){
7649 					assert(ai2!=ai);
7650 
7651 					if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7652 						oksamestartingtime=false;
7653 						goto impossiblesamestartingtime;
7654 					}
7655 
7656 					if(!conflActivities[newtime].contains(ai2)){
7657 						conflActivities[newtime].append(ai2);
7658 						nConflActivities[newtime]++;
7659 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
7660 					}
7661 				}
7662 			}
7663 		}
7664 impossiblesamestartingtime:
7665 		if(!oksamestartingtime){
7666 			//if(updateSubgroups || updateTeachers)
7667 			//	removeAiFromNewTimetable(ai, act, d, h);
7668 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7669 
7670 			nConflActivities[newtime]=MAX_ACTIVITIES;
7671 			continue;
7672 		}
7673 
7674 /////////////////////////////////////////////////////////////////////////////////////////////
7675 
7676 		//allowed from same starting hour
7677 		oksamestartinghour=true;
7678 
7679 		for(int i=0; i<activitiesSameStartingHourActivities[ai].count(); i++){
7680 			int ai2=activitiesSameStartingHourActivities[ai].at(i);
7681 			double perc=activitiesSameStartingHourPercentages[ai].at(i);
7682 			if(c.times[ai2]!=UNALLOCATED_TIME){
7683 				bool sR=skipRandom(perc);
7684 
7685 				//if(fixedTimeActivity[ai] && perc<100.0)
7686 				//	sR=true;
7687 
7688 				if((newtime/gt.rules.nDaysPerWeek)!=(c.times[ai2]/gt.rules.nDaysPerWeek) && !sR){
7689 					if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7690 						oksamestartinghour=false;
7691 						goto impossiblesamestartinghour;
7692 					}
7693 
7694 					if(!conflActivities[newtime].contains(ai2)){
7695 						conflActivities[newtime].append(ai2);
7696 						nConflActivities[newtime]++;
7697 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
7698 					}
7699 				}
7700 			}
7701 		}
7702 impossiblesamestartinghour:
7703 		if(!oksamestartinghour){
7704 			//if(updateSubgroups || updateTeachers)
7705 			//	removeAiFromNewTimetable(ai, act, d, h);
7706 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7707 
7708 			nConflActivities[newtime]=MAX_ACTIVITIES;
7709 			continue;
7710 		}
7711 
7712 /////////////////////////////////////////////////////////////////////////////////////////////
7713 
7714 		//allowed from same starting day
7715 		oksamestartingday=true;
7716 
7717 		for(int i=0; i<activitiesSameStartingDayActivities[ai].count(); i++){
7718 			int ai2=activitiesSameStartingDayActivities[ai].at(i);
7719 			double perc=activitiesSameStartingDayPercentages[ai].at(i);
7720 			if(c.times[ai2]!=UNALLOCATED_TIME){
7721 				bool sR=skipRandom(perc);
7722 
7723 				//if(fixedTimeActivity[ai] && perc<100.0)
7724 				//	sR=true;
7725 
7726 				if((newtime%gt.rules.nDaysPerWeek)!=(c.times[ai2]%gt.rules.nDaysPerWeek) && !sR){
7727 					if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7728 						oksamestartingday=false;
7729 						goto impossiblesamestartingday;
7730 					}
7731 
7732 					if(!conflActivities[newtime].contains(ai2)){
7733 						conflActivities[newtime].append(ai2);
7734 						nConflActivities[newtime]++;
7735 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
7736 					}
7737 				}
7738 			}
7739 		}
7740 impossiblesamestartingday:
7741 		if(!oksamestartingday){
7742 			//if(updateSubgroups || updateTeachers)
7743 			//	removeAiFromNewTimetable(ai, act, d, h);
7744 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7745 
7746 			nConflActivities[newtime]=MAX_ACTIVITIES;
7747 			continue;
7748 		}
7749 
7750 /////////////////////////////////////////////////////////////////////////////////////////////
7751 
7752 		//allowed from not overlapping
7753 		oknotoverlapping=true;
7754 
7755 		for(int i=0; i<activitiesNotOverlappingActivities[ai].count(); i++){
7756 			int ai2=activitiesNotOverlappingActivities[ai].at(i);
7757 			double perc=activitiesNotOverlappingPercentages[ai].at(i);
7758 			if(c.times[ai2]!=UNALLOCATED_TIME){
7759 				int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
7760 				//int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
7761 				if(d==d2){
7762 					int st=newtime;
7763 					int en=st+gt.rules.nDaysPerWeek*act->duration;
7764 					int st2=c.times[ai2];
7765 					int en2=st2+gt.rules.nDaysPerWeek*gt.rules.internalActivitiesList[ai2].duration;
7766 
7767 					bool sR=skipRandom(perc);
7768 					//if(fixedTimeActivity[ai] && perc<100.0)
7769 					//	sR=true;
7770 
7771 					if(!(en<=st2 || en2<=st) && !sR){
7772 						assert(ai2!=ai);
7773 
7774 						if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7775 							oknotoverlapping=false;
7776 							goto impossiblenotoverlapping;
7777 						}
7778 
7779 						if(!conflActivities[newtime].contains(ai2)){
7780 							conflActivities[newtime].append(ai2);
7781 							nConflActivities[newtime]++;
7782 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
7783 						}
7784 					}
7785 				}
7786 			}
7787 		}
7788 impossiblenotoverlapping:
7789 		if(!oknotoverlapping){
7790 			//if(updateSubgroups || updateTeachers)
7791 			//	removeAiFromNewTimetable(ai, act, d, h);
7792 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7793 
7794 			nConflActivities[newtime]=MAX_ACTIVITIES;
7795 			continue;
7796 		}
7797 
7798 /////////////////////////////////////////////////////////////////////////////////////////////
7799 
7800 		//allowed from two activities consecutive
7801 		oktwoactivitiesconsecutive=true;
7802 
7803 		for(int i=0; i<constrTwoActivitiesConsecutiveActivities[ai].count(); i++){
7804 			//direct
7805 			int ai2=constrTwoActivitiesConsecutiveActivities[ai].at(i);
7806 			double perc=constrTwoActivitiesConsecutivePercentages[ai].at(i);
7807 			if(c.times[ai2]!=UNALLOCATED_TIME){
7808 				int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
7809 				int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
7810 				bool ok=true;
7811 
7812 				if(d2!=d)
7813 					ok=false;
7814 				else if(h+act->duration > h2)
7815 					ok=false;
7816 				else{
7817 					assert(d2==d);
7818 					int kk;
7819 					for(kk=h+act->duration; kk<gt.rules.nHoursPerDay; kk++)
7820 						if(!breakDayHour(d,kk))
7821 							break;
7822 					assert(kk<=h2);
7823 					if(kk!=h2)
7824 						ok=false;
7825 				}
7826 
7827 				bool sR=skipRandom(perc);
7828 				//if(fixedTimeActivity[ai] && perc<100.0)
7829 				//	sR=true;
7830 
7831 				if(!ok && !sR){
7832 					assert(ai2!=ai);
7833 
7834 					if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7835 						oktwoactivitiesconsecutive=false;
7836 						goto impossibletwoactivitiesconsecutive;
7837 					}
7838 
7839 					if(!conflActivities[newtime].contains(ai2)){
7840 						conflActivities[newtime].append(ai2);
7841 						nConflActivities[newtime]++;
7842 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
7843 					}
7844 				}
7845 			}
7846 		}
7847 
7848 		for(int i=0; i<inverseConstrTwoActivitiesConsecutiveActivities[ai].count(); i++){
7849 			//inverse
7850 			int ai2=inverseConstrTwoActivitiesConsecutiveActivities[ai].at(i);
7851 			double perc=inverseConstrTwoActivitiesConsecutivePercentages[ai].at(i);
7852 			if(c.times[ai2]!=UNALLOCATED_TIME){
7853 				int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
7854 				int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
7855 				bool ok=true;
7856 
7857 				if(d2!=d)
7858 					ok=false;
7859 				else if(h2+gt.rules.internalActivitiesList[ai2].duration > h)
7860 					ok=false;
7861 				else{
7862 					assert(d2==d);
7863 					int kk;
7864 					for(kk=h2+gt.rules.internalActivitiesList[ai2].duration; kk<gt.rules.nHoursPerDay; kk++)
7865 						if(!breakDayHour(d,kk))
7866 							break;
7867 					assert(kk<=h);
7868 					if(kk!=h)
7869 						ok=false;
7870 				}
7871 
7872 				bool sR=skipRandom(perc);
7873 				//if(fixedTimeActivity[ai] && perc<100.0)
7874 				//	sR=true;
7875 
7876 				if(!ok && !sR){
7877 					assert(ai2!=ai);
7878 
7879 					if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7880 						oktwoactivitiesconsecutive=false;
7881 						goto impossibletwoactivitiesconsecutive;
7882 					}
7883 
7884 					if(!conflActivities[newtime].contains(ai2)){
7885 						conflActivities[newtime].append(ai2);
7886 						nConflActivities[newtime]++;
7887 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
7888 					}
7889 				}
7890 			}
7891 		}
7892 
7893 impossibletwoactivitiesconsecutive:
7894 		if(!oktwoactivitiesconsecutive){
7895 			//if(updateSubgroups || updateTeachers)
7896 			//	removeAiFromNewTimetable(ai, act, d, h);
7897 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7898 
7899 			nConflActivities[newtime]=MAX_ACTIVITIES;
7900 			continue;
7901 		}
7902 
7903 /////////////////////////////////////////////////////////////////////////////////////////////
7904 
7905 		//allowed from two activities grouped
7906 		oktwoactivitiesgrouped=true;
7907 
7908 		for(int i=0; i<constrTwoActivitiesGroupedActivities[ai].count(); i++){
7909 			//direct
7910 			int ai2=constrTwoActivitiesGroupedActivities[ai].at(i);
7911 			double perc=constrTwoActivitiesGroupedPercentages[ai].at(i);
7912 			if(c.times[ai2]!=UNALLOCATED_TIME){
7913 				int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
7914 				int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
7915 				bool ok=true;
7916 
7917 				if(d2!=d){
7918 					ok=false;
7919 				}
7920 				else if(d==d2 && h2+gt.rules.internalActivitiesList[ai2].duration <= h){
7921 					int kk;
7922 					for(kk=h2+gt.rules.internalActivitiesList[ai2].duration; kk<gt.rules.nHoursPerDay; kk++)
7923 						if(!breakDayHour(d2,kk))
7924 							break;
7925 					assert(kk<=h);
7926 					if(kk!=h)
7927 						ok=false;
7928 				}
7929 				else if(d==d2 && h+act->duration <= h2){
7930 					int kk;
7931 					for(kk=h+act->duration; kk<gt.rules.nHoursPerDay; kk++)
7932 						if(!breakDayHour(d,kk))
7933 							break;
7934 					assert(kk<=h2);
7935 					if(kk!=h2)
7936 						ok=false;
7937 				}
7938 				else
7939 					ok=false;
7940 
7941 				bool sR=skipRandom(perc);
7942 				//if(fixedTimeActivity[ai] && perc<100.0)
7943 				//	sR=true;
7944 
7945 				if(!ok && !sR){
7946 					assert(ai2!=ai);
7947 
7948 					if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
7949 						oktwoactivitiesgrouped=false;
7950 						goto impossibletwoactivitiesgrouped;
7951 					}
7952 
7953 					if(!conflActivities[newtime].contains(ai2)){
7954 						conflActivities[newtime].append(ai2);
7955 						nConflActivities[newtime]++;
7956 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
7957 					}
7958 				}
7959 			}
7960 		}
7961 
7962 impossibletwoactivitiesgrouped:
7963 		if(!oktwoactivitiesgrouped){
7964 			//if(updateSubgroups || updateTeachers)
7965 			//	removeAiFromNewTimetable(ai, act, d, h);
7966 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
7967 
7968 			nConflActivities[newtime]=MAX_ACTIVITIES;
7969 			continue;
7970 		}
7971 
7972 /////////////////////////////////////////////////////////////////////////////////////////////
7973 
7974 		//allowed from three activities grouped
7975 		okthreeactivitiesgrouped=true;
7976 
7977 		for(int i=0; i<constrThreeActivitiesGroupedActivities[ai].count(); i++){
7978 			int ai2=constrThreeActivitiesGroupedActivities[ai].at(i).first;
7979 			int ai3=constrThreeActivitiesGroupedActivities[ai].at(i).second;
7980 			double perc=constrThreeActivitiesGroupedPercentages[ai].at(i);
7981 
7982 			bool sR=skipRandom(perc);
7983 			//if(fixedTimeActivity[ai] && perc<100.0)
7984 			//	sR=true;
7985 
7986 			int aip1=-1, aip2=-1; //ai placed
7987 			int ainp1=-1, ainp2=-1; //ai not placed
7988 			if(c.times[ai2]==UNALLOCATED_TIME || conflActivities[newtime].contains(ai2))
7989 				ainp1=ai2;
7990 			else
7991 				aip1=ai2;
7992 			if(c.times[ai3]==UNALLOCATED_TIME || conflActivities[newtime].contains(ai3)){
7993 				if(ainp1==-1)
7994 					ainp1=ai3;
7995 				else
7996 					ainp2=ai3;
7997 			}
7998 			else{
7999 				if(aip1==-1)
8000 					aip1=ai3;
8001 				else
8002 					aip2=ai3;
8003 			}
8004 
8005 			int cnt=0;
8006 			if(ainp1>=0)
8007 				cnt++;
8008 			if(ainp2>=0)
8009 				cnt++;
8010 			if(aip1>=0)
8011 				cnt++;
8012 			if(aip2>=0)
8013 				cnt++;
8014 			assert(cnt==2);
8015 
8016 			bool ok;
8017 
8018 			if(aip1==-1){
8019 				//ok - both not placed
8020 				ok=true;
8021 			}
8022 			else if(aip2==-1){
8023 				//only one placed, one not placed
8024 				int dp1=c.times[aip1]%gt.rules.nDaysPerWeek;
8025 				int hp1=c.times[aip1]/gt.rules.nDaysPerWeek;
8026 				int durp1=gt.rules.internalActivitiesList[aip1].duration;
8027 
8028 				int hoursBetweenThem=-1;
8029 
8030 				if(dp1!=d)
8031 					hoursBetweenThem=-1;
8032 				else if(dp1==d && h >= hp1+durp1){
8033 					hoursBetweenThem=0;
8034 					for(int kk=hp1+durp1; kk<h; kk++)
8035 						if(!breakDayHour(d,kk)){
8036 							//check that the working hours are not separated by a break
8037 							//assertion that durp1>0, so the kk-1 >= 0
8038 							if(breakDayHour(d,kk-1) && hoursBetweenThem>0){
8039 								hoursBetweenThem=-1;
8040 								break;
8041 							}
8042 
8043 							hoursBetweenThem++;
8044 						}
8045 				}
8046 				else if(dp1==d && hp1 >= h+act->duration){
8047 					hoursBetweenThem=0;
8048 					for(int kk=h+act->duration; kk<hp1; kk++)
8049 						if(!breakDayHour(d,kk)){
8050 							//check that the working hours are not separated by a break
8051 							//assertion that act->duration>0, so the kk-1 >= 0
8052 							if(breakDayHour(d,kk-1) && hoursBetweenThem>0){
8053 								hoursBetweenThem=-1;
8054 								break;
8055 							}
8056 
8057 							hoursBetweenThem++;
8058 						}
8059 				}
8060 				else
8061 					hoursBetweenThem=-1;
8062 
8063 				assert(ainp1>=0);
8064 				if(hoursBetweenThem==0 || hoursBetweenThem==gt.rules.internalActivitiesList[ainp1].duration)
8065 					//OK
8066 					ok=true;
8067 				else
8068 					//not OK
8069 					ok=false;
8070 			}
8071 			else{
8072 				assert(aip1>=0 && aip2>=0);
8073 				//both placed
8074 				int dp1=c.times[aip1]%gt.rules.nDaysPerWeek;
8075 				int hp1=c.times[aip1]/gt.rules.nDaysPerWeek;
8076 				//int durp1=gt.rules.internalActivitiesList[aip1].duration;
8077 
8078 				int dp2=c.times[aip2]%gt.rules.nDaysPerWeek;
8079 				int hp2=c.times[aip2]/gt.rules.nDaysPerWeek;
8080 				//int durp2=gt.rules.internalActivitiesList[aip2].duration;
8081 
8082 				if(dp1==dp2 && dp1==d){
8083 					int ao1=-1, ao2=-1, ao3=-1; //order them, 1 then 2 then 3
8084 					if(h>=hp1 && h>=hp2 && hp2>=hp1){
8085 						ao1=aip1;
8086 						ao2=aip2;
8087 						ao3=ai;
8088 					}
8089 					else if(h>=hp1 && h>=hp2 && hp1>=hp2){
8090 						ao1=aip2;
8091 						ao2=aip1;
8092 						ao3=ai;
8093 					}
8094 					else if(hp1>=h && hp1>=hp2 && h>=hp2){
8095 						ao1=aip2;
8096 						ao2=ai;
8097 						ao3=aip1;
8098 					}
8099 					else if(hp1>=h && hp1>=hp2 && hp2>=h){
8100 						ao1=ai;
8101 						ao2=aip2;
8102 						ao3=aip1;
8103 					}
8104 					else if(hp2>=h && hp2>=hp1 && h>=hp1){
8105 						ao1=aip1;
8106 						ao2=ai;
8107 						ao3=aip2;
8108 					}
8109 					else if(hp2>=h && hp2>=hp1 && hp1>=h){
8110 						ao1=ai;
8111 						ao2=aip1;
8112 						ao3=aip2;
8113 					}
8114 					else
8115 						assert(0);
8116 
8117 					int do1;
8118 					int ho1;
8119 					int duro1;
8120 
8121 					int do2;
8122 					int ho2;
8123 					int duro2;
8124 
8125 					int do3;
8126 					int ho3;
8127 					//int duro3;
8128 
8129 					if(ao1==ai){
8130 						do1=d;
8131 						ho1=h;
8132 						duro1=act->duration;
8133 					}
8134 					else{
8135 						do1=c.times[ao1]%gt.rules.nDaysPerWeek;
8136 						ho1=c.times[ao1]/gt.rules.nDaysPerWeek;
8137 						duro1=gt.rules.internalActivitiesList[ao1].duration;
8138 					}
8139 
8140 					if(ao2==ai){
8141 						do2=d;
8142 						ho2=h;
8143 						duro2=act->duration;
8144 					}
8145 					else{
8146 						do2=c.times[ao2]%gt.rules.nDaysPerWeek;
8147 						ho2=c.times[ao2]/gt.rules.nDaysPerWeek;
8148 						duro2=gt.rules.internalActivitiesList[ao2].duration;
8149 					}
8150 
8151 					if(ao3==ai){
8152 						do3=d;
8153 						ho3=h;
8154 						//duro3=act->duration;
8155 					}
8156 					else{
8157 						do3=c.times[ao3]%gt.rules.nDaysPerWeek;
8158 						ho3=c.times[ao3]/gt.rules.nDaysPerWeek;
8159 						//duro3=gt.rules.internalActivitiesList[ao3].duration;
8160 					}
8161 
8162 					assert(do1==do2 && do1==do3);
8163 					if(ho1+duro1<=ho2 && ho2+duro2<=ho3){
8164 						int hoursBetweenThem=0;
8165 
8166 						for(int kk=ho1+duro1; kk<ho2; kk++)
8167 							if(!breakDayHour(d,kk))
8168 								hoursBetweenThem++;
8169 						for(int kk=ho2+duro2; kk<ho3; kk++)
8170 							if(!breakDayHour(d,kk))
8171 								hoursBetweenThem++;
8172 
8173 						if(hoursBetweenThem==0)
8174 							ok=true;
8175 						else
8176 							ok=false;
8177 					}
8178 					else{
8179 						//not OK
8180 						ok=false;
8181 					}
8182 				}
8183 				else{
8184 					//not OK
8185 					ok=false;
8186 				}
8187 			}
8188 
8189 			bool again;//=false;
8190 
8191 			if(!ok && !sR){
8192 				int aidisplaced=-1;
8193 
8194 				if(aip2>=0){ //two placed activities
8195 					again=true;
8196 
8197 					QList<int> acts;
8198 
8199 					if(!fixedTimeActivity[aip1] && !swappedActivities[aip1])
8200 						acts.append(aip1);
8201 					if(!fixedTimeActivity[aip2] && !swappedActivities[aip2])
8202 						acts.append(aip2);
8203 
8204 					if(acts.count()==0)
8205 						aidisplaced=-1;
8206 					else if(acts.count()==1)
8207 						aidisplaced=acts.at(0);
8208 					else{
8209 						int t;
8210 						if(level==0){
8211 							int optMinWrong=INF;
8212 
8213 							QList<int> tl;
8214 
8215 							for(int q=0; q<acts.count(); q++){
8216 								int tta=acts.at(q);
8217 								if(optMinWrong>triedRemovals(tta,c.times[tta])){
8218 								 	optMinWrong=triedRemovals(tta,c.times[tta]);
8219 								}
8220 							}
8221 
8222 							for(int q=0; q<acts.count(); q++){
8223 								int tta=acts.at(q);
8224 								if(optMinWrong==triedRemovals(tta,c.times[tta]))
8225 									tl.append(q);
8226 							}
8227 
8228 							assert(tl.count()>=1);
8229 							int mpos=tl.at(rng.intMRG32k3a(tl.count()));
8230 
8231 							assert(mpos>=0 && mpos<acts.count());
8232 							t=mpos;
8233 						}
8234 						else{
8235 							t=rng.intMRG32k3a(acts.count());
8236 						}
8237 
8238 						aidisplaced=acts.at(t);
8239 					}
8240 				}
8241 				else{
8242 					again=false;
8243 					assert(aip1>=0);
8244 					if(!fixedTimeActivity[aip1] && !swappedActivities[aip1])
8245 						aidisplaced=aip1;
8246 					else
8247 						aidisplaced=-1;
8248 				}
8249 
8250 				assert(aidisplaced!=ai);
8251 
8252 				if(aidisplaced==-1){
8253 					okthreeactivitiesgrouped=false;
8254 					goto impossiblethreeactivitiesgrouped;
8255 				}
8256 				if(fixedTimeActivity[aidisplaced] || swappedActivities[aidisplaced]){
8257 					okthreeactivitiesgrouped=false;
8258 					goto impossiblethreeactivitiesgrouped;
8259 				}
8260 
8261 				assert(!conflActivities[newtime].contains(aidisplaced));
8262 				conflActivities[newtime].append(aidisplaced);
8263 				nConflActivities[newtime]++;
8264 				assert(conflActivities[newtime].count()==nConflActivities[newtime]);
8265 
8266 				//if !again, everything is OK, because there was one placed activity and it was eliminated
8267 
8268 				if(again){
8269 					aip1=-1, aip2=-1;
8270 					ainp1=-1, ainp2=-1;
8271 					if(c.times[ai2]==UNALLOCATED_TIME || conflActivities[newtime].contains(ai2))
8272 						ainp1=ai2;
8273 					else
8274 						aip1=ai2;
8275 					if(c.times[ai3]==UNALLOCATED_TIME || conflActivities[newtime].contains(ai3)){
8276 						if(ainp1==-1)
8277 							ainp1=ai3;
8278 						else
8279 							ainp2=ai3;
8280 					}
8281 					else{
8282 						if(aip1==-1)
8283 							aip1=ai3;
8284 						else
8285 							aip2=ai3;
8286 					}
8287 
8288 					assert(aip1>=0 && ainp1>=0 && aip2==-1 && ainp2==-1); //only one placed
8289 
8290 					//again the procedure from above, with 1 placed
8291 					int dp1=c.times[aip1]%gt.rules.nDaysPerWeek;
8292 					int hp1=c.times[aip1]/gt.rules.nDaysPerWeek;
8293 					int durp1=gt.rules.internalActivitiesList[aip1].duration;
8294 
8295 					int hoursBetweenThem=-1;
8296 
8297 					if(dp1!=d)
8298 						hoursBetweenThem=-1;
8299 					else if(dp1==d && h >= hp1+durp1){
8300 						hoursBetweenThem=0;
8301 						for(int kk=hp1+durp1; kk<h; kk++)
8302 							if(!breakDayHour(d,kk)){
8303 								//check that the working hours are not separated by a break
8304 								//assertion that durp1>0, so the kk-1 >= 0
8305 								if(breakDayHour(d,kk-1) && hoursBetweenThem>0){
8306 									hoursBetweenThem=-1;
8307 									break;
8308 								}
8309 
8310 								hoursBetweenThem++;
8311 							}
8312 					}
8313 					else if(dp1==d && hp1 >= h+act->duration){
8314 						hoursBetweenThem=0;
8315 						for(int kk=h+act->duration; kk<hp1; kk++)
8316 							if(!breakDayHour(d,kk)){
8317 								//check that the working hours are not separated by a break
8318 								//assertion that act->duration>0, so the kk-1 >= 0
8319 								if(breakDayHour(d,kk-1) && hoursBetweenThem>0){
8320 									hoursBetweenThem=-1;
8321 									break;
8322 								}
8323 
8324 								hoursBetweenThem++;
8325 							}
8326 					}
8327 					else
8328 						hoursBetweenThem=-1;
8329 
8330 					assert(ainp1>=0);
8331 					if(hoursBetweenThem==0 || hoursBetweenThem==gt.rules.internalActivitiesList[ainp1].duration)
8332 						//OK
8333 						ok=true;
8334 					else
8335 						//not OK
8336 						ok=false;
8337 
8338 					assert(!sR);
8339 					if(!ok){
8340 						aidisplaced=aip1;
8341 						if(fixedTimeActivity[aidisplaced] || swappedActivities[aidisplaced]){
8342 							okthreeactivitiesgrouped=false;
8343 							goto impossiblethreeactivitiesgrouped;
8344 						}
8345 
8346 						assert(!conflActivities[newtime].contains(aidisplaced));
8347 						conflActivities[newtime].append(aidisplaced);
8348 						nConflActivities[newtime]++;
8349 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
8350 
8351 						//now it is OK, because there were two activities placed and both were eliminated
8352 					}
8353 				} //end if(again)
8354 			}
8355 		}
8356 
8357 impossiblethreeactivitiesgrouped:
8358 		if(!okthreeactivitiesgrouped){
8359 			//if(updateSubgroups || updateTeachers)
8360 			//	removeAiFromNewTimetable(ai, act, d, h);
8361 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
8362 
8363 			nConflActivities[newtime]=MAX_ACTIVITIES;
8364 			continue;
8365 		}
8366 
8367 /////////////////////////////////////////////////////////////////////////////////////////////
8368 
8369 		//allowed from two activities ordered
8370 		oktwoactivitiesordered=true;
8371 
8372 		for(int i=0; i<constrTwoActivitiesOrderedActivities[ai].count(); i++){
8373 			//direct
8374 			int ai2=constrTwoActivitiesOrderedActivities[ai].at(i);
8375 			double perc=constrTwoActivitiesOrderedPercentages[ai].at(i);
8376 			if(c.times[ai2]!=UNALLOCATED_TIME){
8377 				int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
8378 				int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
8379 				bool ok=true;
8380 
8381 				if(!(d<d2 || (d==d2 && h+act->duration-1<h2)))
8382 					ok=false;
8383 
8384 				bool sR=skipRandom(perc);
8385 				//if(fixedTimeActivity[ai] && perc<100.0)
8386 				//	sR=true;
8387 
8388 				if(!ok && !sR){
8389 					assert(ai2!=ai);
8390 
8391 					if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
8392 						oktwoactivitiesordered=false;
8393 						goto impossibletwoactivitiesordered;
8394 					}
8395 
8396 					if(!conflActivities[newtime].contains(ai2)){
8397 						conflActivities[newtime].append(ai2);
8398 						nConflActivities[newtime]++;
8399 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
8400 					}
8401 				}
8402 			}
8403 		}
8404 
8405 		for(int i=0; i<inverseConstrTwoActivitiesOrderedActivities[ai].count(); i++){
8406 			//inverse
8407 			int ai2=inverseConstrTwoActivitiesOrderedActivities[ai].at(i);
8408 			double perc=inverseConstrTwoActivitiesOrderedPercentages[ai].at(i);
8409 			if(c.times[ai2]!=UNALLOCATED_TIME){
8410 				int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
8411 				int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
8412 				int dur2=gt.rules.internalActivitiesList[ai2].duration;
8413 				bool ok=true;
8414 
8415 				if(!(d2<d || (d2==d && h2+dur2-1<h)))
8416 					ok=false;
8417 
8418 				bool sR=skipRandom(perc);
8419 				//if(fixedTimeActivity[ai] && perc<100.0)
8420 				//	sR=true;
8421 
8422 				if(!ok && !sR){
8423 					assert(ai2!=ai);
8424 
8425 					if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
8426 						oktwoactivitiesordered=false;
8427 						goto impossibletwoactivitiesordered;
8428 					}
8429 
8430 					if(!conflActivities[newtime].contains(ai2)){
8431 						conflActivities[newtime].append(ai2);
8432 						nConflActivities[newtime]++;
8433 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
8434 					}
8435 				}
8436 			}
8437 		}
8438 
8439 impossibletwoactivitiesordered:
8440 		if(!oktwoactivitiesordered){
8441 			//if(updateSubgroups || updateTeachers)
8442 			//	removeAiFromNewTimetable(ai, act, d, h);
8443 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
8444 
8445 			nConflActivities[newtime]=MAX_ACTIVITIES;
8446 			continue;
8447 		}
8448 
8449 /////////////////////////////////////////////////////////////////////////////////////////////
8450 
8451 		//allowed from two activities ordered if same day
8452 		oktwoactivitiesorderedifsameday=true;
8453 
8454 		for(int i=0; i<constrTwoActivitiesOrderedIfSameDayActivities[ai].count(); i++){
8455 			//direct
8456 			int ai2=constrTwoActivitiesOrderedIfSameDayActivities[ai].at(i);
8457 			double perc=constrTwoActivitiesOrderedIfSameDayPercentages[ai].at(i);
8458 			if(c.times[ai2]!=UNALLOCATED_TIME){
8459 				int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
8460 				int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
8461 				bool ok=true;
8462 
8463 				if(!(d!=d2 || (d==d2 && h+act->duration-1<h2)))
8464 					ok=false;
8465 
8466 				bool sR=skipRandom(perc);
8467 				//if(fixedTimeActivity[ai] && perc<100.0)
8468 				//	sR=true;
8469 
8470 				if(!ok && !sR){
8471 					assert(ai2!=ai);
8472 
8473 					if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
8474 						oktwoactivitiesorderedifsameday=false;
8475 						goto impossibletwoactivitiesorderedifsameday;
8476 					}
8477 
8478 					if(!conflActivities[newtime].contains(ai2)){
8479 						conflActivities[newtime].append(ai2);
8480 						nConflActivities[newtime]++;
8481 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
8482 					}
8483 				}
8484 			}
8485 		}
8486 
8487 		for(int i=0; i<inverseConstrTwoActivitiesOrderedIfSameDayActivities[ai].count(); i++){
8488 			//inverse
8489 			int ai2=inverseConstrTwoActivitiesOrderedIfSameDayActivities[ai].at(i);
8490 			double perc=inverseConstrTwoActivitiesOrderedIfSameDayPercentages[ai].at(i);
8491 			if(c.times[ai2]!=UNALLOCATED_TIME){
8492 				int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
8493 				int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
8494 				int dur2=gt.rules.internalActivitiesList[ai2].duration;
8495 				bool ok=true;
8496 
8497 				if(!(d2!=d || (d2==d && h2+dur2-1<h)))
8498 					ok=false;
8499 
8500 				bool sR=skipRandom(perc);
8501 				//if(fixedTimeActivity[ai] && perc<100.0)
8502 				//	sR=true;
8503 
8504 				if(!ok && !sR){
8505 					assert(ai2!=ai);
8506 
8507 					if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
8508 						oktwoactivitiesorderedifsameday=false;
8509 						goto impossibletwoactivitiesorderedifsameday;
8510 					}
8511 
8512 					if(!conflActivities[newtime].contains(ai2)){
8513 						conflActivities[newtime].append(ai2);
8514 						nConflActivities[newtime]++;
8515 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
8516 					}
8517 				}
8518 			}
8519 		}
8520 
8521 impossibletwoactivitiesorderedifsameday:
8522 		if(!oktwoactivitiesorderedifsameday){
8523 			//if(updateSubgroups || updateTeachers)
8524 			//	removeAiFromNewTimetable(ai, act, d, h);
8525 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
8526 
8527 			nConflActivities[newtime]=MAX_ACTIVITIES;
8528 			continue;
8529 		}
8530 
8531 /////////////////////////////////////////////////////////////////////////////////////////////
8532 
8533 		//allowed from activity ends students day
8534 		okactivityendsstudentsday=true;
8535 
8536 		if(haveActivityEndsStudentsDay){
8537 			//1. If current activity needs to be at the end
8538 			if(activityEndsStudentsDayPercentages[ai]>=0){
8539 				bool skip=skipRandom(activityEndsStudentsDayPercentages[ai]);
8540 				if(!skip){
8541 					for(int sb : qAsConst(act->iSubgroupsList)){
8542 						for(int hh=h+act->duration; hh<gt.rules.nHoursPerDay; hh++){
8543 							int ai2=subgroupsTimetable(sb,d,hh);
8544 							if(ai2>=0){
8545 								if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
8546 									okactivityendsstudentsday=false;
8547 									goto impossibleactivityendsstudentsday;
8548 								}
8549 
8550 								if(!conflActivities[newtime].contains(ai2)){
8551 									conflActivities[newtime].append(ai2);
8552 									nConflActivities[newtime]++;
8553 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
8554 								}
8555 							}
8556 						}
8557 					}
8558 				}
8559 			}
8560 
8561 			//2. Check activities which have to be at the end, in the same day with current activity
8562 			for(int sb : qAsConst(act->iSubgroupsList)){
8563 				for(int hh=h-1; hh>=0; hh--){
8564 					int ai2=subgroupsTimetable(sb,d,hh);
8565 					if(ai2>=0)
8566 						if(activityEndsStudentsDayPercentages[ai2]>=0){
8567 							bool skip=skipRandom(activityEndsStudentsDayPercentages[ai2]);
8568 							if(!skip){
8569 								if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
8570 									okactivityendsstudentsday=false;
8571 									goto impossibleactivityendsstudentsday;
8572 								}
8573 
8574 								if(!conflActivities[newtime].contains(ai2)){
8575 									conflActivities[newtime].append(ai2);
8576 									nConflActivities[newtime]++;
8577 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
8578 								}
8579 							}
8580 						}
8581 				}
8582 			}
8583 		}
8584 
8585 impossibleactivityendsstudentsday:
8586 		if(!okactivityendsstudentsday){
8587 			//if(updateSubgroups || updateTeachers)
8588 			//	removeAiFromNewTimetable(ai, act, d, h);
8589 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
8590 
8591 			nConflActivities[newtime]=MAX_ACTIVITIES;
8592 			continue;
8593 		}
8594 
8595 /////////////////////////////////////////////////////////////////////////////////////////////
8596 
8597 		//allowed from activity ends teachers day
8598 		okactivityendsteachersday=true;
8599 
8600 		if(haveActivityEndsTeachersDay){
8601 			//1. If current activity needs to be at the end
8602 			if(activityEndsTeachersDayPercentages[ai]>=0){
8603 				bool skip=skipRandom(activityEndsTeachersDayPercentages[ai]);
8604 				if(!skip){
8605 					for(int tch : qAsConst(act->iTeachersList)){
8606 						for(int hh=h+act->duration; hh<gt.rules.nHoursPerDay; hh++){
8607 							int ai2=teachersTimetable(tch,d,hh);
8608 							if(ai2>=0){
8609 								if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
8610 									okactivityendsteachersday=false;
8611 									goto impossibleactivityendsteachersday;
8612 								}
8613 
8614 								if(!conflActivities[newtime].contains(ai2)){
8615 									conflActivities[newtime].append(ai2);
8616 									nConflActivities[newtime]++;
8617 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
8618 								}
8619 							}
8620 						}
8621 					}
8622 				}
8623 			}
8624 
8625 			//2. Check activities which have to be at the end, in the same day with current activity
8626 			for(int tch : qAsConst(act->iTeachersList)){
8627 				for(int hh=h-1; hh>=0; hh--){
8628 					int ai2=teachersTimetable(tch,d,hh);
8629 					if(ai2>=0)
8630 						if(activityEndsTeachersDayPercentages[ai2]>=0){
8631 							bool skip=skipRandom(activityEndsTeachersDayPercentages[ai2]);
8632 							if(!skip){
8633 								if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
8634 									okactivityendsteachersday=false;
8635 									goto impossibleactivityendsteachersday;
8636 								}
8637 
8638 								if(!conflActivities[newtime].contains(ai2)){
8639 									conflActivities[newtime].append(ai2);
8640 									nConflActivities[newtime]++;
8641 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
8642 								}
8643 							}
8644 						}
8645 				}
8646 			}
8647 		}
8648 
8649 impossibleactivityendsteachersday:
8650 		if(!okactivityendsteachersday){
8651 			//if(updateSubgroups || updateTeachers)
8652 			//	removeAiFromNewTimetable(ai, act, d, h);
8653 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
8654 
8655 			nConflActivities[newtime]=MAX_ACTIVITIES;
8656 			continue;
8657 		}
8658 
8659 /////////////////////////////////////////////////////////////////////////////////////////////
8660 
8661 /////////////////////////////////////////////////////////
8662 
8663 		int dpair;
8664 
8665 		dpair=-1;
8666 
8667 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
8668 			if(d%2==0)
8669 				dpair=d+1;
8670 			else
8671 				dpair=d-1;
8672 			assert(dpair>=0 && dpair<gt.rules.nDaysPerWeek);
8673 		}
8674 
8675 		///Teachers must not be in both days 1&2, both 3&4, and so on, but with the exceptions of special teachers
8676 
8677 		okteachersmorningsafternoonsbehavior=true;
8678 
8679 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
8680 			for(int tc : qAsConst(act->iTeachersList)){
8681 				int morningsAfternoonsBehavior=gt.rules.internalTeachersList[tc]->morningsAfternoonsBehavior;
8682 
8683 				if(morningsAfternoonsBehavior==TEACHER_ONE_DAY_EXCEPTION)
8684 				// if(gt.rules.internalExceptionTeachersSet.contains(tc))
8685 				{
8686 					QList<int> pairedCurrentDay;
8687 
8688 					for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8689 						if(teachersTimetable(tc,dpair,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dpair,hh)))
8690 							pairedCurrentDay.append(teachersTimetable(tc,dpair,hh));
8691 
8692 					if(pairedCurrentDay.count()==0)
8693 						continue;
8694 
8695 					QList<int> morningOther;
8696 					QList<int> afternoonOther;
8697 
8698 					int dother=-1;
8699 					for(int dd=0; dd<gt.rules.nDaysPerWeek/2; dd++){
8700 						bool morning=false, afternoon=false;
8701 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8702 							if(teachersTimetable(tc,dd*2,hh)>=0)
8703 								morning=true;
8704 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8705 							if(teachersTimetable(tc,dd*2+1,hh)>=0)
8706 								afternoon=true;
8707 						if(dd!=d/2 && morning && afternoon){
8708 							assert(dd!=d/2);
8709 							assert(dother==-1);
8710 							dother=dd;
8711 						}
8712 					}
8713 					if(dother>=0){
8714 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8715 							if(teachersTimetable(tc,dother*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother*2,hh)))
8716 								morningOther.append(teachersTimetable(tc,dother*2,hh));
8717 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8718 							if(teachersTimetable(tc,dother*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother*2+1,hh)))
8719 								afternoonOther.append(teachersTimetable(tc,dother*2+1,hh));
8720 					}
8721 
8722 					assert(pairedCurrentDay.count()>0);
8723 					if(morningOther.count()>0 && afternoonOther.count()>0){
8724 						//empty one of these three
8725 
8726 						///////////////////////////////////
8727 						//0 is paired current day, 1 is morning other, 2 is afternoon other
8728 
8729 						bool occupiedDay[3];
8730 						bool canEmptyDay[3];
8731 
8732 						int _minWrong[3];
8733 						int _nWrong[3];
8734 						int _nConflActivities[3];
8735 						int _minIndexAct[3];
8736 
8737 						QList<int> _activitiesForDay[3];
8738 
8739 						for(int d2=0; d2<3; d2++){
8740 							occupiedDay[d2]=false;
8741 							canEmptyDay[d2]=true;
8742 
8743 							_minWrong[d2]=INF;
8744 							_nWrong[d2]=0;
8745 							_nConflActivities[d2]=0;
8746 							_minIndexAct[d2]=gt.rules.nInternalActivities;
8747 							_activitiesForDay[d2].clear();
8748 
8749 							QList<int> tmp;
8750 							if(d2==0)
8751 								tmp=pairedCurrentDay;
8752 							else if(d2==1)
8753 								tmp=morningOther;
8754 							else if(d2==2)
8755 								tmp=afternoonOther;
8756 
8757 							for(int ai2 : qAsConst(tmp)){
8758 								assert(ai2>=0);
8759 								if(ai2>=0){
8760 									if(!conflActivities[newtime].contains(ai2)){
8761 										occupiedDay[d2]=true;
8762 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
8763 											canEmptyDay[d2]=false;
8764 										else if(!_activitiesForDay[d2].contains(ai2)){
8765 											_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
8766 											_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
8767 											_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
8768 											_nConflActivities[d2]++;
8769 											_activitiesForDay[d2].append(ai2);
8770 											assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
8771 										}
8772 									}
8773 								}
8774 							}
8775 
8776 							assert(occupiedDay[d2]);
8777 							if(!occupiedDay[d2])
8778 								canEmptyDay[d2]=false;
8779 						}
8780 
8781 						int nOc=0;
8782 						bool canChooseDay=false;
8783 
8784 						for(int j=0; j<3; j++)
8785 							if(occupiedDay[j]){
8786 								nOc++;
8787 								if(canEmptyDay[j]){
8788 									canChooseDay=true;
8789 								}
8790 							}
8791 
8792 						if(!canChooseDay){
8793 							if(level==0){
8794 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
8795 							}
8796 							okteachersmorningsafternoonsbehavior=false;
8797 							goto impossibleteachersmorningsafternoonsbehavior;
8798 						}
8799 
8800 						int d2=-1;
8801 
8802 						if(level!=0){
8803 							//choose random day from those with minimum number of conflicting activities
8804 							QList<int> candidateDays;
8805 
8806 							int m=gt.rules.nInternalActivities;
8807 
8808 							for(int kk=0; kk<3; kk++)
8809 								if(canEmptyDay[kk])
8810 									if(m>_nConflActivities[kk])
8811 										m=_nConflActivities[kk];
8812 
8813 							candidateDays.clear();
8814 							for(int kk=0; kk<3; kk++)
8815 								if(canEmptyDay[kk] && m==_nConflActivities[kk])
8816 									candidateDays.append(kk);
8817 
8818 							assert(candidateDays.count()>0);
8819 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
8820 						}
8821 						else{ //level==0
8822 							QList<int> candidateDays;
8823 
8824 							int _mW=INF;
8825 							int _nW=INF;
8826 							int _mCA=gt.rules.nInternalActivities;
8827 							int _mIA=gt.rules.nInternalActivities;
8828 
8829 							for(int kk=0; kk<3; kk++)
8830 								if(canEmptyDay[kk]){
8831 									if(_mW>_minWrong[kk] ||
8832 									(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
8833 									(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
8834 									(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
8835 										_mW=_minWrong[kk];
8836 										_nW=_nWrong[kk];
8837 										_mCA=_nConflActivities[kk];
8838 										_mIA=_minIndexAct[kk];
8839 									}
8840 								}
8841 
8842 							assert(_mW<INF);
8843 
8844 							candidateDays.clear();
8845 							for(int kk=0; kk<3; kk++)
8846 								if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
8847 									candidateDays.append(kk);
8848 
8849 							assert(candidateDays.count()>0);
8850 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
8851 						}
8852 
8853 						assert(d2>=0);
8854 
8855 						assert(_activitiesForDay[d2].count()>0);
8856 
8857 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
8858 							assert(ai2!=ai);
8859 							assert(!swappedActivities[ai2]);
8860 							assert(!fixedTimeActivity[ai2]);
8861 							assert(!conflActivities[newtime].contains(ai2));
8862 							conflActivities[newtime].append(ai2);
8863 							nConflActivities[newtime]++;
8864 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
8865 						}
8866 					}
8867 				}
8868 				else if(morningsAfternoonsBehavior==TEACHER_TWO_DAYS_EXCEPTION)
8869 				//else if(gt.rules.internalExceptionTeachers2Set.contains(tc))
8870 				{
8871 					QList<int> pairedCurrentDay;
8872 
8873 					for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8874 						if(teachersTimetable(tc,dpair,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dpair,hh)))
8875 							pairedCurrentDay.append(teachersTimetable(tc,dpair,hh));
8876 
8877 					if(pairedCurrentDay.count()==0)
8878 						continue;
8879 
8880 					QList<int> morningOther;
8881 					QList<int> afternoonOther;
8882 					QList<int> morningOther2;
8883 					QList<int> afternoonOther2;
8884 
8885 					int dother=-1, dother2=-1;
8886 					for(int dd=0; dd<gt.rules.nDaysPerWeek/2; dd++){
8887 						bool morning=false, afternoon=false;
8888 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8889 							if(teachersTimetable(tc,dd*2,hh)>=0)
8890 								morning=true;
8891 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8892 							if(teachersTimetable(tc,dd*2+1,hh)>=0)
8893 								afternoon=true;
8894 						if(dd!=d/2 && morning && afternoon){
8895 							assert(dd!=d/2);
8896 							if(dother==-1){
8897 								assert(dother==-1);
8898 								dother=dd;
8899 							}
8900 							else{
8901 								dother2=dd;
8902 							}
8903 						}
8904 					}
8905 					if(dother>=0){
8906 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8907 							if(teachersTimetable(tc,dother*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother*2,hh)))
8908 								morningOther.append(teachersTimetable(tc,dother*2,hh));
8909 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8910 							if(teachersTimetable(tc,dother*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother*2+1,hh)))
8911 								afternoonOther.append(teachersTimetable(tc,dother*2+1,hh));
8912 					}
8913 					if(dother2>=0){
8914 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8915 							if(teachersTimetable(tc,dother2*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother2*2,hh)))
8916 								morningOther2.append(teachersTimetable(tc,dother2*2,hh));
8917 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
8918 							if(teachersTimetable(tc,dother2*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother2*2+1,hh)))
8919 								afternoonOther2.append(teachersTimetable(tc,dother2*2+1,hh));
8920 					}
8921 
8922 					assert(pairedCurrentDay.count()>0);
8923 					if(morningOther.count()>0 && afternoonOther.count()>0 && morningOther2.count()>0 && afternoonOther2.count()>0){
8924 						//empty one of these 5
8925 
8926 						///////////////////////////////////
8927 						//0 is paired current day, 1 is morning other, 2 is afternoon other
8928 						//3 is morning other 2, 4 is afternoon other 2
8929 
8930 						bool occupiedDay[3+2];
8931 						bool canEmptyDay[3+2];
8932 
8933 						int _minWrong[3+2];
8934 						int _nWrong[3+2];
8935 						int _nConflActivities[3+2];
8936 						int _minIndexAct[3+2];
8937 
8938 						QList<int> _activitiesForDay[3+2];
8939 
8940 						for(int d2=0; d2<3+2; d2++){
8941 							occupiedDay[d2]=false;
8942 							canEmptyDay[d2]=true;
8943 
8944 							_minWrong[d2]=INF;
8945 							_nWrong[d2]=0;
8946 							_nConflActivities[d2]=0;
8947 							_minIndexAct[d2]=gt.rules.nInternalActivities;
8948 							_activitiesForDay[d2].clear();
8949 
8950 							QList<int> tmp;
8951 							if(d2==0)
8952 								tmp=pairedCurrentDay;
8953 							else if(d2==1)
8954 								tmp=morningOther;
8955 							else if(d2==2)
8956 								tmp=afternoonOther;
8957 							else if(d2==3)
8958 								tmp=morningOther2;
8959 							else if(d2==4)
8960 								tmp=afternoonOther2;
8961 
8962 							for(int ai2 : qAsConst(tmp)){
8963 								assert(ai2>=0);
8964 								if(ai2>=0){
8965 									if(!conflActivities[newtime].contains(ai2)){
8966 										occupiedDay[d2]=true;
8967 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
8968 											canEmptyDay[d2]=false;
8969 										else if(!_activitiesForDay[d2].contains(ai2)){
8970 											_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
8971 											_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
8972 											_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
8973 											_nConflActivities[d2]++;
8974 											_activitiesForDay[d2].append(ai2);
8975 											assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
8976 										}
8977 									}
8978 								}
8979 							}
8980 
8981 							assert(occupiedDay[d2]);
8982 							if(!occupiedDay[d2])
8983 								canEmptyDay[d2]=false;
8984 						}
8985 
8986 						int nOc=0;
8987 						bool canChooseDay=false;
8988 
8989 						for(int j=0; j<3+2; j++)
8990 							if(occupiedDay[j]){
8991 								nOc++;
8992 								if(canEmptyDay[j]){
8993 									canChooseDay=true;
8994 								}
8995 							}
8996 
8997 						if(!canChooseDay){
8998 							if(level==0){
8999 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
9000 							}
9001 							okteachersmorningsafternoonsbehavior=false;
9002 							goto impossibleteachersmorningsafternoonsbehavior;
9003 						}
9004 
9005 						int d2=-1;
9006 
9007 						if(level!=0){
9008 							//choose random day from those with minimum number of conflicting activities
9009 							QList<int> candidateDays;
9010 
9011 							int m=gt.rules.nInternalActivities;
9012 
9013 							for(int kk=0; kk<3+2; kk++)
9014 								if(canEmptyDay[kk])
9015 									if(m>_nConflActivities[kk])
9016 										m=_nConflActivities[kk];
9017 
9018 							candidateDays.clear();
9019 							for(int kk=0; kk<3+2; kk++)
9020 								if(canEmptyDay[kk] && m==_nConflActivities[kk])
9021 									candidateDays.append(kk);
9022 
9023 							assert(candidateDays.count()>0);
9024 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
9025 						}
9026 						else{ //level==0
9027 							QList<int> candidateDays;
9028 
9029 							int _mW=INF;
9030 							int _nW=INF;
9031 							int _mCA=gt.rules.nInternalActivities;
9032 							int _mIA=gt.rules.nInternalActivities;
9033 
9034 							for(int kk=0; kk<3+2; kk++)
9035 								if(canEmptyDay[kk]){
9036 									if(_mW>_minWrong[kk] ||
9037 									(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
9038 									(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
9039 									(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
9040 										_mW=_minWrong[kk];
9041 										_nW=_nWrong[kk];
9042 										_mCA=_nConflActivities[kk];
9043 										_mIA=_minIndexAct[kk];
9044 									}
9045 								}
9046 
9047 							assert(_mW<INF);
9048 
9049 							candidateDays.clear();
9050 							for(int kk=0; kk<3+2; kk++)
9051 								if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
9052 									candidateDays.append(kk);
9053 
9054 							assert(candidateDays.count()>0);
9055 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
9056 						}
9057 
9058 						assert(d2>=0);
9059 
9060 						assert(_activitiesForDay[d2].count()>0);
9061 
9062 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
9063 							assert(ai2!=ai);
9064 							assert(!swappedActivities[ai2]);
9065 							assert(!fixedTimeActivity[ai2]);
9066 							assert(!conflActivities[newtime].contains(ai2));
9067 							conflActivities[newtime].append(ai2);
9068 							nConflActivities[newtime]++;
9069 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
9070 						}
9071 					}
9072 				}
9073 				//2019-08-20 - for ZINEDDINE18 - three days exception
9074 				else if(morningsAfternoonsBehavior==TEACHER_THREE_DAYS_EXCEPTION)
9075 				//else if(gt.rules.internalExceptionTeachers3Set.contains(tc))
9076 				{
9077 					QList<int> pairedCurrentDay;
9078 
9079 					for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9080 						if(teachersTimetable(tc,dpair,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dpair,hh)))
9081 							pairedCurrentDay.append(teachersTimetable(tc,dpair,hh));
9082 
9083 					if(pairedCurrentDay.count()==0)
9084 						continue;
9085 
9086 					QList<int> morningOther;
9087 					QList<int> afternoonOther;
9088 					QList<int> morningOther2;
9089 					QList<int> afternoonOther2;
9090 					QList<int> morningOther3;
9091 					QList<int> afternoonOther3;
9092 
9093 					int dother=-1, dother2=-1, dother3=-1;
9094 					for(int dd=0; dd<gt.rules.nDaysPerWeek/2; dd++){
9095 						bool morning=false, afternoon=false;
9096 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9097 							if(teachersTimetable(tc,dd*2,hh)>=0)
9098 								morning=true;
9099 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9100 							if(teachersTimetable(tc,dd*2+1,hh)>=0)
9101 								afternoon=true;
9102 						if(dd!=d/2 && morning && afternoon){
9103 							assert(dd!=d/2);
9104 							if(dother==-1){
9105 								assert(dother==-1);
9106 								dother=dd;
9107 							}
9108 							else if(dother2==-1){
9109 								assert(dother2==-1);
9110 								dother2=dd;
9111 							}
9112 							else{
9113 								dother3=dd;
9114 							}
9115 						}
9116 					}
9117 					if(dother>=0){
9118 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9119 							if(teachersTimetable(tc,dother*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother*2,hh)))
9120 								morningOther.append(teachersTimetable(tc,dother*2,hh));
9121 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9122 							if(teachersTimetable(tc,dother*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother*2+1,hh)))
9123 								afternoonOther.append(teachersTimetable(tc,dother*2+1,hh));
9124 					}
9125 					if(dother2>=0){
9126 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9127 							if(teachersTimetable(tc,dother2*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother2*2,hh)))
9128 								morningOther2.append(teachersTimetable(tc,dother2*2,hh));
9129 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9130 							if(teachersTimetable(tc,dother2*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother2*2+1,hh)))
9131 								afternoonOther2.append(teachersTimetable(tc,dother2*2+1,hh));
9132 					}
9133 					if(dother3>=0){
9134 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9135 							if(teachersTimetable(tc,dother3*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother3*2,hh)))
9136 								morningOther3.append(teachersTimetable(tc,dother3*2,hh));
9137 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9138 							if(teachersTimetable(tc,dother3*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother3*2+1,hh)))
9139 								afternoonOther3.append(teachersTimetable(tc,dother3*2+1,hh));
9140 					}
9141 
9142 					assert(pairedCurrentDay.count()>0);
9143 					if(morningOther.count()>0 && afternoonOther.count()>0
9144 					&& morningOther2.count()>0 && afternoonOther2.count()>0
9145 					&& morningOther3.count()>0 && afternoonOther3.count()>0){
9146 						//empty one of these 7
9147 
9148 						///////////////////////////////////
9149 						//0 is paired current day, 1 is morning other, 2 is afternoon other
9150 						//3 is morning other 2, 4 is afternoon other 2
9151 						//5 is morning other 3, 6 is afternoon other 3
9152 
9153 						bool occupiedDay[3+2+2];
9154 						bool canEmptyDay[3+2+2];
9155 
9156 						int _minWrong[3+2+2];
9157 						int _nWrong[3+2+2];
9158 						int _nConflActivities[3+2+2];
9159 						int _minIndexAct[3+2+2];
9160 
9161 						QList<int> _activitiesForDay[3+2+2];
9162 
9163 						for(int d2=0; d2<3+2+2; d2++){
9164 							occupiedDay[d2]=false;
9165 							canEmptyDay[d2]=true;
9166 
9167 							_minWrong[d2]=INF;
9168 							_nWrong[d2]=0;
9169 							_nConflActivities[d2]=0;
9170 							_minIndexAct[d2]=gt.rules.nInternalActivities;
9171 							_activitiesForDay[d2].clear();
9172 
9173 							QList<int> tmp;
9174 							if(d2==0)
9175 								tmp=pairedCurrentDay;
9176 							else if(d2==1)
9177 								tmp=morningOther;
9178 							else if(d2==2)
9179 								tmp=afternoonOther;
9180 							else if(d2==3)
9181 								tmp=morningOther2;
9182 							else if(d2==4)
9183 								tmp=afternoonOther2;
9184 							else if(d2==5)
9185 								tmp=morningOther3;
9186 							else if(d2==6)
9187 								tmp=afternoonOther3;
9188 
9189 							for(int ai2 : qAsConst(tmp)){
9190 								assert(ai2>=0);
9191 								if(ai2>=0){
9192 									if(!conflActivities[newtime].contains(ai2)){
9193 										occupiedDay[d2]=true;
9194 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
9195 											canEmptyDay[d2]=false;
9196 										else if(!_activitiesForDay[d2].contains(ai2)){
9197 											_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
9198 											_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
9199 											_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
9200 											_nConflActivities[d2]++;
9201 											_activitiesForDay[d2].append(ai2);
9202 											assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
9203 										}
9204 									}
9205 								}
9206 							}
9207 
9208 							assert(occupiedDay[d2]);
9209 							if(!occupiedDay[d2])
9210 								canEmptyDay[d2]=false;
9211 						}
9212 
9213 						int nOc=0;
9214 						bool canChooseDay=false;
9215 
9216 						for(int j=0; j<3+2+2; j++)
9217 							if(occupiedDay[j]){
9218 								nOc++;
9219 								if(canEmptyDay[j]){
9220 									canChooseDay=true;
9221 								}
9222 							}
9223 
9224 						if(!canChooseDay){
9225 							if(level==0){
9226 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
9227 							}
9228 							okteachersmorningsafternoonsbehavior=false;
9229 							goto impossibleteachersmorningsafternoonsbehavior;
9230 						}
9231 
9232 						int d2=-1;
9233 
9234 						if(level!=0){
9235 							//choose random day from those with minimum number of conflicting activities
9236 							QList<int> candidateDays;
9237 
9238 							int m=gt.rules.nInternalActivities;
9239 
9240 							for(int kk=0; kk<3+2+2; kk++)
9241 								if(canEmptyDay[kk])
9242 									if(m>_nConflActivities[kk])
9243 										m=_nConflActivities[kk];
9244 
9245 							candidateDays.clear();
9246 							for(int kk=0; kk<3+2+2; kk++)
9247 								if(canEmptyDay[kk] && m==_nConflActivities[kk])
9248 									candidateDays.append(kk);
9249 
9250 							assert(candidateDays.count()>0);
9251 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
9252 						}
9253 						else{ //level==0
9254 							QList<int> candidateDays;
9255 
9256 							int _mW=INF;
9257 							int _nW=INF;
9258 							int _mCA=gt.rules.nInternalActivities;
9259 							int _mIA=gt.rules.nInternalActivities;
9260 
9261 							for(int kk=0; kk<3+2+2; kk++)
9262 								if(canEmptyDay[kk]){
9263 									if(_mW>_minWrong[kk] ||
9264 									(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
9265 									(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
9266 									(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
9267 										_mW=_minWrong[kk];
9268 										_nW=_nWrong[kk];
9269 										_mCA=_nConflActivities[kk];
9270 										_mIA=_minIndexAct[kk];
9271 									}
9272 								}
9273 
9274 							assert(_mW<INF);
9275 
9276 							candidateDays.clear();
9277 							for(int kk=0; kk<3+2+2; kk++)
9278 								if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
9279 									candidateDays.append(kk);
9280 
9281 							assert(candidateDays.count()>0);
9282 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
9283 						}
9284 
9285 						assert(d2>=0);
9286 
9287 						assert(_activitiesForDay[d2].count()>0);
9288 
9289 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
9290 							assert(ai2!=ai);
9291 							assert(!swappedActivities[ai2]);
9292 							assert(!fixedTimeActivity[ai2]);
9293 							assert(!conflActivities[newtime].contains(ai2));
9294 							conflActivities[newtime].append(ai2);
9295 							nConflActivities[newtime]++;
9296 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
9297 						}
9298 					}
9299 				}
9300 				//2020-06-06 - for boulabat and ZINEDDINE18 - four days exception
9301 				else if(morningsAfternoonsBehavior==TEACHER_FOUR_DAYS_EXCEPTION)
9302 				//else if(gt.rules.internalExceptionTeachers4Set.contains(tc))
9303 				{
9304 					QList<int> pairedCurrentDay;
9305 
9306 					for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9307 						if(teachersTimetable(tc,dpair,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dpair,hh)))
9308 							pairedCurrentDay.append(teachersTimetable(tc,dpair,hh));
9309 
9310 					if(pairedCurrentDay.count()==0)
9311 						continue;
9312 
9313 					QList<int> morningOther;
9314 					QList<int> afternoonOther;
9315 					QList<int> morningOther2;
9316 					QList<int> afternoonOther2;
9317 					QList<int> morningOther3;
9318 					QList<int> afternoonOther3;
9319 					QList<int> morningOther4;
9320 					QList<int> afternoonOther4;
9321 
9322 					int dother=-1, dother2=-1, dother3=-1, dother4=-1;
9323 					for(int dd=0; dd<gt.rules.nDaysPerWeek/2; dd++){
9324 						bool morning=false, afternoon=false;
9325 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9326 							if(teachersTimetable(tc,dd*2,hh)>=0)
9327 								morning=true;
9328 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9329 							if(teachersTimetable(tc,dd*2+1,hh)>=0)
9330 								afternoon=true;
9331 						if(dd!=d/2 && morning && afternoon){
9332 							assert(dd!=d/2);
9333 							if(dother==-1){
9334 								dother=dd;
9335 							}
9336 							else if(dother2==-1){
9337 								dother2=dd;
9338 							}
9339 							else if(dother3==-1){
9340 								dother3=dd;
9341 							}
9342 							else{
9343 								assert(dother4==-1);
9344 								dother4=dd;
9345 							}
9346 						}
9347 					}
9348 					if(dother>=0){
9349 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9350 							if(teachersTimetable(tc,dother*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother*2,hh)))
9351 								morningOther.append(teachersTimetable(tc,dother*2,hh));
9352 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9353 							if(teachersTimetable(tc,dother*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother*2+1,hh)))
9354 								afternoonOther.append(teachersTimetable(tc,dother*2+1,hh));
9355 					}
9356 					if(dother2>=0){
9357 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9358 							if(teachersTimetable(tc,dother2*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother2*2,hh)))
9359 								morningOther2.append(teachersTimetable(tc,dother2*2,hh));
9360 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9361 							if(teachersTimetable(tc,dother2*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother2*2+1,hh)))
9362 								afternoonOther2.append(teachersTimetable(tc,dother2*2+1,hh));
9363 					}
9364 					if(dother3>=0){
9365 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9366 							if(teachersTimetable(tc,dother3*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother3*2,hh)))
9367 								morningOther3.append(teachersTimetable(tc,dother3*2,hh));
9368 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9369 							if(teachersTimetable(tc,dother3*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother3*2+1,hh)))
9370 								afternoonOther3.append(teachersTimetable(tc,dother3*2+1,hh));
9371 					}
9372 					if(dother4>=0){
9373 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9374 							if(teachersTimetable(tc,dother4*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother4*2,hh)))
9375 								morningOther4.append(teachersTimetable(tc,dother4*2,hh));
9376 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9377 							if(teachersTimetable(tc,dother4*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother4*2+1,hh)))
9378 								afternoonOther4.append(teachersTimetable(tc,dother4*2+1,hh));
9379 					}
9380 
9381 					assert(pairedCurrentDay.count()>0);
9382 					if(morningOther.count()>0 && afternoonOther.count()>0
9383 					&& morningOther2.count()>0 && afternoonOther2.count()>0
9384 					&& morningOther3.count()>0 && afternoonOther3.count()>0
9385 					&& morningOther4.count()>0 && afternoonOther4.count()>0){
9386 						//empty one of these 9
9387 
9388 						///////////////////////////////////
9389 						//0 is paired current day, 1 is morning other, 2 is afternoon other
9390 						//3 is morning other 2, 4 is afternoon other 2
9391 						//5 is morning other 3, 6 is afternoon other 3
9392 						//7 is morning other 4, 8 is afternoon other 4
9393 
9394 						bool occupiedDay[3+2+2+2];
9395 						bool canEmptyDay[3+2+2+2];
9396 
9397 						int _minWrong[3+2+2+2];
9398 						int _nWrong[3+2+2+2];
9399 						int _nConflActivities[3+2+2+2];
9400 						int _minIndexAct[3+2+2+2];
9401 
9402 						QList<int> _activitiesForDay[3+2+2+2];
9403 
9404 						for(int d2=0; d2<3+2+2+2; d2++){
9405 							occupiedDay[d2]=false;
9406 							canEmptyDay[d2]=true;
9407 
9408 							_minWrong[d2]=INF;
9409 							_nWrong[d2]=0;
9410 							_nConflActivities[d2]=0;
9411 							_minIndexAct[d2]=gt.rules.nInternalActivities;
9412 							_activitiesForDay[d2].clear();
9413 
9414 							QList<int> tmp;
9415 							if(d2==0)
9416 								tmp=pairedCurrentDay;
9417 							else if(d2==1)
9418 								tmp=morningOther;
9419 							else if(d2==2)
9420 								tmp=afternoonOther;
9421 							else if(d2==3)
9422 								tmp=morningOther2;
9423 							else if(d2==4)
9424 								tmp=afternoonOther2;
9425 							else if(d2==5)
9426 								tmp=morningOther3;
9427 							else if(d2==6)
9428 								tmp=afternoonOther3;
9429 							else if(d2==7)
9430 								tmp=morningOther4;
9431 							else if(d2==8)
9432 								tmp=afternoonOther4;
9433 
9434 							for(int ai2 : qAsConst(tmp)){
9435 								assert(ai2>=0);
9436 								if(ai2>=0){
9437 									if(!conflActivities[newtime].contains(ai2)){
9438 										occupiedDay[d2]=true;
9439 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
9440 											canEmptyDay[d2]=false;
9441 										else if(!_activitiesForDay[d2].contains(ai2)){
9442 											_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
9443 											_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
9444 											_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
9445 											_nConflActivities[d2]++;
9446 											_activitiesForDay[d2].append(ai2);
9447 											assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
9448 										}
9449 									}
9450 								}
9451 							}
9452 
9453 							assert(occupiedDay[d2]);
9454 							if(!occupiedDay[d2])
9455 								canEmptyDay[d2]=false;
9456 						}
9457 
9458 						int nOc=0;
9459 						bool canChooseDay=false;
9460 
9461 						for(int j=0; j<3+2+2+2; j++)
9462 							if(occupiedDay[j]){
9463 								nOc++;
9464 								if(canEmptyDay[j]){
9465 									canChooseDay=true;
9466 								}
9467 							}
9468 
9469 						if(!canChooseDay){
9470 							if(level==0){
9471 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
9472 							}
9473 							okteachersmorningsafternoonsbehavior=false;
9474 							goto impossibleteachersmorningsafternoonsbehavior;
9475 						}
9476 
9477 						int d2=-1;
9478 
9479 						if(level!=0){
9480 							//choose random day from those with minimum number of conflicting activities
9481 							QList<int> candidateDays;
9482 
9483 							int m=gt.rules.nInternalActivities;
9484 
9485 							for(int kk=0; kk<3+2+2+2; kk++)
9486 								if(canEmptyDay[kk])
9487 									if(m>_nConflActivities[kk])
9488 										m=_nConflActivities[kk];
9489 
9490 							candidateDays.clear();
9491 							for(int kk=0; kk<3+2+2+2; kk++)
9492 								if(canEmptyDay[kk] && m==_nConflActivities[kk])
9493 									candidateDays.append(kk);
9494 
9495 							assert(candidateDays.count()>0);
9496 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
9497 						}
9498 						else{ //level==0
9499 							QList<int> candidateDays;
9500 
9501 							int _mW=INF;
9502 							int _nW=INF;
9503 							int _mCA=gt.rules.nInternalActivities;
9504 							int _mIA=gt.rules.nInternalActivities;
9505 
9506 							for(int kk=0; kk<3+2+2+2; kk++)
9507 								if(canEmptyDay[kk]){
9508 									if(_mW>_minWrong[kk] ||
9509 									(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
9510 									(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
9511 									(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
9512 										_mW=_minWrong[kk];
9513 										_nW=_nWrong[kk];
9514 										_mCA=_nConflActivities[kk];
9515 										_mIA=_minIndexAct[kk];
9516 									}
9517 								}
9518 
9519 							assert(_mW<INF);
9520 
9521 							candidateDays.clear();
9522 							for(int kk=0; kk<3+2+2+2; kk++)
9523 								if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
9524 									candidateDays.append(kk);
9525 
9526 							assert(candidateDays.count()>0);
9527 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
9528 						}
9529 
9530 						assert(d2>=0);
9531 
9532 						assert(_activitiesForDay[d2].count()>0);
9533 
9534 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
9535 							assert(ai2!=ai);
9536 							assert(!swappedActivities[ai2]);
9537 							assert(!fixedTimeActivity[ai2]);
9538 							assert(!conflActivities[newtime].contains(ai2));
9539 							conflActivities[newtime].append(ai2);
9540 							nConflActivities[newtime]++;
9541 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
9542 						}
9543 					}
9544 				}
9545 				//2020-06-06 - for boulabat and ZINEDDINE18 - five days exception
9546 				else if(morningsAfternoonsBehavior==TEACHER_FIVE_DAYS_EXCEPTION)
9547 				//else if(gt.rules.internalExceptionTeachers5Set.contains(tc))
9548 				{
9549 					QList<int> pairedCurrentDay;
9550 
9551 					for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9552 						if(teachersTimetable(tc,dpair,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dpair,hh)))
9553 							pairedCurrentDay.append(teachersTimetable(tc,dpair,hh));
9554 
9555 					if(pairedCurrentDay.count()==0)
9556 						continue;
9557 
9558 					QList<int> morningOther;
9559 					QList<int> afternoonOther;
9560 					QList<int> morningOther2;
9561 					QList<int> afternoonOther2;
9562 					QList<int> morningOther3;
9563 					QList<int> afternoonOther3;
9564 					QList<int> morningOther4;
9565 					QList<int> afternoonOther4;
9566 					QList<int> morningOther5;
9567 					QList<int> afternoonOther5;
9568 
9569 					int dother=-1, dother2=-1, dother3=-1, dother4=-1, dother5=-1;
9570 					for(int dd=0; dd<gt.rules.nDaysPerWeek/2; dd++){
9571 						bool morning=false, afternoon=false;
9572 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9573 							if(teachersTimetable(tc,dd*2,hh)>=0)
9574 								morning=true;
9575 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9576 							if(teachersTimetable(tc,dd*2+1,hh)>=0)
9577 								afternoon=true;
9578 						if(dd!=d/2 && morning && afternoon){
9579 							assert(dd!=d/2);
9580 							if(dother==-1){
9581 								dother=dd;
9582 							}
9583 							else if(dother2==-1){
9584 								dother2=dd;
9585 							}
9586 							else if(dother3==-1){
9587 								dother3=dd;
9588 							}
9589 							else if(dother4==-1){
9590 								dother4=dd;
9591 							}
9592 							else{
9593 								assert(dother5==-1);
9594 								dother5=dd;
9595 							}
9596 						}
9597 					}
9598 					if(dother>=0){
9599 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9600 							if(teachersTimetable(tc,dother*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother*2,hh)))
9601 								morningOther.append(teachersTimetable(tc,dother*2,hh));
9602 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9603 							if(teachersTimetable(tc,dother*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother*2+1,hh)))
9604 								afternoonOther.append(teachersTimetable(tc,dother*2+1,hh));
9605 					}
9606 					if(dother2>=0){
9607 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9608 							if(teachersTimetable(tc,dother2*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother2*2,hh)))
9609 								morningOther2.append(teachersTimetable(tc,dother2*2,hh));
9610 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9611 							if(teachersTimetable(tc,dother2*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother2*2+1,hh)))
9612 								afternoonOther2.append(teachersTimetable(tc,dother2*2+1,hh));
9613 					}
9614 					if(dother3>=0){
9615 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9616 							if(teachersTimetable(tc,dother3*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother3*2,hh)))
9617 								morningOther3.append(teachersTimetable(tc,dother3*2,hh));
9618 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9619 							if(teachersTimetable(tc,dother3*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother3*2+1,hh)))
9620 								afternoonOther3.append(teachersTimetable(tc,dother3*2+1,hh));
9621 					}
9622 					if(dother4>=0){
9623 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9624 							if(teachersTimetable(tc,dother4*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother4*2,hh)))
9625 								morningOther4.append(teachersTimetable(tc,dother4*2,hh));
9626 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9627 							if(teachersTimetable(tc,dother4*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother4*2+1,hh)))
9628 								afternoonOther4.append(teachersTimetable(tc,dother4*2+1,hh));
9629 					}
9630 					if(dother5>=0){
9631 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9632 							if(teachersTimetable(tc,dother5*2,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother5*2,hh)))
9633 								morningOther5.append(teachersTimetable(tc,dother5*2,hh));
9634 						for(int hh=0; hh<gt.rules.nHoursPerDay; hh++)
9635 							if(teachersTimetable(tc,dother5*2+1,hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc,dother5*2+1,hh)))
9636 								afternoonOther5.append(teachersTimetable(tc,dother5*2+1,hh));
9637 					}
9638 
9639 					assert(pairedCurrentDay.count()>0);
9640 					if(morningOther.count()>0 && afternoonOther.count()>0
9641 					&& morningOther2.count()>0 && afternoonOther2.count()>0
9642 					&& morningOther3.count()>0 && afternoonOther3.count()>0
9643 					&& morningOther4.count()>0 && afternoonOther4.count()>0
9644 					&& morningOther5.count()>0 && afternoonOther5.count()>0){
9645 						//empty one of these 11
9646 
9647 						///////////////////////////////////
9648 						//0 is paired current day, 1 is morning other, 2 is afternoon other
9649 						//3 is morning other 2, 4 is afternoon other 2
9650 						//5 is morning other 3, 6 is afternoon other 3
9651 						//7 is morning other 4, 8 is afternoon other 4
9652 						//9 is morning other 5, 10 is afternoon other 5
9653 
9654 						bool occupiedDay[3+2+2+2+2];
9655 						bool canEmptyDay[3+2+2+2+2];
9656 
9657 						int _minWrong[3+2+2+2+2];
9658 						int _nWrong[3+2+2+2+2];
9659 						int _nConflActivities[3+2+2+2+2];
9660 						int _minIndexAct[3+2+2+2+2];
9661 
9662 						QList<int> _activitiesForDay[3+2+2+2+2];
9663 
9664 						for(int d2=0; d2<3+2+2+2+2; d2++){
9665 							occupiedDay[d2]=false;
9666 							canEmptyDay[d2]=true;
9667 
9668 							_minWrong[d2]=INF;
9669 							_nWrong[d2]=0;
9670 							_nConflActivities[d2]=0;
9671 							_minIndexAct[d2]=gt.rules.nInternalActivities;
9672 							_activitiesForDay[d2].clear();
9673 
9674 							QList<int> tmp;
9675 							if(d2==0)
9676 								tmp=pairedCurrentDay;
9677 							else if(d2==1)
9678 								tmp=morningOther;
9679 							else if(d2==2)
9680 								tmp=afternoonOther;
9681 							else if(d2==3)
9682 								tmp=morningOther2;
9683 							else if(d2==4)
9684 								tmp=afternoonOther2;
9685 							else if(d2==5)
9686 								tmp=morningOther3;
9687 							else if(d2==6)
9688 								tmp=afternoonOther3;
9689 							else if(d2==7)
9690 								tmp=morningOther4;
9691 							else if(d2==8)
9692 								tmp=afternoonOther4;
9693 							else if(d2==9)
9694 								tmp=morningOther5;
9695 							else if(d2==10)
9696 								tmp=afternoonOther5;
9697 
9698 							for(int ai2 : qAsConst(tmp)){
9699 								assert(ai2>=0);
9700 								if(ai2>=0){
9701 									if(!conflActivities[newtime].contains(ai2)){
9702 										occupiedDay[d2]=true;
9703 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
9704 											canEmptyDay[d2]=false;
9705 										else if(!_activitiesForDay[d2].contains(ai2)){
9706 											_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
9707 											_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
9708 											_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
9709 											_nConflActivities[d2]++;
9710 											_activitiesForDay[d2].append(ai2);
9711 											assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
9712 										}
9713 									}
9714 								}
9715 							}
9716 
9717 							assert(occupiedDay[d2]);
9718 							if(!occupiedDay[d2])
9719 								canEmptyDay[d2]=false;
9720 						}
9721 
9722 						int nOc=0;
9723 						bool canChooseDay=false;
9724 
9725 						for(int j=0; j<3+2+2+2+2; j++)
9726 							if(occupiedDay[j]){
9727 								nOc++;
9728 								if(canEmptyDay[j]){
9729 									canChooseDay=true;
9730 								}
9731 							}
9732 
9733 						if(!canChooseDay){
9734 							if(level==0){
9735 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
9736 							}
9737 							okteachersmorningsafternoonsbehavior=false;
9738 							goto impossibleteachersmorningsafternoonsbehavior;
9739 						}
9740 
9741 						int d2=-1;
9742 
9743 						if(level!=0){
9744 							//choose random day from those with minimum number of conflicting activities
9745 							QList<int> candidateDays;
9746 
9747 							int m=gt.rules.nInternalActivities;
9748 
9749 							for(int kk=0; kk<3+2+2+2+2; kk++)
9750 								if(canEmptyDay[kk])
9751 									if(m>_nConflActivities[kk])
9752 										m=_nConflActivities[kk];
9753 
9754 							candidateDays.clear();
9755 							for(int kk=0; kk<3+2+2+2+2; kk++)
9756 								if(canEmptyDay[kk] && m==_nConflActivities[kk])
9757 									candidateDays.append(kk);
9758 
9759 							assert(candidateDays.count()>0);
9760 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
9761 						}
9762 						else{ //level==0
9763 							QList<int> candidateDays;
9764 
9765 							int _mW=INF;
9766 							int _nW=INF;
9767 							int _mCA=gt.rules.nInternalActivities;
9768 							int _mIA=gt.rules.nInternalActivities;
9769 
9770 							for(int kk=0; kk<3+2+2+2+2; kk++)
9771 								if(canEmptyDay[kk]){
9772 									if(_mW>_minWrong[kk] ||
9773 									(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
9774 									(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
9775 									(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
9776 										_mW=_minWrong[kk];
9777 										_nW=_nWrong[kk];
9778 										_mCA=_nConflActivities[kk];
9779 										_mIA=_minIndexAct[kk];
9780 									}
9781 								}
9782 
9783 							assert(_mW<INF);
9784 
9785 							candidateDays.clear();
9786 							for(int kk=0; kk<3+2+2+2+2; kk++)
9787 								if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
9788 									candidateDays.append(kk);
9789 
9790 							assert(candidateDays.count()>0);
9791 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
9792 						}
9793 
9794 						assert(d2>=0);
9795 
9796 						assert(_activitiesForDay[d2].count()>0);
9797 
9798 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
9799 							assert(ai2!=ai);
9800 							assert(!swappedActivities[ai2]);
9801 							assert(!fixedTimeActivity[ai2]);
9802 							assert(!conflActivities[newtime].contains(ai2));
9803 							conflActivities[newtime].append(ai2);
9804 							nConflActivities[newtime]++;
9805 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
9806 						}
9807 					}
9808 				}
9809 				else if(morningsAfternoonsBehavior==TEACHER_MORNING_OR_EXCLUSIVELY_AFTERNOON)
9810 				{ //should not have activities in both morning and afternoon, without exceptions.
9811 					for(int hh=0; hh<gt.rules.nHoursPerDay; hh++){
9812 						int ai2=teachersTimetable(tc,dpair,hh);
9813 						if(ai2>=0){
9814 							if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
9815 								okteachersmorningsafternoonsbehavior=false;
9816 								goto impossibleteachersmorningsafternoonsbehavior;
9817 							}
9818 
9819 							if(!conflActivities[newtime].contains(ai2)){
9820 								conflActivities[newtime].append(ai2);
9821 								nConflActivities[newtime]++;
9822 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
9823 							}
9824 						}
9825 					}
9826 				}
9827 				else
9828 				{
9829 					assert(morningsAfternoonsBehavior==TEACHER_UNRESTRICTED_MORNINGS_AFTERNOONS);
9830 				}
9831 			}
9832 		}
9833 
9834 impossibleteachersmorningsafternoonsbehavior:
9835 		if(!okteachersmorningsafternoonsbehavior){
9836 			//if(updateSubgroups || updateTeachers)
9837 			//	removeAiFromNewTimetable(ai, act, d, h);
9838 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
9839 
9840 			nConflActivities[newtime]=MAX_ACTIVITIES;
9841 			continue;
9842 		}
9843 
9844 		//////////////////
9845 		//Teachers must work max 2 consecutive mornings/afternoons
9846 
9847 		ok_max_two_consecutive_mornings_afternoons=true;
9848 
9849 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
9850 			d_first=d-4;
9851 			d_last=d+4;
9852 
9853 			d_pre=d-2;
9854 			d_after=d+2;
9855 
9856 			if((d_pre>=0 && d_after<gt.rules.nDaysPerWeek) || d_first>=0 || d_last<gt.rules.nDaysPerWeek){
9857 				for(int tc : qAsConst(act->iTeachersList)){
9858 					if((d%2==0 && teachersMaxTwoConsecutiveMorningsPercentage[tc]>=0) || (d%2==1 && teachersMaxTwoConsecutiveAfternoonsPercentage[tc]>=0)){
9859 						if(d_pre>=0 && d_after<gt.rules.nDaysPerWeek){
9860 							//d is in the middle
9861 							QList<int> d_pre_activities_list;
9862 							QSet<int> d_pre_activities_set;
9863 
9864 							QList<int> d_after_activities_list;
9865 							QSet<int> d_after_activities_set;
9866 
9867 							for(int hh=0; hh<gt.rules.nHoursPerDay; hh++){
9868 								if(teachersTimetable(tc, d_pre, hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc, d_pre, hh))){
9869 									if(!d_pre_activities_set.contains(teachersTimetable(tc, d_pre, hh))){
9870 										d_pre_activities_set.insert(teachersTimetable(tc, d_pre, hh));
9871 										d_pre_activities_list.append(teachersTimetable(tc, d_pre, hh));
9872 									}
9873 								}
9874 
9875 								if(teachersTimetable(tc, d_after, hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc, d_after, hh))){
9876 									if(!d_after_activities_set.contains(teachersTimetable(tc, d_after, hh))){
9877 										d_after_activities_set.insert(teachersTimetable(tc, d_after, hh));
9878 										d_after_activities_list.append(teachersTimetable(tc, d_after, hh));
9879 									}
9880 								}
9881 							}
9882 
9883 							if(d_pre_activities_list.count()>0 && d_after_activities_list.count()>0){
9884 								bool canEmptyDay[2]; //0 is d_pre, 1 is d_after
9885 
9886 								int _minWrong[2];
9887 								int _nWrong[2];
9888 								int _nConflActivities[2];
9889 								int _minIndexAct[2];
9890 								QList<int> _activitiesForDay[2];
9891 
9892 								for(int d2=0; d2<2; d2++){
9893 									canEmptyDay[d2]=true;
9894 
9895 									_minWrong[d2]=INF;
9896 									_nWrong[d2]=0;
9897 									_nConflActivities[d2]=0;
9898 									_minIndexAct[d2]=gt.rules.nInternalActivities;
9899 
9900 									QList<int> tmp;
9901 
9902 									if(d2==0)
9903 										tmp=d_pre_activities_list;
9904 									else
9905 										tmp=d_after_activities_list;
9906 
9907 									for(int ai2 : qAsConst(tmp)){
9908 										assert(ai2>=0);
9909 										assert(!conflActivities[newtime].contains(ai2));
9910 
9911 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
9912 											canEmptyDay[d2]=false;
9913 										else{
9914 											_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
9915 											_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
9916 											_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
9917 											_nConflActivities[d2]++;
9918 											_activitiesForDay[d2].append(ai2);
9919 											assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
9920 										}
9921 									}
9922 								}
9923 
9924 								if(canEmptyDay[0])
9925 									assert(_nConflActivities[0]>0);
9926 								if(canEmptyDay[1])
9927 									assert(_nConflActivities[1]>0);
9928 
9929 								if(!canEmptyDay[0] && !canEmptyDay[1]){
9930 									if(level==0){
9931 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
9932 									}
9933 									ok_max_two_consecutive_mornings_afternoons=false;
9934 									goto impossible_max_two_consecutive_mornings_afternoons;
9935 								}
9936 
9937 								int d2=-1;
9938 
9939 								if(level!=0){
9940 									//choose random day from those with minimum number of conflicting activities
9941 									QList<int> candidateDays;
9942 
9943 									int m=gt.rules.nInternalActivities;
9944 
9945 									for(int kk=0; kk<2; kk++)
9946 										if(canEmptyDay[kk])
9947 											if(m>_nConflActivities[kk])
9948 												m=_nConflActivities[kk];
9949 
9950 									candidateDays.clear();
9951 									for(int kk=0; kk<2; kk++)
9952 										if(canEmptyDay[kk] && m==_nConflActivities[kk])
9953 											candidateDays.append(kk);
9954 
9955 									assert(candidateDays.count()>0);
9956 									d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
9957 								}
9958 								else{ //level==0
9959 									QList<int> candidateDays;
9960 
9961 									int _mW=INF;
9962 									int _nW=INF;
9963 									int _mCA=gt.rules.nInternalActivities;
9964 									int _mIA=gt.rules.nInternalActivities;
9965 
9966 									for(int kk=0; kk<2; kk++)
9967 										if(canEmptyDay[kk]){
9968 											if(_mW>_minWrong[kk] ||
9969 											(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
9970 											(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
9971 											(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
9972 												_mW=_minWrong[kk];
9973 												_nW=_nWrong[kk];
9974 												_mCA=_nConflActivities[kk];
9975 												_mIA=_minIndexAct[kk];
9976 											}
9977 										}
9978 
9979 									assert(_mW<INF);
9980 
9981 									candidateDays.clear();
9982 									for(int kk=0; kk<2; kk++)
9983 										if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
9984 											candidateDays.append(kk);
9985 
9986 									assert(candidateDays.count()>0);
9987 									d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
9988 								}
9989 
9990 								assert(d2>=0);
9991 
9992 								assert(_activitiesForDay[d2].count()>0);
9993 
9994 								for(int ai2 : qAsConst(_activitiesForDay[d2])){
9995 									assert(ai2!=ai);
9996 									assert(!swappedActivities[ai2]);
9997 									assert(!fixedTimeActivity[ai2]);
9998 									assert(!conflActivities[newtime].contains(ai2));
9999 									conflActivities[newtime].append(ai2);
10000 									nConflActivities[newtime]++;
10001 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
10002 								}
10003 
10004 								if(d2==0){ //we emptied d_pre
10005 									d_last=d+4;
10006 									d_first=-10;
10007 								}
10008 								else{ //we emptied d_after
10009 									d_first=d-4;
10010 									d_last=MAX_DAYS_PER_WEEK+10;
10011 								}
10012 							}
10013 						}
10014 
10015 						if(d_first>=0){
10016 							//now we have d_first, d_pre, then d.
10017 							//empty d_first or d_pre?
10018 							//assert(d_last==MAX_DAYS_PER_WEEK+10);
10019 
10020 							QList<int> d_first_activities_list;
10021 							QSet<int> d_first_activities_set;
10022 
10023 							QList<int> d_pre_activities_list;
10024 							QSet<int> d_pre_activities_set;
10025 
10026 							for(int hh=0; hh<gt.rules.nHoursPerDay; hh++){
10027 								if(teachersTimetable(tc, d_first, hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc, d_first, hh))){
10028 									if(!d_first_activities_set.contains(teachersTimetable(tc, d_first, hh))){
10029 										d_first_activities_set.insert(teachersTimetable(tc, d_first, hh));
10030 										d_first_activities_list.append(teachersTimetable(tc, d_first, hh));
10031 									}
10032 								}
10033 
10034 								if(teachersTimetable(tc, d_pre, hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc, d_pre, hh))){
10035 									if(!d_pre_activities_set.contains(teachersTimetable(tc, d_pre, hh))){
10036 										d_pre_activities_set.insert(teachersTimetable(tc, d_pre, hh));
10037 										d_pre_activities_list.append(teachersTimetable(tc, d_pre, hh));
10038 									}
10039 								}
10040 							}
10041 
10042 							if(d_first_activities_list.count()>0 && d_pre_activities_list.count()>0){
10043 								bool canEmptyDay[2]; //0 is d_first, 1 is d_pre
10044 
10045 								int _minWrong[2];
10046 								int _nWrong[2];
10047 								int _nConflActivities[2];
10048 								int _minIndexAct[2];
10049 								QList<int> _activitiesForDay[2];
10050 
10051 								for(int d2=0; d2<2; d2++){
10052 									canEmptyDay[d2]=true;
10053 
10054 									_minWrong[d2]=INF;
10055 									_nWrong[d2]=0;
10056 									_nConflActivities[d2]=0;
10057 									_minIndexAct[d2]=gt.rules.nInternalActivities;
10058 
10059 									QList<int> tmp;
10060 
10061 									if(d2==0)
10062 										tmp=d_first_activities_list;
10063 									else
10064 										tmp=d_pre_activities_list;
10065 
10066 									for(int ai2 : qAsConst(tmp)){
10067 										assert(ai2>=0);
10068 										assert(!conflActivities[newtime].contains(ai2));
10069 
10070 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
10071 											canEmptyDay[d2]=false;
10072 										else{
10073 											_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
10074 											_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
10075 											_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
10076 											_nConflActivities[d2]++;
10077 											_activitiesForDay[d2].append(ai2);
10078 											assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
10079 										}
10080 									}
10081 								}
10082 
10083 								if(canEmptyDay[0])
10084 									assert(_nConflActivities[0]>0);
10085 								if(canEmptyDay[1])
10086 									assert(_nConflActivities[1]>0);
10087 
10088 								if(!canEmptyDay[0] && !canEmptyDay[1]){
10089 									if(level==0){
10090 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
10091 									}
10092 									ok_max_two_consecutive_mornings_afternoons=false;
10093 									goto impossible_max_two_consecutive_mornings_afternoons;
10094 								}
10095 
10096 								int d2=-1;
10097 
10098 								if(level!=0){
10099 									//choose random day from those with minimum number of conflicting activities
10100 									QList<int> candidateDays;
10101 
10102 									int m=gt.rules.nInternalActivities;
10103 
10104 									for(int kk=0; kk<2; kk++)
10105 										if(canEmptyDay[kk])
10106 											if(m>_nConflActivities[kk])
10107 												m=_nConflActivities[kk];
10108 
10109 									candidateDays.clear();
10110 									for(int kk=0; kk<2; kk++)
10111 										if(canEmptyDay[kk] && m==_nConflActivities[kk])
10112 											candidateDays.append(kk);
10113 
10114 									assert(candidateDays.count()>0);
10115 									d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
10116 								}
10117 								else{ //level==0
10118 									QList<int> candidateDays;
10119 
10120 									int _mW=INF;
10121 									int _nW=INF;
10122 									int _mCA=gt.rules.nInternalActivities;
10123 									int _mIA=gt.rules.nInternalActivities;
10124 
10125 									for(int kk=0; kk<2; kk++)
10126 										if(canEmptyDay[kk]){
10127 											if(_mW>_minWrong[kk] ||
10128 											(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
10129 											(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
10130 											(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
10131 												_mW=_minWrong[kk];
10132 												_nW=_nWrong[kk];
10133 												_mCA=_nConflActivities[kk];
10134 												_mIA=_minIndexAct[kk];
10135 											}
10136 										}
10137 
10138 									assert(_mW<INF);
10139 
10140 									candidateDays.clear();
10141 									for(int kk=0; kk<2; kk++)
10142 										if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
10143 											candidateDays.append(kk);
10144 
10145 									assert(candidateDays.count()>0);
10146 									d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
10147 								}
10148 
10149 								assert(d2>=0);
10150 
10151 								assert(_activitiesForDay[d2].count()>0);
10152 
10153 								for(int ai2 : qAsConst(_activitiesForDay[d2])){
10154 									assert(ai2!=ai);
10155 									assert(!swappedActivities[ai2]);
10156 									assert(!fixedTimeActivity[ai2]);
10157 									assert(!conflActivities[newtime].contains(ai2));
10158 									conflActivities[newtime].append(ai2);
10159 									nConflActivities[newtime]++;
10160 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
10161 								}
10162 							}
10163 						}
10164 
10165 						if(d_last<gt.rules.nDaysPerWeek){
10166 							//now we have d, d_after, then d_last.
10167 							//empty d_after or d_last?
10168 							//assert(d_first==-10);
10169 
10170 							QList<int> d_after_activities_list;
10171 							QSet<int> d_after_activities_set;
10172 
10173 							QList<int> d_last_activities_list;
10174 							QSet<int> d_last_activities_set;
10175 
10176 							for(int hh=0; hh<gt.rules.nHoursPerDay; hh++){
10177 								if(teachersTimetable(tc, d_after, hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc, d_after, hh))){
10178 									if(!d_after_activities_set.contains(teachersTimetable(tc, d_after, hh))){
10179 										d_after_activities_set.insert(teachersTimetable(tc, d_after, hh));
10180 										d_after_activities_list.append(teachersTimetable(tc, d_after, hh));
10181 									}
10182 								}
10183 
10184 								if(teachersTimetable(tc, d_last, hh)>=0 && !conflActivities[newtime].contains(teachersTimetable(tc, d_last, hh))){
10185 									if(!d_last_activities_set.contains(teachersTimetable(tc, d_last, hh))){
10186 										d_last_activities_set.insert(teachersTimetable(tc, d_last, hh));
10187 										d_last_activities_list.append(teachersTimetable(tc, d_last, hh));
10188 									}
10189 								}
10190 							}
10191 
10192 							if(d_after_activities_list.count()>0 && d_last_activities_list.count()>0){
10193 								bool canEmptyDay[2]; //0 is d_first, 1 is d_pre
10194 
10195 								int _minWrong[2];
10196 								int _nWrong[2];
10197 								int _nConflActivities[2];
10198 								int _minIndexAct[2];
10199 								QList<int> _activitiesForDay[2];
10200 
10201 								for(int d2=0; d2<2; d2++){
10202 									canEmptyDay[d2]=true;
10203 
10204 									_minWrong[d2]=INF;
10205 									_nWrong[d2]=0;
10206 									_nConflActivities[d2]=0;
10207 									_minIndexAct[d2]=gt.rules.nInternalActivities;
10208 
10209 									QList<int> tmp;
10210 
10211 									if(d2==0)
10212 										tmp=d_after_activities_list;
10213 									else
10214 										tmp=d_last_activities_list;
10215 
10216 									for(int ai2 : qAsConst(tmp)){
10217 										assert(ai2>=0);
10218 										assert(!conflActivities[newtime].contains(ai2));
10219 
10220 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
10221 											canEmptyDay[d2]=false;
10222 										else{
10223 											_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
10224 											_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
10225 											_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
10226 											_nConflActivities[d2]++;
10227 											_activitiesForDay[d2].append(ai2);
10228 											assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
10229 										}
10230 									}
10231 								}
10232 
10233 								if(canEmptyDay[0])
10234 									assert(_nConflActivities[0]>0);
10235 								if(canEmptyDay[1])
10236 									assert(_nConflActivities[1]>0);
10237 
10238 								if(!canEmptyDay[0] && !canEmptyDay[1]){
10239 									if(level==0){
10240 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
10241 									}
10242 									ok_max_two_consecutive_mornings_afternoons=false;
10243 									goto impossible_max_two_consecutive_mornings_afternoons;
10244 								}
10245 
10246 								int d2=-1;
10247 
10248 								if(level!=0){
10249 									//choose random day from those with minimum number of conflicting activities
10250 									QList<int> candidateDays;
10251 
10252 									int m=gt.rules.nInternalActivities;
10253 
10254 									for(int kk=0; kk<2; kk++)
10255 										if(canEmptyDay[kk])
10256 											if(m>_nConflActivities[kk])
10257 												m=_nConflActivities[kk];
10258 
10259 									candidateDays.clear();
10260 									for(int kk=0; kk<2; kk++)
10261 										if(canEmptyDay[kk] && m==_nConflActivities[kk])
10262 											candidateDays.append(kk);
10263 
10264 									assert(candidateDays.count()>0);
10265 									d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
10266 								}
10267 								else{ //level==0
10268 									QList<int> candidateDays;
10269 
10270 									int _mW=INF;
10271 									int _nW=INF;
10272 									int _mCA=gt.rules.nInternalActivities;
10273 									int _mIA=gt.rules.nInternalActivities;
10274 
10275 									for(int kk=0; kk<2; kk++)
10276 										if(canEmptyDay[kk]){
10277 											if(_mW>_minWrong[kk] ||
10278 											(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
10279 											(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
10280 											(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
10281 												_mW=_minWrong[kk];
10282 												_nW=_nWrong[kk];
10283 												_mCA=_nConflActivities[kk];
10284 												_mIA=_minIndexAct[kk];
10285 											}
10286 										}
10287 
10288 									assert(_mW<INF);
10289 
10290 									candidateDays.clear();
10291 									for(int kk=0; kk<2; kk++)
10292 										if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
10293 											candidateDays.append(kk);
10294 
10295 									assert(candidateDays.count()>0);
10296 									d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
10297 								}
10298 
10299 								assert(d2>=0);
10300 
10301 								assert(_activitiesForDay[d2].count()>0);
10302 
10303 								for(int ai2 : qAsConst(_activitiesForDay[d2])){
10304 									assert(ai2!=ai);
10305 									assert(!swappedActivities[ai2]);
10306 									assert(!fixedTimeActivity[ai2]);
10307 									assert(!conflActivities[newtime].contains(ai2));
10308 									conflActivities[newtime].append(ai2);
10309 									nConflActivities[newtime]++;
10310 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
10311 								}
10312 							}
10313 						}
10314 					}
10315 				}
10316 			}
10317 			else{
10318 				//shouldn't appear - too few FET days (real half days) per week
10319 			}
10320 
10321 	impossible_max_two_consecutive_mornings_afternoons:
10322 			if(!ok_max_two_consecutive_mornings_afternoons){
10323 				//if(updateSubgroups || updateTeachers)
10324 				//	removeAiFromNewTimetable(ai, act, d, h);
10325 				//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
10326 
10327 				nConflActivities[newtime]=MAX_ACTIVITIES;
10328 				continue;
10329 			}
10330 		}
10331 
10332 /////////////////////////////////////////////////////////////////////////////////////////////
10333 
10334 		//////////////////////////////////////////////////
10335 		if(updateSubgroups || updateTeachers){
10336 			addAiToNewTimetable(ai, act, d, h);
10337 			if(updateTeachers){
10338 				updateTeachersNHoursGaps(ai, d);
10339 				if(gt.rules.mode==MORNINGS_AFTERNOONS && haveTeachersMaxGapsPerRealDay)
10340 					updateTeachersNHoursGapsRealDay(ai, d/2);
10341 			}
10342 			if(updateSubgroups){
10343 				updateSubgroupsNHoursGaps(ai, d);
10344 				if(gt.rules.mode==MORNINGS_AFTERNOONS && haveStudentsMaxGapsPerRealDay)
10345 					updateSubgroupsNHoursGapsRealDay(ai, d/2);
10346 			}
10347 		}
10348 		//////////////////////////////////////////////////
10349 
10350 /////////////////////////////////////////////////////////////////////////////////////////////
10351 
10352 		////////////STUDENTS////////////////
10353 
10354 /////////////////////////////////////////////////////////////////////////////////////////////
10355 
10356 		//not breaking the students max days per week constraints
10357 		////////////////////////////BEGIN max days per week for students
10358 		okstudentsmaxdaysperweek=true;
10359 		for(int st : qAsConst(subgroupsWithMaxDaysPerWeekForActivities[ai])){
10360 			if(skipRandom(subgroupsMaxDaysPerWeekWeightPercentages[st]))
10361 				continue;
10362 
10363 			int maxDays=subgroupsMaxDaysPerWeekMaxDays[st];
10364 			assert(maxDays>=0); //the list contains real information
10365 
10366 			//preliminary test
10367 			int _nOc=0;
10368 			for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
10369 				//comments taken from teachers constraint:
10370 				//if(newTeachersDayNHours(tch,d2)>0)
10371 
10372 				//IT IS VITAL TO USE teacherActivitiesOfTheDay as a QList<int> (tch,d2)!!!!!!!
10373 				//The order of evaluation of activities is changed,
10374 				//with activities which were moved forward and back again
10375 				//being put at the end.
10376 				//If you do not follow this, you'll get impossible timetables
10377 				//for the Italian example Italy/2007/difficult/highschool-Ancona.fet or the examples from
10378 				//South Africa: South-Africa/difficult/Collegiate_Junior_School2.fet or
10379 				//South-Africa/difficult/Insight_Learning_Centre2.fet, I am not sure which of these 3
10380 
10381 				if(subgroupActivitiesOfTheDay(st,d2).count()>0 || d2==d)
10382 					_nOc++;
10383 			if(_nOc<=maxDays)
10384 				continue; //OK, preliminary
10385 
10386 			if(maxDays>=0){
10387 				assert(maxDays>0);
10388 
10389 				if(level>0){
10390 					///getTchTimetable(tch, conflActivities[newtime]);
10391 					///tchGetNHoursGaps(tch);
10392 
10393 					//bool occupiedDay[MAX_DAYS_PER_WEEK];
10394 					//bool canEmptyDay[MAX_DAYS_PER_WEEK];
10395 
10396 					///int _minWrong[MAX_DAYS_PER_WEEK];
10397 					///int _nWrong[MAX_DAYS_PER_WEEK];
10398 					//int _nConflActivities[MAX_DAYS_PER_WEEK];
10399 					///int _minIndexAct[MAX_DAYS_PER_WEEK];
10400 
10401 					//QList<int> _activitiesForDay[MAX_DAYS_PER_WEEK];
10402 
10403 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
10404 						if(d2==d)
10405 							continue;
10406 
10407 						occupiedDay[d2]=false;
10408 						canEmptyDay[d2]=true;
10409 
10410 						//_minWrong[d2]=INF;
10411 						//_nWrong[d2]=0;
10412 						_nConflActivities[d2]=0;
10413 						//_minIndexAct[d2]=gt.rules.nInternalActivities;
10414 						_activitiesForDay[d2].clear();
10415 
10416 						for(int ai2 : qAsConst(subgroupActivitiesOfTheDay(st,d2))){
10417 							if(ai2>=0){
10418 								if(!conflActivities[newtime].contains(ai2)){
10419 									occupiedDay[d2]=true;
10420 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
10421 										canEmptyDay[d2]=false;
10422 									else if(!_activitiesForDay[d2].contains(ai2)){
10423 										//_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
10424 										//_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
10425 										//_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
10426 										_nConflActivities[d2]++;
10427 										_activitiesForDay[d2].append(ai2);
10428 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
10429 									}
10430 								}
10431 							}
10432 						}
10433 
10434 						if(!occupiedDay[d2])
10435 							canEmptyDay[d2]=false;
10436 					}
10437 					occupiedDay[d]=true;
10438 					canEmptyDay[d]=false;
10439 
10440 					int nOc=0;
10441 					bool canChooseDay=false;
10442 
10443 					for(int j=0; j<gt.rules.nDaysPerWeek; j++)
10444 						if(occupiedDay[j]){
10445 							nOc++;
10446 							if(canEmptyDay[j]){
10447 								canChooseDay=true;
10448 							}
10449 						}
10450 
10451 					if(nOc>maxDays){
10452 						assert(nOc==maxDays+1);
10453 
10454 						if(!canChooseDay){
10455 							if(level==0){
10456 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
10457 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
10458 							}
10459 							okstudentsmaxdaysperweek=false;
10460 							goto impossiblestudentsmaxdaysperweek;
10461 						}
10462 
10463 						int d2=-1;
10464 
10465 						///////////////////
10466 						//choose a random day from those with minimum number of conflicting activities
10467 						QList<int> candidateDays;
10468 
10469 						int m=gt.rules.nInternalActivities;
10470 
10471 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
10472 							if(canEmptyDay[kk])
10473 								if(m>_nConflActivities[kk])
10474 									m=_nConflActivities[kk];
10475 
10476 						candidateDays.clear();
10477 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
10478 							if(canEmptyDay[kk])
10479 								if(m==_nConflActivities[kk])
10480 									candidateDays.append(kk);
10481 
10482 						assert(candidateDays.count()>0);
10483 						d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
10484 						//////////////////
10485 
10486 						assert(d2>=0);
10487 
10488 						assert(_activitiesForDay[d2].count()>0);
10489 
10490 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
10491 							assert(ai2!=ai);
10492 							assert(!swappedActivities[ai2]);
10493 							assert(!fixedTimeActivity[ai2]);
10494 							assert(!conflActivities[newtime].contains(ai2));
10495 							conflActivities[newtime].append(ai2);
10496 							nConflActivities[newtime]++;
10497 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
10498 						}
10499 					}
10500 				}
10501 				else{
10502 					assert(level==0);
10503 					///getTchTimetable(tch, conflActivities[newtime]);
10504 					///tchGetNHoursGaps(tch);
10505 
10506 					//bool occupiedDay[MAX_DAYS_PER_WEEK];
10507 					//bool canEmptyDay[MAX_DAYS_PER_WEEK];
10508 
10509 					//int _minWrong[MAX_DAYS_PER_WEEK];
10510 					//int _nWrong[MAX_DAYS_PER_WEEK];
10511 					//int _nConflActivities[MAX_DAYS_PER_WEEK];
10512 					//int _minIndexAct[MAX_DAYS_PER_WEEK];
10513 
10514 					//QList<int> _activitiesForDay[MAX_DAYS_PER_WEEK];
10515 
10516 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
10517 						if(d2==d)
10518 							continue;
10519 
10520 						occupiedDay[d2]=false;
10521 						canEmptyDay[d2]=true;
10522 
10523 						_minWrong[d2]=INF;
10524 						_nWrong[d2]=0;
10525 						_nConflActivities[d2]=0;
10526 						_minIndexAct[d2]=gt.rules.nInternalActivities;
10527 						_activitiesForDay[d2].clear();
10528 
10529 						for(int ai2 : qAsConst(subgroupActivitiesOfTheDay(st,d2))){
10530 							if(ai2>=0){
10531 								if(!conflActivities[newtime].contains(ai2)){
10532 									occupiedDay[d2]=true;
10533 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
10534 										canEmptyDay[d2]=false;
10535 									else if(!_activitiesForDay[d2].contains(ai2)){
10536 										_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
10537 										_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
10538 										_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
10539 										_nConflActivities[d2]++;
10540 										_activitiesForDay[d2].append(ai2);
10541 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
10542 									}
10543 								}
10544 							}
10545 						}
10546 
10547 						if(!occupiedDay[d2])
10548 							canEmptyDay[d2]=false;
10549 					}
10550 					occupiedDay[d]=true;
10551 					canEmptyDay[d]=false;
10552 
10553 					int nOc=0;
10554 					bool canChooseDay=false;
10555 
10556 					for(int j=0; j<gt.rules.nDaysPerWeek; j++)
10557 						if(occupiedDay[j]){
10558 							nOc++;
10559 							if(canEmptyDay[j]){
10560 								canChooseDay=true;
10561 							}
10562 						}
10563 
10564 					if(nOc>maxDays){
10565 						assert(nOc==maxDays+1);
10566 
10567 						if(!canChooseDay){
10568 							if(level==0){
10569 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
10570 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
10571 							}
10572 							okstudentsmaxdaysperweek=false;
10573 							goto impossiblestudentsmaxdaysperweek;
10574 						}
10575 
10576 						int d2=-1;
10577 
10578 						//////////////////
10579 						QList<int> candidateDays;
10580 
10581 						int _mW=INF;
10582 						int _nW=INF;
10583 						int _mCA=gt.rules.nInternalActivities;
10584 						int _mIA=gt.rules.nInternalActivities;
10585 
10586 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
10587 							if(canEmptyDay[kk]){
10588 								if(_mW>_minWrong[kk] ||
10589 								(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
10590 								(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
10591 								(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
10592 									_mW=_minWrong[kk];
10593 									_nW=_nWrong[kk];
10594 									_mCA=_nConflActivities[kk];
10595 									_mIA=_minIndexAct[kk];
10596 								}
10597 							}
10598 
10599 						assert(_mW<INF);
10600 
10601 						candidateDays.clear();
10602 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
10603 							if(canEmptyDay[kk])
10604 								if(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
10605 									candidateDays.append(kk);
10606 
10607 						assert(candidateDays.count()>0);
10608 						d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
10609 						///////////////////
10610 
10611 						assert(d2>=0);
10612 
10613 						assert(_activitiesForDay[d2].count()>0);
10614 
10615 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
10616 							assert(ai2!=ai);
10617 							assert(!swappedActivities[ai2]);
10618 							assert(!fixedTimeActivity[ai2]);
10619 							assert(!conflActivities[newtime].contains(ai2));
10620 							conflActivities[newtime].append(ai2);
10621 							nConflActivities[newtime]++;
10622 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
10623 						}
10624 					}
10625 				}
10626 			}
10627 		}
10628 impossiblestudentsmaxdaysperweek:
10629 		if(!okstudentsmaxdaysperweek){
10630 			if(updateSubgroups || updateTeachers)
10631 				removeAiFromNewTimetable(ai, act, d, h);
10632 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
10633 
10634 			nConflActivities[newtime]=MAX_ACTIVITIES;
10635 			continue;
10636 		}
10637 
10638 		////////////////////////////END students max days per week
10639 
10640 /////////////////////////////////////////////////////////////////////////////////////////////
10641 
10642 		//not breaking the students max real days per week constraints
10643 		////////////////////////////BEGIN max real days per week for students
10644 		okstudentsmaxrealdaysperweek=true;
10645 		//for(int tch : qAsConst(act->iTeachersList)){
10646 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
10647 			//for(int tch : qAsConst(act->iTeachersList)){
10648 			for(int st : qAsConst(subgroupsWithMaxRealDaysPerWeekForActivities[ai])){
10649 				if(skipRandom(subgroupsMaxRealDaysPerWeekWeightPercentages[st]))
10650 					continue;
10651 
10652 				int maxDays=subgroupsMaxRealDaysPerWeekMaxDays[st];
10653 				assert(maxDays>=0); //the list contains real information
10654 
10655 				//preliminary test
10656 				int _nOc=0;
10657 				for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
10658 					//comments taken from teachers constraint:
10659 					//if(newTeachersDayNHours(tch,d2)>0)
10660 
10661 					//IT IS VITAL TO USE teacherActivitiesOfTheDay as a QList<int> (tch,d2)!!!!!!!
10662 					//The order of evaluation of activities is changed,
10663 					//with activities which were moved forward and back again
10664 					//being put at the end.
10665 					//If you do not follow this, you'll get impossible timetables
10666 					//for the Italian example Italy/2007/difficult/highschool-Ancona.fet or the examples from
10667 					//South Africa: South-Africa/difficult/Collegiate_Junior_School2.fet or
10668 					//South-Africa/difficult/Insight_Learning_Centre2.fet, I am not sure which of these 3
10669 
10670 					if(subgroupActivitiesOfTheDay(st,2*d2).count()+subgroupActivitiesOfTheDay(st,2*d2+1).count()>0 || d2==d/2)
10671 						_nOc++;
10672 				if(_nOc<=maxDays)
10673 					continue; //OK, preliminary
10674 
10675 				if(maxDays>=0){
10676 					assert(maxDays>0);
10677 
10678 					//getTchTimetable(tch, conflActivities[newtime]);
10679 					//tchGetNHoursGaps(tch);
10680 
10681 					//bool occupiedDay[MAX_DAYS_PER_WEEK]; //should be MAX_DAYS_PER_WEEK/2, but doesn't matter
10682 					//bool canEmptyDay[MAX_DAYS_PER_WEEK];
10683 
10684 					//int _minWrong[MAX_DAYS_PER_WEEK];
10685 					//int _nWrong[MAX_DAYS_PER_WEEK];
10686 					//int _nConflActivities[MAX_DAYS_PER_WEEK];
10687 					//int _minIndexAct[MAX_DAYS_PER_WEEK];
10688 
10689 					//QList<int> _activitiesForDay[MAX_DAYS_PER_WEEK];
10690 
10691 					for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
10692 						if(d2==d/2)
10693 							continue;
10694 
10695 						occupiedDay[d2]=false;
10696 						canEmptyDay[d2]=true;
10697 
10698 						_minWrong[d2]=INF;
10699 						_nWrong[d2]=0;
10700 						_nConflActivities[d2]=0;
10701 						_minIndexAct[d2]=gt.rules.nInternalActivities;
10702 						_activitiesForDay[d2].clear();
10703 
10704 						for(int ai2 : qAsConst(subgroupActivitiesOfTheDay(st,2*d2))){
10705 							if(ai2>=0){
10706 								if(!conflActivities[newtime].contains(ai2)){
10707 									occupiedDay[d2]=true;
10708 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
10709 										canEmptyDay[d2]=false;
10710 									else if(!_activitiesForDay[d2].contains(ai2)){
10711 										_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
10712 										_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
10713 										_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
10714 										_nConflActivities[d2]++;
10715 										_activitiesForDay[d2].append(ai2);
10716 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
10717 									}
10718 								}
10719 							}
10720 						}
10721 						for(int ai2 : qAsConst(subgroupActivitiesOfTheDay(st,2*d2+1))){
10722 							if(ai2>=0){
10723 								if(!conflActivities[newtime].contains(ai2)){
10724 									occupiedDay[d2]=true;
10725 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
10726 										canEmptyDay[d2]=false;
10727 									else if(!_activitiesForDay[d2].contains(ai2)){
10728 										_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
10729 										_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
10730 										_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
10731 										_nConflActivities[d2]++;
10732 										_activitiesForDay[d2].append(ai2);
10733 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
10734 									}
10735 								}
10736 							}
10737 						}
10738 
10739 						if(!occupiedDay[d2])
10740 							canEmptyDay[d2]=false;
10741 					}
10742 					occupiedDay[d/2]=true;
10743 					canEmptyDay[d/2]=false;
10744 
10745 					int nOc=0;
10746 					bool canChooseDay=false;
10747 
10748 					for(int j=0; j<gt.rules.nDaysPerWeek/2; j++)
10749 						if(occupiedDay[j]){
10750 							nOc++;
10751 							if(canEmptyDay[j]){
10752 								canChooseDay=true;
10753 							}
10754 						}
10755 
10756 					if(nOc>maxDays){
10757 						assert(nOc==maxDays+1);
10758 
10759 						if(!canChooseDay){
10760 							if(level==0){
10761 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
10762 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
10763 							}
10764 							okstudentsmaxrealdaysperweek=false;
10765 							goto impossiblestudentsmaxrealdaysperweek;
10766 						}
10767 
10768 						int d2=-1;
10769 
10770 						if(level!=0){
10771 							//choose random day from those with minimum number of conflicting activities
10772 							QList<int> candidateDays;
10773 
10774 							int m=gt.rules.nInternalActivities;
10775 
10776 							for(int kk=0; kk<gt.rules.nDaysPerWeek/2; kk++)
10777 								if(canEmptyDay[kk])
10778 									if(m>_nConflActivities[kk])
10779 										m=_nConflActivities[kk];
10780 
10781 							candidateDays.clear();
10782 							for(int kk=0; kk<gt.rules.nDaysPerWeek/2; kk++)
10783 								if(canEmptyDay[kk] && m==_nConflActivities[kk])
10784 									candidateDays.append(kk);
10785 
10786 							assert(candidateDays.count()>0);
10787 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
10788 						}
10789 						else{ //level==0
10790 							QList<int> candidateDays;
10791 
10792 							int _mW=INF;
10793 							int _nW=INF;
10794 							int _mCA=gt.rules.nInternalActivities;
10795 							int _mIA=gt.rules.nInternalActivities;
10796 
10797 							for(int kk=0; kk<gt.rules.nDaysPerWeek/2; kk++)
10798 								if(canEmptyDay[kk]){
10799 									if(_mW>_minWrong[kk] ||
10800 									 (_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
10801 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
10802 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
10803 										_mW=_minWrong[kk];
10804 										_nW=_nWrong[kk];
10805 										_mCA=_nConflActivities[kk];
10806 										_mIA=_minIndexAct[kk];
10807 									}
10808 								}
10809 
10810 							assert(_mW<INF);
10811 
10812 							candidateDays.clear();
10813 							for(int kk=0; kk<gt.rules.nDaysPerWeek/2; kk++)
10814 								if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
10815 									candidateDays.append(kk);
10816 
10817 							assert(candidateDays.count()>0);
10818 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
10819 						}
10820 
10821 						assert(d2>=0);
10822 
10823 						assert(_activitiesForDay[d2].count()>0);
10824 
10825 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
10826 							assert(ai2!=ai);
10827 							assert(!swappedActivities[ai2]);
10828 							assert(!fixedTimeActivity[ai2]);
10829 							assert(!conflActivities[newtime].contains(ai2));
10830 							conflActivities[newtime].append(ai2);
10831 							nConflActivities[newtime]++;
10832 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
10833 							//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
10834 						}
10835 					}
10836 				}
10837 			}
10838 		}
10839 impossiblestudentsmaxrealdaysperweek:
10840 		if(!okstudentsmaxrealdaysperweek){
10841 			if(updateSubgroups || updateTeachers)
10842 				removeAiFromNewTimetable(ai, act, d, h);
10843 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
10844 
10845 			nConflActivities[newtime]=MAX_ACTIVITIES;
10846 			continue;
10847 		}
10848 
10849 		////////////////////////////END students max real days per week
10850 
10851 /////////////////////////////////////////////////////////////////////////////////////////////
10852 
10853 		okstudentsafternoonsearlymaxbeginningsatsecondhour=true;
10854 
10855 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
10856 			//for lakhdarbe - 2020-07-24
10857 			if(haveStudentsAfternoonsEarly){
10858 				for(int sbg : qAsConst(act->iSubgroupsList))
10859 					if(!skipRandom(subgroupsAfternoonsEarlyMaxBeginningsAtSecondHourPercentage[sbg])){
10860 						//preliminary check
10861 						int mhd=1; //min hours per day
10862 						if(subgroupsMinHoursDailyMinHours[sbg][1]>=0)
10863 							mhd=subgroupsMinHoursDailyMinHours[sbg][1];
10864 						assert(mhd>=1);
10865 						int mhm=mhd; //min hours per morning
10866 						if(subgroupsMinHoursDailyMinHours[sbg][0]>=0)
10867 							mhm=subgroupsMinHoursDailyMinHours[sbg][0];
10868 						assert(mhm>=mhd);
10869 						bool maxGapsZero=false;
10870 						if(subgroupsMaxGapsPerDayMaxGaps[sbg]==0 ||
10871 						 subgroupsMaxGapsPerWeekMaxGaps[sbg]==0 ||
10872 						 subgroupsMaxGapsPerRealDayMaxGaps[sbg]==0 ||
10873 						 subgroupsMaxGapsPerWeekForRealDaysMaxGaps[sbg]==0)
10874 							maxGapsZero=true;
10875 
10876 						int _nUsedMornings=0;
10877 						int _nUsedAfternoons=0;
10878 
10879 						int _nHours=0;
10880 						int _nfg=0;
10881 
10882 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
10883 							if(d2%2==0){ //morning
10884 								if(!maxGapsZero){
10885 									if(newSubgroupsDayNHours(sbg,d2)>0){
10886 										_nHours+=max(newSubgroupsDayNHours(sbg,d2), mhm);
10887 										_nUsedMornings++;
10888 									}
10889 								}
10890 								else{
10891 									if(newSubgroupsDayNHours(sbg,d2)>0){
10892 										_nHours+=max(newSubgroupsDayNHours(sbg,d2)+newSubgroupsDayNGaps(sbg,d2), mhm);
10893 										_nUsedMornings++;
10894 									}
10895 								}
10896 							}
10897 							else{ //afternoon
10898 								if(!maxGapsZero){
10899 									if(newSubgroupsDayNHours(sbg,d2)>0){
10900 										int _nh=newSubgroupsDayNHours(sbg,d2);
10901 										if(newSubgroupsDayNFirstGaps(sbg,d2)==1){
10902 											if(_nh<mhd){
10903 												_nh=mhd;
10904 											}
10905 											else{
10906 												_nfg++;
10907 											}
10908 										}
10909 										else if(newSubgroupsDayNFirstGaps(sbg,d2)>=2){
10910 											_nh++;
10911 										}
10912 
10913 										_nHours+=max(_nh, mhd);
10914 										_nUsedAfternoons++;
10915 									}
10916 								}
10917 								else{
10918 									if(newSubgroupsDayNHours(sbg,d2)>0){
10919 										int _nh=newSubgroupsDayNHours(sbg,d2)+newSubgroupsDayNGaps(sbg,d2);
10920 										if(newSubgroupsDayNFirstGaps(sbg,d2)==1){
10921 											if(_nh<mhd){
10922 												_nh=mhd;
10923 											}
10924 											else{
10925 												_nfg++;
10926 											}
10927 										}
10928 										else if(newSubgroupsDayNFirstGaps(sbg,d2)>=2){
10929 											_nh+=newSubgroupsDayNFirstGaps(sbg,d2)-1;
10930 											if(_nh<mhd){
10931 												_nh=mhd;
10932 											}
10933 											else{
10934 												_nfg++;
10935 											}
10936 										}
10937 
10938 										_nHours+=max(_nh, mhd);
10939 										_nUsedAfternoons++;
10940 									}
10941 								}
10942 							}
10943 						}
10944 
10945 						if(subgroupsAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]<_nfg)
10946 							_nHours+=_nfg-subgroupsAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
10947 
10948 						if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
10949 							if(subgroupsMinMorningsPerWeekMinMornings[sbg]>_nUsedMornings)
10950 								_nHours+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-_nUsedMornings)*mhm;
10951 
10952 						if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
10953 							if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>_nUsedAfternoons)
10954 								_nHours+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-_nUsedAfternoons)*mhd;
10955 
10956 						if(_nHours > nHoursPerSubgroup[sbg]){
10957 							if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
10958 								okstudentsafternoonsearlymaxbeginningsatsecondhour=false;
10959 								goto impossiblestudentsafternoonsearlymaxbeginningsatsecondhour;
10960 							}
10961 
10962 							getSbgTimetable(sbg, conflActivities[newtime]);
10963 							sbgGetNHoursGaps(sbg);
10964 
10965 							for(;;){
10966 								int nUsedMornings=0;
10967 								int nUsedAfternoons=0;
10968 
10969 								int nHours=0;
10970 								int nfg=0;
10971 
10972 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
10973 									if(d2%2==0){ //morning
10974 										if(!maxGapsZero){
10975 											if(sbgDayNHours[d2]>0){
10976 												nHours+=max(sbgDayNHours[d2], mhm);
10977 												nUsedMornings++;
10978 											}
10979 										}
10980 										else{
10981 											if(sbgDayNHours[d2]>0){
10982 												nHours+=max(sbgDayNHours[d2]+sbgDayNGaps[d2], mhm);
10983 												nUsedMornings++;
10984 											}
10985 										}
10986 									}
10987 									else{ //afternoon
10988 										if(!maxGapsZero){
10989 											if(sbgDayNHours[d2]>0){
10990 												int nh=sbgDayNHours[d2];
10991 												if(sbgDayNFirstGaps[d2]==1){
10992 													if(nh<mhd){
10993 														nh=mhd;
10994 													}
10995 													else{
10996 														nfg++;
10997 													}
10998 												}
10999 												else if(sbgDayNFirstGaps[d2]>=2){
11000 													nh++;
11001 												}
11002 
11003 												nHours+=max(nh, mhd);
11004 												nUsedAfternoons++;
11005 											}
11006 										}
11007 										else{
11008 											if(sbgDayNHours[d2]>0){
11009 												int nh=sbgDayNHours[d2]+sbgDayNGaps[d2];
11010 												if(sbgDayNFirstGaps[d2]==1){
11011 													if(nh<mhd){
11012 														nh=mhd;
11013 													}
11014 													else{
11015 														nfg++;
11016 													}
11017 												}
11018 												else if(sbgDayNFirstGaps[d2]>=2){
11019 													nh+=sbgDayNFirstGaps[d2]-1;
11020 													if(nh<mhd){
11021 														nh=mhd;
11022 													}
11023 													else{
11024 														nfg++;
11025 													}
11026 												}
11027 
11028 												nHours+=max(nh, mhd);
11029 												nUsedAfternoons++;
11030 											}
11031 										}
11032 									}
11033 								}
11034 
11035 								if(subgroupsAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]<nfg)
11036 									nHours+=nfg-subgroupsAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
11037 
11038 								if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
11039 									if(subgroupsMinMorningsPerWeekMinMornings[sbg]>nUsedMornings)
11040 										nHours+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-nUsedMornings)*mhm;
11041 
11042 								if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
11043 									if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>nUsedAfternoons)
11044 										nHours+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-nUsedAfternoons)*mhd;
11045 
11046 								int ai2=-1;
11047 
11048 								if(nHours > nHoursPerSubgroup[sbg]){
11049 									//remove an activity
11050 									bool k=subgroupRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
11051 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
11052 									if(!k){
11053 										if(maxGapsZero){
11054 											okstudentsafternoonsearlymaxbeginningsatsecondhour=false;
11055 											goto impossiblestudentsafternoonsearlymaxbeginningsatsecondhour;
11056 										}
11057 										else{
11058 											bool ka=subgroupRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
11059 											assert(conflActivities[newtime].count()==nConflActivities[newtime]);
11060 
11061 											if(!ka){
11062 												okstudentsafternoonsearlymaxbeginningsatsecondhour=false;
11063 												goto impossiblestudentsafternoonsearlymaxbeginningsatsecondhour;
11064 											}
11065 										}
11066 									}
11067 								}
11068 								else{ //OK
11069 									break;
11070 								}
11071 
11072 								assert(ai2>=0);
11073 
11074 								removeAi2FromSbgTimetable(ai2);
11075 								updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
11076 							}
11077 						}
11078 					}
11079 			}
11080 		}
11081 
11082 impossiblestudentsafternoonsearlymaxbeginningsatsecondhour:
11083 		if(!okstudentsafternoonsearlymaxbeginningsatsecondhour){
11084 			if(updateSubgroups || updateTeachers)
11085 				removeAiFromNewTimetable(ai, act, d, h);
11086 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
11087 
11088 			nConflActivities[newtime]=MAX_ACTIVITIES;
11089 			continue;
11090 		}
11091 
11092 		////////////////////////////END students afternoons early
11093 
11094 /////////////////////////////////////////////////////////////////////////////////////////////
11095 
11096 		okstudentsmorningsearlymaxbeginningsatsecondhour=true;
11097 
11098 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
11099 			if(haveStudentsMorningsEarly){
11100 				for(int sbg : qAsConst(act->iSubgroupsList))
11101 					if(!skipRandom(subgroupsMorningsEarlyMaxBeginningsAtSecondHourPercentage[sbg])){
11102 						//preliminary check
11103 						int mhd=1; //min hours per day
11104 						if(subgroupsMinHoursDailyMinHours[sbg][1]>=0)
11105 							mhd=subgroupsMinHoursDailyMinHours[sbg][1];
11106 						assert(mhd>=1);
11107 						int mhm=mhd; //min hours per morning
11108 						if(subgroupsMinHoursDailyMinHours[sbg][0]>=0)
11109 							mhm=subgroupsMinHoursDailyMinHours[sbg][0];
11110 						assert(mhm>=mhd);
11111 						bool maxGapsZero=false;
11112 						if(subgroupsMaxGapsPerDayMaxGaps[sbg]==0 ||
11113 						 subgroupsMaxGapsPerWeekMaxGaps[sbg]==0 ||
11114 						 subgroupsMaxGapsPerRealDayMaxGaps[sbg]==0 ||
11115 						 subgroupsMaxGapsPerWeekForRealDaysMaxGaps[sbg]==0)
11116 							maxGapsZero=true;
11117 
11118 						int _nUsedMornings=0;
11119 						int _nUsedAfternoons=0;
11120 
11121 						int _nHours=0;
11122 						int _nfg=0;
11123 
11124 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
11125 							if(d2%2==0){ //morning
11126 								if(!maxGapsZero){
11127 									if(newSubgroupsDayNHours(sbg,d2)>0){
11128 										int _nh=newSubgroupsDayNHours(sbg,d2);
11129 										if(newSubgroupsDayNFirstGaps(sbg,d2)==1){
11130 											if(_nh<mhm){
11131 												_nh=mhm;
11132 											}
11133 											else{
11134 												_nfg++;
11135 											}
11136 										}
11137 										else if(newSubgroupsDayNFirstGaps(sbg,d2)>=2){
11138 											_nh++;
11139 										}
11140 
11141 										_nHours+=max(_nh, mhm);
11142 										_nUsedMornings++;
11143 									}
11144 								}
11145 								else{
11146 									if(newSubgroupsDayNHours(sbg,d2)>0){
11147 										int _nh=newSubgroupsDayNHours(sbg,d2)+newSubgroupsDayNGaps(sbg,d2);
11148 										if(newSubgroupsDayNFirstGaps(sbg,d2)==1){
11149 											if(_nh<mhm){
11150 												_nh=mhm;
11151 											}
11152 											else{
11153 												_nfg++;
11154 											}
11155 										}
11156 										else if(newSubgroupsDayNFirstGaps(sbg,d2)>=2){
11157 											_nh+=newSubgroupsDayNFirstGaps(sbg,d2)-1;
11158 											if(_nh<mhm){
11159 												_nh=mhm;
11160 											}
11161 											else{
11162 												_nfg++;
11163 											}
11164 										}
11165 
11166 										_nHours+=max(_nh, mhm);
11167 										_nUsedMornings++;
11168 									}
11169 								}
11170 							}
11171 							else{ //afternoon
11172 								if(!maxGapsZero){
11173 									if(newSubgroupsDayNHours(sbg,d2)>0){
11174 										_nHours+=max(newSubgroupsDayNHours(sbg,d2), mhd);
11175 										_nUsedAfternoons++;
11176 									}
11177 								}
11178 								else{
11179 									if(newSubgroupsDayNHours(sbg,d2)>0){
11180 										_nHours+=max(newSubgroupsDayNHours(sbg,d2)+newSubgroupsDayNGaps(sbg,d2), mhd);
11181 										_nUsedAfternoons++;
11182 									}
11183 								}
11184 							}
11185 						}
11186 
11187 						if(subgroupsMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]<_nfg)
11188 							_nHours+=_nfg-subgroupsMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
11189 
11190 						if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
11191 							if(subgroupsMinMorningsPerWeekMinMornings[sbg]>_nUsedMornings)
11192 								_nHours+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-_nUsedMornings)*mhm;
11193 
11194 						if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
11195 							if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>_nUsedAfternoons)
11196 								_nHours+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-_nUsedAfternoons)*mhd;
11197 
11198 						if(_nHours > nHoursPerSubgroup[sbg]){
11199 							if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
11200 								okstudentsmorningsearlymaxbeginningsatsecondhour=false;
11201 								goto impossiblestudentsmorningsearlymaxbeginningsatsecondhour;
11202 							}
11203 
11204 							getSbgTimetable(sbg, conflActivities[newtime]);
11205 							sbgGetNHoursGaps(sbg);
11206 
11207 							for(;;){
11208 								int nUsedMornings=0;
11209 								int nUsedAfternoons=0;
11210 
11211 								int nHours=0;
11212 								int nfg=0;
11213 
11214 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
11215 									if(d2%2==0){ //morning
11216 										if(!maxGapsZero){
11217 											if(sbgDayNHours[d2]>0){
11218 												int nh=sbgDayNHours[d2];
11219 												if(sbgDayNFirstGaps[d2]==1){
11220 													if(nh<mhm){
11221 														nh=mhm;
11222 													}
11223 													else{
11224 														nfg++;
11225 													}
11226 												}
11227 												else if(sbgDayNFirstGaps[d2]>=2){
11228 													nh++;
11229 												}
11230 
11231 												nHours+=max(nh, mhm);
11232 												nUsedMornings++;
11233 											}
11234 										}
11235 										else{
11236 											if(sbgDayNHours[d2]>0){
11237 												int nh=sbgDayNHours[d2]+sbgDayNGaps[d2];
11238 												if(sbgDayNFirstGaps[d2]==1){
11239 													if(nh<mhm){
11240 														nh=mhm;
11241 													}
11242 													else{
11243 														nfg++;
11244 													}
11245 												}
11246 												else if(sbgDayNFirstGaps[d2]>=2){
11247 													nh+=sbgDayNFirstGaps[d2]-1;
11248 													if(nh<mhm){
11249 														nh=mhm;
11250 													}
11251 													else{
11252 														nfg++;
11253 													}
11254 												}
11255 
11256 												nHours+=max(nh, mhm);
11257 												nUsedMornings++;
11258 											}
11259 										}
11260 									}
11261 									else{ //afternoon
11262 										if(!maxGapsZero){
11263 											if(sbgDayNHours[d2]>0){
11264 												nHours+=max(sbgDayNHours[d2], mhd);
11265 												nUsedAfternoons++;
11266 											}
11267 										}
11268 										else{
11269 											if(sbgDayNHours[d2]>0){
11270 												nHours+=max(sbgDayNHours[d2]+sbgDayNGaps[d2], mhd);
11271 												nUsedAfternoons++;
11272 											}
11273 										}
11274 									}
11275 								}
11276 
11277 								if(subgroupsMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]<nfg)
11278 									nHours+=nfg-subgroupsMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
11279 
11280 								if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
11281 									if(subgroupsMinMorningsPerWeekMinMornings[sbg]>nUsedMornings)
11282 										nHours+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-nUsedMornings)*mhm;
11283 
11284 								if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
11285 									if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>nUsedAfternoons)
11286 										nHours+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-nUsedAfternoons)*mhd;
11287 
11288 								int ai2=-1;
11289 
11290 								if(nHours > nHoursPerSubgroup[sbg]){
11291 									//remove an activity
11292 									bool k=subgroupRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
11293 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
11294 									if(!k){
11295 										if(maxGapsZero){
11296 											okstudentsmorningsearlymaxbeginningsatsecondhour=false;
11297 											goto impossiblestudentsmorningsearlymaxbeginningsatsecondhour;
11298 										}
11299 										else{
11300 											bool ka=subgroupRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
11301 											assert(conflActivities[newtime].count()==nConflActivities[newtime]);
11302 
11303 											if(!ka){
11304 												okstudentsmorningsearlymaxbeginningsatsecondhour=false;
11305 												goto impossiblestudentsmorningsearlymaxbeginningsatsecondhour;
11306 											}
11307 										}
11308 									}
11309 								}
11310 								else{ //OK
11311 									break;
11312 								}
11313 
11314 								assert(ai2>=0);
11315 
11316 								removeAi2FromSbgTimetable(ai2);
11317 								updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
11318 							}
11319 						}
11320 					}
11321 			}
11322 		}
11323 
11324 impossiblestudentsmorningsearlymaxbeginningsatsecondhour:
11325 		if(!okstudentsmorningsearlymaxbeginningsatsecondhour){
11326 			if(updateSubgroups || updateTeachers)
11327 				removeAiFromNewTimetable(ai, act, d, h);
11328 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
11329 
11330 			nConflActivities[newtime]=MAX_ACTIVITIES;
11331 			continue;
11332 		}
11333 
11334 		////////////////////////////END students mornings early
11335 
11336 /////////////////////////////////////////////////////////////////////////////////////////////
11337 
11338 		//not breaking the students max afternoons per week constraints
11339 		////////////////////////////BEGIN max afternoons per week for students
11340 		okstudentsmaxafternoonsperweek=true;
11341 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
11342 			//for(int tch : qAsConst(act->iTeachersList)){
11343 			for(int sbg : qAsConst(subgroupsWithMaxAfternoonsPerWeekForActivities[ai])){
11344 				if(skipRandom(subgroupsMaxAfternoonsPerWeekWeightPercentages[sbg]))
11345 					continue;
11346 
11347 				int maxAfternoons=subgroupsMaxAfternoonsPerWeekMaxAfternoons[sbg];
11348 				assert(maxAfternoons>=0); //the list contains real information
11349 
11350 				//preliminary test
11351 				int _nOc=0;
11352 				for(int d2=1; d2<gt.rules.nDaysPerWeek; d2+=2)
11353 					//if(newTeachersDayNHours(tch,d2)>0)
11354 
11355 					//IT IS VITAL TO USE teacherActivitiesOfTheDay as a QList<int> (tch,d2)!!!!!!!
11356 					//The order of evaluation of activities is changed,
11357 					//with activities which were moved forward and back again
11358 					//being put at the end.
11359 					//If you do not follow this, you'll get impossible timetables
11360 					//for the Italian example Italy/2007/difficult/highschool-Ancona.fet or the examples from
11361 					//South Africa: South-Africa/difficult/Collegiate_Junior_School2.fet or
11362 					//South-Africa/difficult/Insight_Learning_Centre2.fet, I am not sure which of these 3
11363 
11364 					if(subgroupActivitiesOfTheDay(sbg,d2).count()>0 || d2==d)
11365 						_nOc++;
11366 				if(_nOc<=maxAfternoons)
11367 					continue; //OK, preliminary
11368 
11369 				if(maxAfternoons>=0){
11370 					assert(maxAfternoons>0);
11371 
11372 					//getTchTimetable(tch, conflActivities[newtime]);
11373 					//tchGetNHoursGaps(tch);
11374 
11375 					//bool occupiedDay[MAX_DAYS_PER_WEEK];
11376 					//bool canEmptyDay[MAX_DAYS_PER_WEEK];
11377 
11378 					//int _minWrong[MAX_DAYS_PER_WEEK];
11379 					//int _nWrong[MAX_DAYS_PER_WEEK];
11380 					//int _nConflActivities[MAX_DAYS_PER_WEEK];
11381 					//int _minIndexAct[MAX_DAYS_PER_WEEK];
11382 
11383 					//QList<int> _activitiesForDay[MAX_DAYS_PER_WEEK];
11384 
11385 					for(int d2=1; d2<gt.rules.nDaysPerWeek; d2+=2){
11386 						if(d2==d)
11387 							continue;
11388 
11389 						occupiedDay[d2]=false;
11390 						canEmptyDay[d2]=true;
11391 
11392 						_minWrong[d2]=INF;
11393 						_nWrong[d2]=0;
11394 						_nConflActivities[d2]=0;
11395 						_minIndexAct[d2]=gt.rules.nInternalActivities;
11396 						_activitiesForDay[d2].clear();
11397 
11398 						for(int ai2 : qAsConst(subgroupActivitiesOfTheDay(sbg,d2))){
11399 							if(ai2>=0){
11400 								if(!conflActivities[newtime].contains(ai2)){
11401 									occupiedDay[d2]=true;
11402 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
11403 										canEmptyDay[d2]=false;
11404 									else if(!_activitiesForDay[d2].contains(ai2)){
11405 										_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
11406 										_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
11407 										_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
11408 										_nConflActivities[d2]++;
11409 										_activitiesForDay[d2].append(ai2);
11410 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
11411 									}
11412 								}
11413 							}
11414 						}
11415 
11416 						if(!occupiedDay[d2])
11417 							canEmptyDay[d2]=false;
11418 					}
11419 					occupiedDay[d]=true;
11420 					canEmptyDay[d]=false;
11421 
11422 					int nOc=0;
11423 					bool canChooseDay=false;
11424 
11425 					for(int j=1; j<gt.rules.nDaysPerWeek; j+=2)
11426 						if(occupiedDay[j]){
11427 							nOc++;
11428 							if(canEmptyDay[j]){
11429 								canChooseDay=true;
11430 							}
11431 						}
11432 
11433 					if(nOc>maxAfternoons){
11434 						assert(nOc==maxAfternoons+1);
11435 
11436 						if(!canChooseDay){
11437 							if(level==0){
11438 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
11439 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
11440 							}
11441 							okstudentsmaxafternoonsperweek=false;
11442 							goto impossiblestudentsmaxafternoonsperweek;
11443 						}
11444 
11445 						int d2=-1;
11446 
11447 						if(level!=0){
11448 							//choose random day from those with minimum number of conflicting activities
11449 							QList<int> candidateDays;
11450 
11451 							int m=gt.rules.nInternalActivities;
11452 
11453 							for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2)
11454 								if(canEmptyDay[kk])
11455 									if(m>_nConflActivities[kk])
11456 										m=_nConflActivities[kk];
11457 
11458 							candidateDays.clear();
11459 							for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2)
11460 								if(canEmptyDay[kk] && m==_nConflActivities[kk])
11461 									candidateDays.append(kk);
11462 
11463 							assert(candidateDays.count()>0);
11464 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
11465 						}
11466 						else{ //level==0
11467 							QList<int> candidateDays;
11468 
11469 							int _mW=INF;
11470 							int _nW=INF;
11471 							int _mCA=gt.rules.nInternalActivities;
11472 							int _mIA=gt.rules.nInternalActivities;
11473 
11474 							for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2)
11475 								if(canEmptyDay[kk]){
11476 									if(_mW>_minWrong[kk] ||
11477 									 (_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
11478 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
11479 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
11480 										_mW=_minWrong[kk];
11481 										_nW=_nWrong[kk];
11482 										_mCA=_nConflActivities[kk];
11483 										_mIA=_minIndexAct[kk];
11484 									}
11485 								}
11486 
11487 							assert(_mW<INF);
11488 
11489 							candidateDays.clear();
11490 							for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2)
11491 								if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
11492 									candidateDays.append(kk);
11493 
11494 							assert(candidateDays.count()>0);
11495 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
11496 						}
11497 
11498 						assert(d2>=0);
11499 
11500 						assert(_activitiesForDay[d2].count()>0);
11501 
11502 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
11503 							assert(ai2!=ai);
11504 							assert(!swappedActivities[ai2]);
11505 							assert(!fixedTimeActivity[ai2]);
11506 							assert(!conflActivities[newtime].contains(ai2));
11507 							conflActivities[newtime].append(ai2);
11508 							nConflActivities[newtime]++;
11509 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
11510 							//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
11511 						}
11512 					}
11513 				}
11514 			}
11515 		}
11516 impossiblestudentsmaxafternoonsperweek:
11517 		if(!okstudentsmaxafternoonsperweek){
11518 			if(updateSubgroups || updateTeachers)
11519 				removeAiFromNewTimetable(ai, act, d, h);
11520 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
11521 
11522 			nConflActivities[newtime]=MAX_ACTIVITIES;
11523 			continue;
11524 		}
11525 
11526 		////////////////////////////END students max afternoons per week
11527 
11528 /////////////////////////////////////////////////////////////////////////////////////////////
11529 
11530 		//not breaking the students max mornings per week constraints
11531 		////////////////////////////BEGIN max mornings per week for students
11532 		okstudentsmaxmorningsperweek=true;
11533 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
11534 			//for(int tch : qAsConst(act->iTeachersList)){
11535 			for(int sbg : qAsConst(subgroupsWithMaxMorningsPerWeekForActivities[ai])){
11536 				if(skipRandom(subgroupsMaxMorningsPerWeekWeightPercentages[sbg]))
11537 					continue;
11538 
11539 				int maxMornings=subgroupsMaxMorningsPerWeekMaxMornings[sbg];
11540 				assert(maxMornings>=0); //the list contains real information
11541 
11542 				//preliminary test
11543 				int _nOc=0;
11544 				for(int d2=0; d2<gt.rules.nDaysPerWeek; d2+=2)
11545 					//if(newTeachersDayNHours(tch,d2)>0)
11546 
11547 					//IT IS VITAL TO USE teacherActivitiesOfTheDay as a QList<int> (tch,d2)!!!!!!!
11548 					//The order of evaluation of activities is changed,
11549 					//with activities which were moved forward and back again
11550 					//being put at the end.
11551 					//If you do not follow this, you'll get impossible timetables
11552 					//for the Italian example Italy/2007/difficult/highschool-Ancona.fet or the examples from
11553 					//South Africa: South-Africa/difficult/Collegiate_Junior_School2.fet or
11554 					//South-Africa/difficult/Insight_Learning_Centre2.fet, I am not sure which of these 3
11555 
11556 					if(subgroupActivitiesOfTheDay(sbg,d2).count()>0 || d2==d)
11557 						_nOc++;
11558 				if(_nOc<=maxMornings)
11559 					continue; //OK, preliminary
11560 
11561 				if(maxMornings>=0){
11562 					assert(maxMornings>0);
11563 
11564 					//getTchTimetable(tch, conflActivities[newtime]);
11565 					//tchGetNHoursGaps(tch);
11566 
11567 					//bool occupiedDay[MAX_DAYS_PER_WEEK];
11568 					//bool canEmptyDay[MAX_DAYS_PER_WEEK];
11569 
11570 					//int _minWrong[MAX_DAYS_PER_WEEK];
11571 					//int _nWrong[MAX_DAYS_PER_WEEK];
11572 					//int _nConflActivities[MAX_DAYS_PER_WEEK];
11573 					//int _minIndexAct[MAX_DAYS_PER_WEEK];
11574 
11575 					//QList<int> _activitiesForDay[MAX_DAYS_PER_WEEK];
11576 
11577 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2+=2){
11578 						if(d2==d)
11579 							continue;
11580 
11581 						occupiedDay[d2]=false;
11582 						canEmptyDay[d2]=true;
11583 
11584 						_minWrong[d2]=INF;
11585 						_nWrong[d2]=0;
11586 						_nConflActivities[d2]=0;
11587 						_minIndexAct[d2]=gt.rules.nInternalActivities;
11588 						_activitiesForDay[d2].clear();
11589 
11590 						for(int ai2 : qAsConst(subgroupActivitiesOfTheDay(sbg,d2))){
11591 							if(ai2>=0){
11592 								if(!conflActivities[newtime].contains(ai2)){
11593 									occupiedDay[d2]=true;
11594 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
11595 										canEmptyDay[d2]=false;
11596 									else if(!_activitiesForDay[d2].contains(ai2)){
11597 										_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
11598 										_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
11599 										_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
11600 										_nConflActivities[d2]++;
11601 										_activitiesForDay[d2].append(ai2);
11602 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
11603 									}
11604 								}
11605 							}
11606 						}
11607 
11608 						if(!occupiedDay[d2])
11609 							canEmptyDay[d2]=false;
11610 					}
11611 					occupiedDay[d]=true;
11612 					canEmptyDay[d]=false;
11613 
11614 					int nOc=0;
11615 					bool canChooseDay=false;
11616 
11617 					for(int j=0; j<gt.rules.nDaysPerWeek; j+=2)
11618 						if(occupiedDay[j]){
11619 							nOc++;
11620 							if(canEmptyDay[j]){
11621 								canChooseDay=true;
11622 							}
11623 						}
11624 
11625 					if(nOc>maxMornings){
11626 						assert(nOc==maxMornings+1);
11627 
11628 						if(!canChooseDay){
11629 							if(level==0){
11630 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
11631 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
11632 							}
11633 							okstudentsmaxmorningsperweek=false;
11634 							goto impossiblestudentsmaxmorningsperweek;
11635 						}
11636 
11637 						int d2=-1;
11638 
11639 						if(level!=0){
11640 							//choose random day from those with minimum number of conflicting activities
11641 							QList<int> candidateDays;
11642 
11643 							int m=gt.rules.nInternalActivities;
11644 
11645 							for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2)
11646 								if(canEmptyDay[kk])
11647 									if(m>_nConflActivities[kk])
11648 										m=_nConflActivities[kk];
11649 
11650 							candidateDays.clear();
11651 							for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2)
11652 								if(canEmptyDay[kk] && m==_nConflActivities[kk])
11653 									candidateDays.append(kk);
11654 
11655 							assert(candidateDays.count()>0);
11656 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
11657 						}
11658 						else{ //level==0
11659 							QList<int> candidateDays;
11660 
11661 							int _mW=INF;
11662 							int _nW=INF;
11663 							int _mCA=gt.rules.nInternalActivities;
11664 							int _mIA=gt.rules.nInternalActivities;
11665 
11666 							for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2)
11667 								if(canEmptyDay[kk]){
11668 									if(_mW>_minWrong[kk] ||
11669 									 (_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
11670 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
11671 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
11672 										_mW=_minWrong[kk];
11673 										_nW=_nWrong[kk];
11674 										_mCA=_nConflActivities[kk];
11675 										_mIA=_minIndexAct[kk];
11676 									}
11677 								}
11678 
11679 							assert(_mW<INF);
11680 
11681 							candidateDays.clear();
11682 							for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2)
11683 								if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
11684 									candidateDays.append(kk);
11685 
11686 							assert(candidateDays.count()>0);
11687 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
11688 						}
11689 
11690 						assert(d2>=0);
11691 
11692 						assert(_activitiesForDay[d2].count()>0);
11693 
11694 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
11695 							assert(ai2!=ai);
11696 							assert(!swappedActivities[ai2]);
11697 							assert(!fixedTimeActivity[ai2]);
11698 							assert(!conflActivities[newtime].contains(ai2));
11699 							conflActivities[newtime].append(ai2);
11700 							nConflActivities[newtime]++;
11701 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
11702 							//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
11703 						}
11704 					}
11705 				}
11706 			}
11707 		}
11708 
11709 impossiblestudentsmaxmorningsperweek:
11710 		if(!okstudentsmaxmorningsperweek){
11711 			if(updateSubgroups || updateTeachers)
11712 				removeAiFromNewTimetable(ai, act, d, h);
11713 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
11714 
11715 			nConflActivities[newtime]=MAX_ACTIVITIES;
11716 			continue;
11717 		}
11718 		////////////////////////////END students max mornings per week
11719 
11720 /////////////////////////////////////////////////////////////////////////////////////////////
11721 
11722 		//BEGIN students interval max days per week
11723 		okstudentsintervalmaxdaysperweek=true;
11724 		for(int sbg : qAsConst(act->iSubgroupsList)){
11725 			for(int cnt=0; cnt<subgroupsIntervalMaxDaysPerWeekPercentages[sbg].count(); cnt++){
11726 				double perc=subgroupsIntervalMaxDaysPerWeekPercentages[sbg].at(cnt);
11727 				int maxDays=subgroupsIntervalMaxDaysPerWeekMaxDays[sbg].at(cnt);
11728 				int sth=subgroupsIntervalMaxDaysPerWeekIntervalStart[sbg].at(cnt);
11729 				int endh=subgroupsIntervalMaxDaysPerWeekIntervalEnd[sbg].at(cnt);
11730 
11731 				assert(perc>=0);
11732 				assert(sth>=0 && sth<gt.rules.nHoursPerDay);
11733 				assert(endh>sth && endh<=gt.rules.nHoursPerDay);
11734 				assert(maxDays>=0 && maxDays<=gt.rules.nDaysPerWeek);
11735 
11736 				if(skipRandom(perc))
11737 					continue;
11738 
11739 				assert(perc==100.0);
11740 
11741 				bool foundothers=false;
11742 				bool foundai=false;
11743 				for(int hh=sth; hh<endh; hh++){
11744 					if(newSubgroupsTimetable(sbg,d,hh)==ai){
11745 						foundai=true;
11746 					}
11747 					else{
11748 						assert(newSubgroupsTimetable(sbg,d,hh)==subgroupsTimetable(sbg,d,hh));
11749 						if(newSubgroupsTimetable(sbg,d,hh)>=0){
11750 							if(!conflActivities[newtime].contains(newSubgroupsTimetable(sbg,d,hh))){
11751 								foundothers=true;
11752 							}
11753 						}
11754 					}
11755 				}
11756 				int nrotherdays=0;
11757 				for(int dd=0; dd<gt.rules.nDaysPerWeek; dd++){
11758 					if(dd!=d){
11759 						for(int hh=sth; hh<endh; hh++){
11760 							assert(newSubgroupsTimetable(sbg,dd,hh)==subgroupsTimetable(sbg,dd,hh));
11761 							if(newSubgroupsTimetable(sbg,dd,hh)>=0 && !conflActivities[newtime].contains(newSubgroupsTimetable(sbg,dd,hh))){
11762 								nrotherdays++;
11763 								break;
11764 							}
11765 						}
11766 					}
11767 				}
11768 				assert(nrotherdays<=maxDays); //if percentage==100%, then it is impossible to break this constraint
11769 				if((foundai && !foundothers) && nrotherdays==maxDays){ //increased above limit
11770 					if(level>0){
11771 						//bool occupiedIntervalDay[MAX_DAYS_PER_WEEK];
11772 						//bool canEmptyIntervalDay[MAX_DAYS_PER_WEEK];
11773 
11774 						///int _minWrong[MAX_DAYS_PER_WEEK];
11775 						///int _nWrong[MAX_DAYS_PER_WEEK];
11776 						//int _nConflActivities[MAX_DAYS_PER_WEEK];
11777 						///int _minIndexAct[MAX_DAYS_PER_WEEK];
11778 
11779 						//QList<int> _activitiesForIntervalDay[MAX_DAYS_PER_WEEK];
11780 
11781 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
11782 							if(d2==d)
11783 								continue;
11784 
11785 							occupiedIntervalDay[d2]=false;
11786 							canEmptyIntervalDay[d2]=true;
11787 
11788 							//_minWrong[d2]=INF;
11789 							//_nWrong[d2]=0;
11790 							_nConflActivities[d2]=0;
11791 							//_minIndexAct[d2]=gt.rules.nInternalActivities;
11792 							_activitiesForIntervalDay[d2].clear();
11793 
11794 							for(int h2=sth; h2<endh; h2++){
11795 								int ai2=subgroupsTimetable(sbg,d2,h2);
11796 								if(ai2>=0){
11797 									if(!conflActivities[newtime].contains(ai2)){
11798 										occupiedIntervalDay[d2]=true;
11799 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
11800 											canEmptyIntervalDay[d2]=false;
11801 										else if(!_activitiesForIntervalDay[d2].contains(ai2)){
11802 											//_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
11803 											//_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
11804 											//_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
11805 											_nConflActivities[d2]++;
11806 											_activitiesForIntervalDay[d2].append(ai2);
11807 											assert(_nConflActivities[d2]==_activitiesForIntervalDay[d2].count());
11808 										}
11809 									}
11810 								}
11811 							}
11812 
11813 							if(!occupiedIntervalDay[d2])
11814 								canEmptyIntervalDay[d2]=false;
11815 						}
11816 						occupiedIntervalDay[d]=true;
11817 						canEmptyIntervalDay[d]=false;
11818 
11819 						int nOc=0;
11820 						bool canChooseDay=false;
11821 
11822 						for(int j=0; j<gt.rules.nDaysPerWeek; j++)
11823 							if(occupiedIntervalDay[j]){
11824 								nOc++;
11825 								if(canEmptyIntervalDay[j]){
11826 									canChooseDay=true;
11827 								}
11828 							}
11829 
11830 						assert(nOc==maxDays+1);
11831 
11832 						if(!canChooseDay){
11833 							if(level==0){
11834 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
11835 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
11836 							}
11837 							okstudentsintervalmaxdaysperweek=false;
11838 							goto impossiblestudentsintervalmaxdaysperweek;
11839 						}
11840 
11841 						int d2=-1;
11842 
11843 						/////////////////////
11844 						//choose a random day from those with minimum number of conflicting activities
11845 						QList<int> candidateDays;
11846 
11847 						int m=gt.rules.nInternalActivities;
11848 
11849 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
11850 							if(canEmptyIntervalDay[kk])
11851 								if(m>_nConflActivities[kk])
11852 									m=_nConflActivities[kk];
11853 
11854 						candidateDays.clear();
11855 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
11856 							if(canEmptyIntervalDay[kk])
11857 								if(m==_nConflActivities[kk])
11858 									candidateDays.append(kk);
11859 
11860 						assert(candidateDays.count()>0);
11861 						d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
11862 						/////////////////////
11863 
11864 						assert(d2>=0);
11865 
11866 						assert(_activitiesForIntervalDay[d2].count()>0);
11867 
11868 						for(int ai2 : qAsConst(_activitiesForIntervalDay[d2])){
11869 							assert(ai2!=ai);
11870 							assert(!swappedActivities[ai2]);
11871 							assert(!fixedTimeActivity[ai2]);
11872 							assert(!conflActivities[newtime].contains(ai2));
11873 							conflActivities[newtime].append(ai2);
11874 							nConflActivities[newtime]++;
11875 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
11876 						}
11877 					}
11878 					else{
11879 						assert(level==0);
11880 
11881 						//bool occupiedIntervalDay[MAX_DAYS_PER_WEEK];
11882 						//bool canEmptyIntervalDay[MAX_DAYS_PER_WEEK];
11883 
11884 						//int _minWrong[MAX_DAYS_PER_WEEK];
11885 						//int _nWrong[MAX_DAYS_PER_WEEK];
11886 						//int _nConflActivities[MAX_DAYS_PER_WEEK];
11887 						//int _minIndexAct[MAX_DAYS_PER_WEEK];
11888 
11889 						//QList<int> _activitiesForIntervalDay[MAX_DAYS_PER_WEEK];
11890 
11891 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
11892 							if(d2==d)
11893 								continue;
11894 
11895 							occupiedIntervalDay[d2]=false;
11896 							canEmptyIntervalDay[d2]=true;
11897 
11898 							_minWrong[d2]=INF;
11899 							_nWrong[d2]=0;
11900 							_nConflActivities[d2]=0;
11901 							_minIndexAct[d2]=gt.rules.nInternalActivities;
11902 							_activitiesForIntervalDay[d2].clear();
11903 
11904 							for(int h2=sth; h2<endh; h2++){
11905 								int ai2=subgroupsTimetable(sbg,d2,h2);
11906 								if(ai2>=0){
11907 									if(!conflActivities[newtime].contains(ai2)){
11908 										occupiedIntervalDay[d2]=true;
11909 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
11910 											canEmptyIntervalDay[d2]=false;
11911 										else if(!_activitiesForIntervalDay[d2].contains(ai2)){
11912 											_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
11913 											_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
11914 											_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
11915 											_nConflActivities[d2]++;
11916 											_activitiesForIntervalDay[d2].append(ai2);
11917 											assert(_nConflActivities[d2]==_activitiesForIntervalDay[d2].count());
11918 										}
11919 									}
11920 								}
11921 							}
11922 
11923 							if(!occupiedIntervalDay[d2])
11924 								canEmptyIntervalDay[d2]=false;
11925 						}
11926 						occupiedIntervalDay[d]=true;
11927 						canEmptyIntervalDay[d]=false;
11928 
11929 						int nOc=0;
11930 						bool canChooseDay=false;
11931 
11932 						for(int j=0; j<gt.rules.nDaysPerWeek; j++)
11933 							if(occupiedIntervalDay[j]){
11934 								nOc++;
11935 								if(canEmptyIntervalDay[j]){
11936 									canChooseDay=true;
11937 								}
11938 							}
11939 
11940 						assert(nOc==maxDays+1);
11941 
11942 						if(!canChooseDay){
11943 							if(level==0){
11944 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
11945 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
11946 							}
11947 							okstudentsintervalmaxdaysperweek=false;
11948 							goto impossiblestudentsintervalmaxdaysperweek;
11949 						}
11950 
11951 						int d2=-1;
11952 
11953 						/////////////////////
11954 						QList<int> candidateDays;
11955 
11956 						int _mW=INF;
11957 						int _nW=INF;
11958 						int _mCA=gt.rules.nInternalActivities;
11959 						int _mIA=gt.rules.nInternalActivities;
11960 
11961 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
11962 							if(canEmptyIntervalDay[kk]){
11963 								if(_mW>_minWrong[kk] ||
11964 								(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
11965 								(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
11966 								(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
11967 									_mW=_minWrong[kk];
11968 									_nW=_nWrong[kk];
11969 									_mCA=_nConflActivities[kk];
11970 									_mIA=_minIndexAct[kk];
11971 								}
11972 							}
11973 
11974 						assert(_mW<INF);
11975 
11976 						candidateDays.clear();
11977 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
11978 							if(canEmptyIntervalDay[kk])
11979 								if(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
11980 									candidateDays.append(kk);
11981 
11982 						assert(candidateDays.count()>0);
11983 						d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
11984 						////////////////////
11985 
11986 						assert(d2>=0);
11987 
11988 						assert(_activitiesForIntervalDay[d2].count()>0);
11989 
11990 						for(int ai2 : qAsConst(_activitiesForIntervalDay[d2])){
11991 							assert(ai2!=ai);
11992 							assert(!swappedActivities[ai2]);
11993 							assert(!fixedTimeActivity[ai2]);
11994 							assert(!conflActivities[newtime].contains(ai2));
11995 							conflActivities[newtime].append(ai2);
11996 							nConflActivities[newtime]++;
11997 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
11998 						}
11999 					}
12000 				}
12001 			}
12002 		}
12003 		//respecting students interval max days per week
12004 impossiblestudentsintervalmaxdaysperweek:
12005 		if(!okstudentsintervalmaxdaysperweek){
12006 			if(updateSubgroups || updateTeachers)
12007 				removeAiFromNewTimetable(ai, act, d, h);
12008 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
12009 
12010 			nConflActivities[newtime]=MAX_ACTIVITIES;
12011 			continue;
12012 		}
12013 
12014 		////////////////////////////END students interval max days per week
12015 
12016 /////////////////////////////////////////////////////////////////////////////////////////////
12017 
12018 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
12019 			//BEGIN students morning interval max days per week
12020 			if(d%2==0){ //d is a morning
12021 				okstudentsmorningintervalmaxdaysperweek=true;
12022 				for(int sbg : qAsConst(act->iSubgroupsList)){
12023 					for(int cnt=0; cnt<subgroupsMorningIntervalMaxDaysPerWeekPercentages[sbg].count(); cnt++){
12024 						double perc=subgroupsMorningIntervalMaxDaysPerWeekPercentages[sbg].at(cnt);
12025 						int maxDays=subgroupsMorningIntervalMaxDaysPerWeekMaxDays[sbg].at(cnt);
12026 						int sth=subgroupsMorningIntervalMaxDaysPerWeekIntervalStart[sbg].at(cnt);
12027 						int endh=subgroupsMorningIntervalMaxDaysPerWeekIntervalEnd[sbg].at(cnt);
12028 
12029 						assert(perc>=0);
12030 						assert(sth>=0 && sth<gt.rules.nHoursPerDay);
12031 						assert(endh>sth && endh<=gt.rules.nHoursPerDay);
12032 						assert(maxDays>=0 && maxDays<=gt.rules.nDaysPerWeek/2);
12033 
12034 						if(skipRandom(perc))
12035 							continue;
12036 
12037 						assert(perc==100.0);
12038 
12039 						bool foundothers=false;
12040 						bool foundai=false;
12041 						for(int hh=sth; hh<endh; hh++){
12042 							if(newSubgroupsTimetable(sbg,d,hh)==ai){
12043 								foundai=true;
12044 							}
12045 							else{
12046 								assert(newSubgroupsTimetable(sbg,d,hh)==subgroupsTimetable(sbg,d,hh));
12047 								if(newSubgroupsTimetable(sbg,d,hh)>=0){
12048 									if(!conflActivities[newtime].contains(newSubgroupsTimetable(sbg,d,hh))){
12049 										foundothers=true;
12050 									}
12051 								}
12052 							}
12053 						}
12054 						int nrotherdays=0;
12055 						for(int dd=0; dd<gt.rules.nDaysPerWeek; dd+=2){ //morning
12056 							if(dd!=d){
12057 								for(int hh=sth; hh<endh; hh++){
12058 									assert(newSubgroupsTimetable(sbg,dd,hh)==subgroupsTimetable(sbg,dd,hh));
12059 									if(newSubgroupsTimetable(sbg,dd,hh)>=0 && !conflActivities[newtime].contains(newSubgroupsTimetable(sbg,dd,hh))){
12060 										nrotherdays++;
12061 										break;
12062 									}
12063 								}
12064 							}
12065 						}
12066 						assert(nrotherdays<=maxDays); //if percentage==100%, then it is impossible to break this constraint
12067 						if((foundai && !foundothers) && nrotherdays==maxDays){
12068 							//increased above limit
12069 							//bool occupiedIntervalDay[MAX_DAYS_PER_WEEK];
12070 							//bool canEmptyIntervalDay[MAX_DAYS_PER_WEEK];
12071 
12072 							//int _minWrong[MAX_DAYS_PER_WEEK];
12073 							//int _nWrong[MAX_DAYS_PER_WEEK];
12074 							//int _nConflActivities[MAX_DAYS_PER_WEEK];
12075 							//int _minIndexAct[MAX_DAYS_PER_WEEK];
12076 
12077 							//QList<int> _activitiesForIntervalDay[MAX_DAYS_PER_WEEK];
12078 
12079 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2+=2){ //morning
12080 								if(d2==d)
12081 									continue;
12082 
12083 								occupiedIntervalDay[d2]=false;
12084 								canEmptyIntervalDay[d2]=true;
12085 
12086 								_minWrong[d2]=INF;
12087 								_nWrong[d2]=0;
12088 								_nConflActivities[d2]=0;
12089 								_minIndexAct[d2]=gt.rules.nInternalActivities;
12090 								_activitiesForIntervalDay[d2].clear();
12091 
12092 								for(int h2=sth; h2<endh; h2++){
12093 									int ai2=subgroupsTimetable(sbg,d2,h2);
12094 								//for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
12095 									if(ai2>=0){
12096 										if(!conflActivities[newtime].contains(ai2)){
12097 											occupiedIntervalDay[d2]=true;
12098 											if(fixedTimeActivity[ai2] || swappedActivities[ai2])
12099 												canEmptyIntervalDay[d2]=false;
12100 											else if(!_activitiesForIntervalDay[d2].contains(ai2)){
12101 												_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
12102 												_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
12103 												_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
12104 												_nConflActivities[d2]++;
12105 												_activitiesForIntervalDay[d2].append(ai2);
12106 												assert(_nConflActivities[d2]==_activitiesForIntervalDay[d2].count());
12107 											}
12108 										}
12109 									}
12110 								}
12111 
12112 								if(!occupiedIntervalDay[d2])
12113 									canEmptyIntervalDay[d2]=false;
12114 							}
12115 							occupiedIntervalDay[d]=true;
12116 							canEmptyIntervalDay[d]=false;
12117 
12118 							int nOc=0;
12119 							bool canChooseDay=false;
12120 
12121 							for(int j=0; j<gt.rules.nDaysPerWeek; j+=2) //morning
12122 								if(occupiedIntervalDay[j]){
12123 									nOc++;
12124 									if(canEmptyIntervalDay[j]){
12125 										canChooseDay=true;
12126 									}
12127 								}
12128 
12129 							//if(nOc>maxDays){
12130 							assert(nOc==maxDays+1);
12131 
12132 							if(!canChooseDay){
12133 								if(level==0){
12134 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
12135 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
12136 								}
12137 								okstudentsmorningintervalmaxdaysperweek=false;
12138 								goto impossiblestudentsmorningintervalmaxdaysperweek;
12139 							}
12140 
12141 							int d2=-1;
12142 
12143 							if(level!=0){
12144 								//choose random day from those with minimum number of conflicting activities
12145 								QList<int> candidateDays;
12146 
12147 								int m=gt.rules.nInternalActivities;
12148 
12149 								for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2) //morning
12150 									if(canEmptyIntervalDay[kk])
12151 										if(m>_nConflActivities[kk])
12152 											m=_nConflActivities[kk];
12153 
12154 								candidateDays.clear();
12155 								for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2) //morning
12156 									if(canEmptyIntervalDay[kk] && m==_nConflActivities[kk])
12157 										candidateDays.append(kk);
12158 
12159 								assert(candidateDays.count()>0);
12160 								d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
12161 							}
12162 							else{ //level==0
12163 								QList<int> candidateDays;
12164 
12165 								int _mW=INF;
12166 								int _nW=INF;
12167 								int _mCA=gt.rules.nInternalActivities;
12168 								int _mIA=gt.rules.nInternalActivities;
12169 
12170 								for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2) //morning
12171 									if(canEmptyIntervalDay[kk]){
12172 										if(_mW>_minWrong[kk] ||
12173 										(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
12174 										(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
12175 										(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
12176 											_mW=_minWrong[kk];
12177 											_nW=_nWrong[kk];
12178 											_mCA=_nConflActivities[kk];
12179 											_mIA=_minIndexAct[kk];
12180 										}
12181 									}
12182 
12183 								assert(_mW<INF);
12184 
12185 								candidateDays.clear();
12186 								for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2) //morning
12187 									if(canEmptyIntervalDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
12188 										candidateDays.append(kk);
12189 
12190 								assert(candidateDays.count()>0);
12191 								d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
12192 							}
12193 
12194 							assert(d2>=0);
12195 
12196 							assert(_activitiesForIntervalDay[d2].count()>0);
12197 
12198 							for(int ai2 : qAsConst(_activitiesForIntervalDay[d2])){
12199 								assert(ai2!=ai);
12200 								assert(!swappedActivities[ai2]);
12201 								assert(!fixedTimeActivity[ai2]);
12202 								assert(!conflActivities[newtime].contains(ai2));
12203 								conflActivities[newtime].append(ai2);
12204 								nConflActivities[newtime]++;
12205 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
12206 								//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
12207 							}
12208 						}
12209 					}
12210 				}
12211 				//respecting students interval max days per week
12212 impossiblestudentsmorningintervalmaxdaysperweek:
12213 				if(!okstudentsmorningintervalmaxdaysperweek){
12214 					if(updateSubgroups || updateTeachers)
12215 						removeAiFromNewTimetable(ai, act, d, h);
12216 					//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
12217 
12218 					nConflActivities[newtime]=MAX_ACTIVITIES;
12219 					continue;
12220 				}
12221 			}
12222 		}
12223 
12224 		////////////////////////////END students morning interval max days per week
12225 
12226 /////////////////////////////////////////////////////////////////////////////////////////////
12227 
12228 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
12229 			//BEGIN students afternoon interval max days per week
12230 			if(d%2==1){ //d is an afternoon
12231 				okstudentsafternoonintervalmaxdaysperweek=true;
12232 				for(int sbg : qAsConst(act->iSubgroupsList)){
12233 					for(int cnt=0; cnt<subgroupsAfternoonIntervalMaxDaysPerWeekPercentages[sbg].count(); cnt++){
12234 						double perc=subgroupsAfternoonIntervalMaxDaysPerWeekPercentages[sbg].at(cnt);
12235 						int maxDays=subgroupsAfternoonIntervalMaxDaysPerWeekMaxDays[sbg].at(cnt);
12236 						int sth=subgroupsAfternoonIntervalMaxDaysPerWeekIntervalStart[sbg].at(cnt);
12237 						int endh=subgroupsAfternoonIntervalMaxDaysPerWeekIntervalEnd[sbg].at(cnt);
12238 
12239 						assert(perc>=0);
12240 						assert(sth>=0 && sth<gt.rules.nHoursPerDay);
12241 						assert(endh>sth && endh<=gt.rules.nHoursPerDay);
12242 						assert(maxDays>=0 && maxDays<=gt.rules.nDaysPerWeek/2);
12243 
12244 						if(skipRandom(perc))
12245 							continue;
12246 
12247 						assert(perc==100.0);
12248 
12249 						bool foundothers=false;
12250 						bool foundai=false;
12251 						for(int hh=sth; hh<endh; hh++){
12252 							if(newSubgroupsTimetable(sbg,d,hh)==ai){
12253 								foundai=true;
12254 							}
12255 							else{
12256 								assert(newSubgroupsTimetable(sbg,d,hh)==subgroupsTimetable(sbg,d,hh));
12257 								if(newSubgroupsTimetable(sbg,d,hh)>=0){
12258 									if(!conflActivities[newtime].contains(newSubgroupsTimetable(sbg,d,hh))){
12259 										foundothers=true;
12260 									}
12261 								}
12262 							}
12263 						}
12264 						int nrotherdays=0;
12265 						for(int dd=1; dd<gt.rules.nDaysPerWeek; dd+=2){ //afternoon
12266 							if(dd!=d){
12267 								for(int hh=sth; hh<endh; hh++){
12268 									assert(newSubgroupsTimetable(sbg,dd,hh)==subgroupsTimetable(sbg,dd,hh));
12269 									if(newSubgroupsTimetable(sbg,dd,hh)>=0 && !conflActivities[newtime].contains(newSubgroupsTimetable(sbg,dd,hh))){
12270 										nrotherdays++;
12271 										break;
12272 									}
12273 								}
12274 							}
12275 						}
12276 						assert(nrotherdays<=maxDays); //if percentage==100%, then it is impossible to break this constraint
12277 						if((foundai && !foundothers) && nrotherdays==maxDays){
12278 							//increased above limit
12279 							//bool occupiedIntervalDay[MAX_DAYS_PER_WEEK];
12280 							//bool canEmptyIntervalDay[MAX_DAYS_PER_WEEK];
12281 
12282 							//int _minWrong[MAX_DAYS_PER_WEEK];
12283 							//int _nWrong[MAX_DAYS_PER_WEEK];
12284 							//int _nConflActivities[MAX_DAYS_PER_WEEK];
12285 							//int _minIndexAct[MAX_DAYS_PER_WEEK];
12286 
12287 							//QList<int> _activitiesForIntervalDay[MAX_DAYS_PER_WEEK];
12288 
12289 							for(int d2=1; d2<gt.rules.nDaysPerWeek; d2+=2){ //afternoon
12290 								if(d2==d)
12291 									continue;
12292 
12293 								occupiedIntervalDay[d2]=false;
12294 								canEmptyIntervalDay[d2]=true;
12295 
12296 								_minWrong[d2]=INF;
12297 								_nWrong[d2]=0;
12298 								_nConflActivities[d2]=0;
12299 								_minIndexAct[d2]=gt.rules.nInternalActivities;
12300 								_activitiesForIntervalDay[d2].clear();
12301 
12302 								for(int h2=sth; h2<endh; h2++){
12303 									int ai2=subgroupsTimetable(sbg,d2,h2);
12304 								//for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
12305 									if(ai2>=0){
12306 										if(!conflActivities[newtime].contains(ai2)){
12307 											occupiedIntervalDay[d2]=true;
12308 											if(fixedTimeActivity[ai2] || swappedActivities[ai2])
12309 												canEmptyIntervalDay[d2]=false;
12310 											else if(!_activitiesForIntervalDay[d2].contains(ai2)){
12311 												_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
12312 												_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
12313 												_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
12314 												_nConflActivities[d2]++;
12315 												_activitiesForIntervalDay[d2].append(ai2);
12316 												assert(_nConflActivities[d2]==_activitiesForIntervalDay[d2].count());
12317 											}
12318 										}
12319 									}
12320 								}
12321 
12322 								if(!occupiedIntervalDay[d2])
12323 									canEmptyIntervalDay[d2]=false;
12324 							}
12325 							occupiedIntervalDay[d]=true;
12326 							canEmptyIntervalDay[d]=false;
12327 
12328 							int nOc=0;
12329 							bool canChooseDay=false;
12330 
12331 							for(int j=1; j<gt.rules.nDaysPerWeek; j+=2) //afternoon
12332 								if(occupiedIntervalDay[j]){
12333 									nOc++;
12334 									if(canEmptyIntervalDay[j]){
12335 										canChooseDay=true;
12336 									}
12337 								}
12338 
12339 							//if(nOc>maxDays){
12340 							assert(nOc==maxDays+1);
12341 
12342 							if(!canChooseDay){
12343 								if(level==0){
12344 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
12345 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
12346 								}
12347 								okstudentsafternoonintervalmaxdaysperweek=false;
12348 								goto impossiblestudentsafternoonintervalmaxdaysperweek;
12349 							}
12350 
12351 							int d2=-1;
12352 
12353 							if(level!=0){
12354 								//choose random day from those with minimum number of conflicting activities
12355 								QList<int> candidateDays;
12356 
12357 								int m=gt.rules.nInternalActivities;
12358 
12359 								for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2) //afternoon
12360 									if(canEmptyIntervalDay[kk])
12361 										if(m>_nConflActivities[kk])
12362 											m=_nConflActivities[kk];
12363 
12364 								candidateDays.clear();
12365 								for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2) //afternoon
12366 									if(canEmptyIntervalDay[kk] && m==_nConflActivities[kk])
12367 										candidateDays.append(kk);
12368 
12369 								assert(candidateDays.count()>0);
12370 								d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
12371 							}
12372 							else{ //level==0
12373 								QList<int> candidateDays;
12374 
12375 								int _mW=INF;
12376 								int _nW=INF;
12377 								int _mCA=gt.rules.nInternalActivities;
12378 								int _mIA=gt.rules.nInternalActivities;
12379 
12380 								for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2) //afternoon
12381 									if(canEmptyIntervalDay[kk]){
12382 										if(_mW>_minWrong[kk] ||
12383 										(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
12384 										(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
12385 										(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
12386 											_mW=_minWrong[kk];
12387 											_nW=_nWrong[kk];
12388 											_mCA=_nConflActivities[kk];
12389 											_mIA=_minIndexAct[kk];
12390 										}
12391 									}
12392 
12393 								assert(_mW<INF);
12394 
12395 								candidateDays.clear();
12396 								for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2) //afternoon
12397 									if(canEmptyIntervalDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
12398 										candidateDays.append(kk);
12399 
12400 								assert(candidateDays.count()>0);
12401 								d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
12402 							}
12403 
12404 							assert(d2>=0);
12405 
12406 							assert(_activitiesForIntervalDay[d2].count()>0);
12407 
12408 							for(int ai2 : qAsConst(_activitiesForIntervalDay[d2])){
12409 								assert(ai2!=ai);
12410 								assert(!swappedActivities[ai2]);
12411 								assert(!fixedTimeActivity[ai2]);
12412 								assert(!conflActivities[newtime].contains(ai2));
12413 								conflActivities[newtime].append(ai2);
12414 								nConflActivities[newtime]++;
12415 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
12416 								//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
12417 							}
12418 						}
12419 					}
12420 				}
12421 				//respecting students interval max days per week
12422 	impossiblestudentsafternoonintervalmaxdaysperweek:
12423 				if(!okstudentsafternoonintervalmaxdaysperweek){
12424 					if(updateSubgroups || updateTeachers)
12425 						removeAiFromNewTimetable(ai, act, d, h);
12426 					//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
12427 
12428 					nConflActivities[newtime]=MAX_ACTIVITIES;
12429 					continue;
12430 				}
12431 			}
12432 		}
12433 
12434 		////////////////////////////END students afternoon interval max days per week
12435 
12436 /////////////////////////////////////////////////////////////////////////////////////////////
12437 
12438 		////////////////////////////BEGIN students max span per day
12439 
12440 		//Rodolfo Ribeiro Gomes's code (https://bitbucket.org/rodolforg/fet/src/dev/) was a source of inspiration for the following constraint
12441 
12442 		okstudentsmaxspanperday=true;
12443 		for(int sbg : qAsConst(act->iSubgroupsList))
12444 			if(subgroupsMaxSpanPerDayPercentages[sbg]>=0){
12445 				//percentage is 100%
12446 				int maxSpanPerDay=subgroupsMaxSpanPerDayMaxSpan[sbg];
12447 
12448 				//preliminary test
12449 				int _cnt=0;
12450 				int _start=-1;
12451 				int _end=-1;
12452 				if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
12453 					if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0){
12454 						for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
12455 							if(!breakDayHour(d,h2) && !subgroupNotAvailableDayHour(sbg,d,h2)){
12456 								_start=h2;
12457 								break;
12458 							}
12459 						}
12460 					}
12461 					else{
12462 						int h2;
12463 						for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
12464 							if(newSubgroupsTimetable(sbg, d, h2)>=0){
12465 								_start=h2;
12466 								break;
12467 							}
12468 							else if(!breakDayHour(d,h2) && !subgroupNotAvailableDayHour(sbg,d,h2)){
12469 								break;
12470 							}
12471 						}
12472 						if(_start==-1){
12473 							h2++;
12474 							for(; h2<gt.rules.nHoursPerDay; h2++){
12475 								if(!breakDayHour(d,h2) && !subgroupNotAvailableDayHour(sbg,d,h2)){
12476 									_start=h2;
12477 									break;
12478 								}
12479 							}
12480 						}
12481 					}
12482 				}
12483 				else{
12484 					for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
12485 						if(newSubgroupsTimetable(sbg, d, h2)>=0){
12486 							_start=h2;
12487 							break;
12488 						}
12489 					}
12490 				}
12491 				for(int h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
12492 					if(newSubgroupsTimetable(sbg, d, h2)>=0){
12493 						_end=h2;
12494 						break;
12495 					}
12496 				}
12497 
12498 				if(_start>=0 && _end>=0 && _end>=_start)
12499 					_cnt=_end-_start+1;
12500 
12501 				if(_cnt<=maxSpanPerDay)
12502 					continue;
12503 
12504 				if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
12505 					okstudentsmaxspanperday=false;
12506 					goto impossiblestudentsmaxspanperday;
12507 				}
12508 
12509 				getSbgTimetable(sbg, conflActivities[newtime]);
12510 				updateSbgNHoursGaps(sbg, d); //needed for subgroupRemoveAnActivityFromBeginOrEndCertainDay or
12511 				//subgroupRemoveAnActivityFromEndCertainDay or subgroupRemoveAnActivityFromBeginCertainDay below
12512 
12513 				for(;;){
12514 					int cnt=0;
12515 					int start=-1;
12516 					int end=-1;
12517 
12518 					if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
12519 						if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0){
12520 							for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
12521 								if(!breakDayHour(d,h2) && !subgroupNotAvailableDayHour(sbg,d,h2)){
12522 									start=h2;
12523 									break;
12524 								}
12525 							}
12526 						}
12527 						else{
12528 							int h2;
12529 							for(h2=0; h2<gt.rules.nHoursPerDay; h2++){
12530 								if(sbgTimetable(d, h2)>=0){
12531 									start=h2;
12532 									break;
12533 								}
12534 								else if(!breakDayHour(d,h2) && !subgroupNotAvailableDayHour(sbg,d,h2)){
12535 									break;
12536 								}
12537 							}
12538 							if(start==-1){
12539 								h2++;
12540 								for(; h2<gt.rules.nHoursPerDay; h2++){
12541 									if(!breakDayHour(d,h2) && !subgroupNotAvailableDayHour(sbg,d,h2)){
12542 										start=h2;
12543 										break;
12544 									}
12545 								}
12546 							}
12547 						}
12548 					}
12549 					else{
12550 						for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
12551 							if(sbgTimetable(d, h2)>=0){
12552 								start=h2;
12553 								break;
12554 							}
12555 						}
12556 					}
12557 
12558 					for(int h2=gt.rules.nHoursPerDay-1; h2>=0; h2--){
12559 						if(sbgTimetable(d, h2)>=0){
12560 							end=h2;
12561 							break;
12562 						}
12563 					}
12564 
12565 					if(start>=0 && end>=0 && end>=start)
12566 						cnt=end-start+1;
12567 
12568 					if(cnt<=maxSpanPerDay)
12569 						break;
12570 
12571 					int ai2=-1;
12572 
12573 					if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
12574 						if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0){
12575 							bool k=subgroupRemoveAnActivityFromEndCertainDay(d, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
12576 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
12577 							if(!k){
12578 								if(level==0){
12579 									//old comment below
12580 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
12581 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
12582 								}
12583 								okstudentsmaxspanperday=false;
12584 								goto impossiblestudentsmaxspanperday;
12585 							}
12586 						}
12587 						else{
12588 							bool k=subgroupRemoveAnActivityFromEndCertainDay(d, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
12589 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
12590 							if(!k){
12591 								bool k2=false;
12592 
12593 								//The following code is theoretically better, but practically much worse on the file
12594 								//examples/Romania/Pedagogic-High-School-Tg-Mures/2007-2008_sem1-d-test-students-max-span-per-day.fet
12595 								//(it slows down with 25%-50% on average - you need to generate more timetables)
12596 								/*int firstAvailableHour=-1;
12597 								for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
12598 									if(!breakDayHour(d,h2) && !subgroupNotAvailableDayHour(sbg,d,h2)){
12599 										firstAvailableHour=h2;
12600 										break;
12601 									}
12602 								}
12603 								//We could assert(firstAvailableHour>=0), because the day is not empty, because the span is too large.
12604 								if(firstAvailableHour>=0){
12605 									assert(firstAvailableHour<gt.rules.nHoursPerDay);
12606 									if(sbgTimetable(d, firstAvailableHour)>=0){
12607 										k2=subgroupRemoveAnActivityFromBeginCertainDay(d, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
12608 										assert(conflActivities[newtime].count()==nConflActivities[newtime]);
12609 									}
12610 								}*/
12611 
12612 								if(!k2){
12613 									if(level==0){
12614 										//old comment below
12615 										//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
12616 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
12617 									}
12618 									okstudentsmaxspanperday=false;
12619 									goto impossiblestudentsmaxspanperday;
12620 								}
12621 							}
12622 						}
12623 					}
12624 					else{
12625 						bool k=subgroupRemoveAnActivityFromBeginOrEndCertainDay(d, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
12626 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
12627 						if(!k){
12628 							if(level==0){
12629 								//old comment below
12630 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
12631 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
12632 							}
12633 							okstudentsmaxspanperday=false;
12634 							goto impossiblestudentsmaxspanperday;
12635 						}
12636 					}
12637 
12638 					assert(ai2>=0);
12639 
12640 					removeAi2FromSbgTimetable(ai2);
12641 					sbgDayNHours[d]-=gt.rules.internalActivitiesList[ai2].duration; //needed for subgroupRemoveAnActivityFromBeginOrEndCertainDay or
12642 					//subgroupRemoveAnActivityFromEndCertainDay or subgroupRemoveAnActivityFromBeginCertainDay above
12643 					assert(sbgDayNHours[d]>=0);
12644 				}
12645 			}
12646 
12647 impossiblestudentsmaxspanperday:
12648 		if(!okstudentsmaxspanperday){
12649 			if(updateSubgroups || updateTeachers)
12650 				removeAiFromNewTimetable(ai, act, d, h);
12651 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
12652 
12653 			nConflActivities[newtime]=MAX_ACTIVITIES;
12654 			continue;
12655 		}
12656 
12657 		////////////////////////////END students max span per day
12658 
12659 /////////////////////////////////////////////////////////////////////////////////////////////
12660 
12661 		////////////////////////////BEGIN students max span per real day
12662 
12663 		//Rodolfo Ribeiro Gomes's code (https://bitbucket.org/rodolforg/fet/src/dev/) was a source of inspiration for the following constraint
12664 
12665 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
12666 			okstudentsmaxspanperrealday=true;
12667 			for(int sbg : qAsConst(act->iSubgroupsList))
12668 				if(subgroupsMaxSpanPerRealDayPercentages[sbg]>=0){
12669 					//percentage is 100%
12670 					int maxSpanPerDay=subgroupsMaxSpanPerRealDayMaxSpan[sbg];
12671 
12672 					//preliminary test
12673 					int _cnt=0;
12674 					int _start=-1;
12675 					int _end=-1;
12676 					if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
12677 						if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0){
12678 							for(int h2=0; h2<2*gt.rules.nHoursPerDay; h2++){
12679 								int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
12680 								int h3=h2%gt.rules.nHoursPerDay;
12681 								if(!breakDayHour(d3,h3) && !subgroupNotAvailableDayHour(sbg,d3,h3)){
12682 									_start=h2;
12683 									break;
12684 								}
12685 							}
12686 						}
12687 						else{
12688 							int h2;
12689 							for(h2=0; h2<2*gt.rules.nHoursPerDay; h2++){
12690 								int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
12691 								int h3=h2%gt.rules.nHoursPerDay;
12692 								if(newSubgroupsTimetable(sbg, d3, h3)>=0){
12693 									_start=h2;
12694 									break;
12695 								}
12696 								else if(!breakDayHour(d3,h3) && !subgroupNotAvailableDayHour(sbg,d3,h3)){
12697 									break;
12698 								}
12699 							}
12700 							if(_start==-1){
12701 								h2++;
12702 								for(; h2<2*gt.rules.nHoursPerDay; h2++){
12703 									int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
12704 									int h3=h2%gt.rules.nHoursPerDay;
12705 									if(!breakDayHour(d3,h3) && !subgroupNotAvailableDayHour(sbg,d3,h3)){
12706 										_start=h2;
12707 										break;
12708 									}
12709 								}
12710 							}
12711 						}
12712 					}
12713 					else{
12714 						for(int h2=0; h2<2*gt.rules.nHoursPerDay; h2++){
12715 							int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
12716 							int h3=h2%gt.rules.nHoursPerDay;
12717 							if(newSubgroupsTimetable(sbg, d3, h3)>=0){
12718 								_start=h2;
12719 								break;
12720 							}
12721 						}
12722 					}
12723 					for(int h2=2*gt.rules.nHoursPerDay-1; h2>=0; h2--){
12724 						int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
12725 						int h3=h2%gt.rules.nHoursPerDay;
12726 						if(newSubgroupsTimetable(sbg, d3, h3)>=0){
12727 							_end=h2;
12728 							break;
12729 						}
12730 					}
12731 
12732 					if(_start>=0 && _end>=0 && _end>=_start)
12733 						_cnt=_end-_start+1;
12734 
12735 					if(_cnt<=maxSpanPerDay)
12736 						continue;
12737 
12738 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
12739 						okstudentsmaxspanperrealday=false;
12740 						goto impossiblestudentsmaxspanperrealday;
12741 					}
12742 
12743 					getSbgTimetable(sbg, conflActivities[newtime]);
12744 					updateSbgNHoursGaps(sbg, d); //needed for
12745 					updateSbgNHoursGaps(sbg, dpair); //subgroupRemoveAnActivityFromEndCertainRealDay or
12746 					//subgroupRemoveAnActivityFromBeginOrEndCertainRealDay
12747 
12748 					for(;;){
12749 						int cnt=0;
12750 						int start=-1;
12751 						int end=-1;
12752 
12753 						if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
12754 							if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0){
12755 								for(int h2=0; h2<2*gt.rules.nHoursPerDay; h2++){
12756 									int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
12757 									int h3=h2%gt.rules.nHoursPerDay;
12758 									if(!breakDayHour(d3,h3) && !subgroupNotAvailableDayHour(sbg,d3,h3)){
12759 										start=h2;
12760 										break;
12761 									}
12762 								}
12763 							}
12764 							else{
12765 								int h2;
12766 								for(h2=0; h2<2*gt.rules.nHoursPerDay; h2++){
12767 									int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
12768 									int h3=h2%gt.rules.nHoursPerDay;
12769 									if(sbgTimetable(d3, h3)>=0){
12770 										start=h2;
12771 										break;
12772 									}
12773 									else if(!breakDayHour(d3,h3) && !subgroupNotAvailableDayHour(sbg,d3,h3)){
12774 										break;
12775 									}
12776 								}
12777 								if(start==-1){
12778 									h2++;
12779 									for(; h2<2*gt.rules.nHoursPerDay; h2++){
12780 										int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
12781 										int h3=h2%gt.rules.nHoursPerDay;
12782 										if(!breakDayHour(d3,h3) && !subgroupNotAvailableDayHour(sbg,d3,h3)){
12783 											start=h2;
12784 											break;
12785 										}
12786 									}
12787 								}
12788 							}
12789 						}
12790 						else{
12791 							for(int h2=0; h2<2*gt.rules.nHoursPerDay; h2++){
12792 								int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
12793 								int h3=h2%gt.rules.nHoursPerDay;
12794 								if(sbgTimetable(d3, h3)>=0){
12795 									start=h2;
12796 									break;
12797 								}
12798 							}
12799 						}
12800 
12801 						for(int h2=2*gt.rules.nHoursPerDay-1; h2>=0; h2--){
12802 							int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
12803 							int h3=h2%gt.rules.nHoursPerDay;
12804 							if(sbgTimetable(d3, h3)>=0){
12805 								end=h2;
12806 								break;
12807 							}
12808 						}
12809 
12810 						if(start>=0 && end>=0 && end>=start)
12811 							cnt=end-start+1;
12812 
12813 						if(cnt<=maxSpanPerDay)
12814 							break;
12815 
12816 						int ai2=-1;
12817 
12818 						if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
12819 							if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0){
12820 								bool k=subgroupRemoveAnActivityFromEndCertainRealDay(d/2, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
12821 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
12822 								if(!k){
12823 									if(level==0){
12824 										//old comment below
12825 										//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
12826 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
12827 									}
12828 									okstudentsmaxspanperrealday=false;
12829 									goto impossiblestudentsmaxspanperrealday;
12830 								}
12831 							}
12832 							else{
12833 								bool k=subgroupRemoveAnActivityFromEndCertainRealDay(d/2, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
12834 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
12835 								if(!k){
12836 									bool k2=false;
12837 
12838 									//The following code is theoretically better, but practically much worse on the file
12839 									//examples/Romania/Pedagogic-High-School-Tg-Mures/2007-2008_sem1-d-test-students-max-span-per-day.fet
12840 									//(it slows down with 25%-50% on average - you need to generate more timetables)
12841 									/*int firstAvailableHour=-1;
12842 									for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
12843 										if(!breakDayHour(d,h2) && !subgroupNotAvailableDayHour(sbg,d,h2)){
12844 											firstAvailableHour=h2;
12845 											break;
12846 										}
12847 									}
12848 									//We could assert(firstAvailableHour>=0), because the day is not empty, because the span is too large.
12849 									if(firstAvailableHour>=0){
12850 										assert(firstAvailableHour<gt.rules.nHoursPerDay);
12851 										if(sbgTimetable(d, firstAvailableHour)>=0){
12852 											k2=subgroupRemoveAnActivityFromBeginCertainDay(sbg, d, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
12853 											assert(conflActivities[newtime].count()==nConflActivities[newtime]);
12854 										}
12855 									}*/
12856 
12857 									if(!k2){
12858 										if(level==0){
12859 											//old comment below
12860 											//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
12861 											//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
12862 										}
12863 										okstudentsmaxspanperrealday=false;
12864 										goto impossiblestudentsmaxspanperrealday;
12865 									}
12866 								}
12867 							}
12868 						}
12869 						else{
12870 							bool k=subgroupRemoveAnActivityFromBeginOrEndCertainRealDay(d/2, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
12871 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
12872 							if(!k){
12873 								if(level==0){
12874 									//old comment below
12875 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
12876 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
12877 								}
12878 								okstudentsmaxspanperrealday=false;
12879 								goto impossiblestudentsmaxspanperrealday;
12880 							}
12881 						}
12882 
12883 						assert(ai2>=0);
12884 
12885 						int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
12886 						removeAi2FromSbgTimetable(ai2);
12887 						sbgDayNHours[d2]-=gt.rules.internalActivitiesList[ai2].duration; //needed for subgroupRemoveAnActivityFromBeginOrEndCertainDay or
12888 						//subgroupRemoveAnActivityFromEndCertainDay or subgroupRemoveAnActivityFromBeginCertainDay above
12889 						assert(sbgDayNHours[d2]>=0);
12890 					}
12891 				}
12892 
12893 impossiblestudentsmaxspanperrealday:
12894 			if(!okstudentsmaxspanperrealday){
12895 				if(updateSubgroups || updateTeachers)
12896 					removeAiFromNewTimetable(ai, act, d, h);
12897 				//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
12898 
12899 				nConflActivities[newtime]=MAX_ACTIVITIES;
12900 				continue;
12901 			}
12902 		}
12903 
12904 		////////////////////////////END students max span per real day
12905 
12906 /////////////////////////////////////////////////////////////////////////////////////////////
12907 
12908 		////////////////////////////BEGIN students min resting hours
12909 
12910 		//Rodolfo Ribeiro Gomes's code (https://bitbucket.org/rodolforg/fet/src/dev/) was a source of inspiration for the following constraint
12911 
12912 		okstudentsminrestinghours=true;
12913 
12914 		for(int sbg : qAsConst(act->iSubgroupsList)){
12915 			for(int qq=0; qq<2; qq++){
12916 				double percentage;
12917 				int minRestingHours;
12918 				bool circular;
12919 				if(qq==0){
12920 					percentage=subgroupsMinRestingHoursCircularPercentages[sbg];
12921 					minRestingHours=subgroupsMinRestingHoursCircularMinHours[sbg];
12922 					circular=true;
12923 				}
12924 				else{
12925 					assert(qq==1);
12926 					percentage=subgroupsMinRestingHoursNotCircularPercentages[sbg];
12927 					minRestingHours=subgroupsMinRestingHoursNotCircularMinHours[sbg];
12928 					circular=false;
12929 				}
12930 				if(percentage>=0){
12931 					//percentage is 100%
12932 					assert(minRestingHours<=gt.rules.nHoursPerDay);
12933 
12934 					//phase 1 - activity is at the end of the day
12935 					int _cnt1=0;
12936 					int _cnt1d=0;
12937 					if(d <= gt.rules.nDaysPerWeek-2+(circular?1:0) && h+act->duration-1 >= gt.rules.nHoursPerDay-minRestingHours){
12938 						for(int h2=gt.rules.nHoursPerDay-1; h2>h+act->duration-1; h2--){
12939 							int ai2=newSubgroupsTimetable(sbg, d, h2);
12940 							if(ai2>=0 && !conflActivities[newtime].contains(ai2))
12941 								break;
12942 							else
12943 								_cnt1++;
12944 						}
12945 						_cnt1d=_cnt1;
12946 						for(int h2=0; h2<minRestingHours-_cnt1d; h2++){
12947 							int ai2=newSubgroupsTimetable(sbg, (d+1<=gt.rules.nDaysPerWeek-1?d+1:0), h2);
12948 							if(ai2>=0 && !conflActivities[newtime].contains(ai2))
12949 								break;
12950 							else
12951 								_cnt1++;
12952 						}
12953 					}
12954 					else
12955 						_cnt1=minRestingHours;
12956 
12957 					//phase 2 - activity is at the beginning of the day
12958 					int _cnt2=0;
12959 					int _cnt2d=0;
12960 					if(d>=1-(circular?1:0) && h<=minRestingHours-1){
12961 						for(int h2=0; h2<h; h2++){
12962 							int ai2=newSubgroupsTimetable(sbg, d, h2);
12963 							if(ai2>=0 && !conflActivities[newtime].contains(ai2))
12964 								break;
12965 							else
12966 								_cnt2++;
12967 						}
12968 						_cnt2d=_cnt2;
12969 						for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d; h2--){
12970 							int ai2=newSubgroupsTimetable(sbg, (d-1>=0?d-1:gt.rules.nDaysPerWeek-1), h2);
12971 							if(ai2>=0 && !conflActivities[newtime].contains(ai2))
12972 								break;
12973 							else
12974 								_cnt2++;
12975 						}
12976 					}
12977 					else
12978 						_cnt2=minRestingHours;
12979 
12980 					if(_cnt1<minRestingHours){
12981 						QList<int> removableActs;
12982 						/*for(int h2=gt.rules.nHoursPerDay-minRestingHours; h2<gt.rules.nHoursPerDay; h2++){
12983 							int ai2=newSubgroupsTimetable(sbg, d, h2);
12984 							if(ai2>=0 && ai2!=ai && !removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2] && !conflActivities[newtime].contains(ai2))
12985 								removableActs.append(ai2);
12986 						}*/
12987 						for(int h2=0; h2<minRestingHours-_cnt1d; h2++){
12988 							int ai2=newSubgroupsTimetable(sbg, (d+1<=gt.rules.nDaysPerWeek-1?d+1:0), h2);
12989 							if(ai2>=0 && !removableActs.contains(ai2) && !conflActivities[newtime].contains(ai2)){
12990 								if(!fixedTimeActivity[ai2] && !swappedActivities[ai2])
12991 									removableActs.append(ai2);
12992 								else
12993 									break;
12994 							}
12995 						}
12996 
12997 						for(;;){
12998 							if(removableActs.count()==0){
12999 								okstudentsminrestinghours=false;
13000 								goto impossiblestudentsminrestinghours;
13001 							}
13002 
13003 							int ai2=removableActs.at(0);
13004 
13005 							int t=removableActs.removeAll(ai2);
13006 							assert(t==1);
13007 
13008 							assert(!conflActivities[newtime].contains(ai2));
13009 							conflActivities[newtime].append(ai2);
13010 							nConflActivities[newtime]++;
13011 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13012 
13013 							int cnt1=0;
13014 							if(d <= gt.rules.nDaysPerWeek-2+(circular?1:0) && h+act->duration-1 >= gt.rules.nHoursPerDay-minRestingHours){
13015 								/*for(int h2=gt.rules.nHoursPerDay-1; h2>h+act->duration-1; h2--){
13016 									int ai2=newSubgroupsTimetable(sbg, d, h2);
13017 									if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13018 										break;
13019 									else
13020 										cnt1++;
13021 								}*/
13022 								cnt1+=_cnt1d;
13023 								for(int h2=0; h2<minRestingHours-_cnt1d; h2++){
13024 									int ai2=newSubgroupsTimetable(sbg, (d+1<=gt.rules.nDaysPerWeek-1?d+1:0), h2);
13025 									if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13026 										break;
13027 									else
13028 										cnt1++;
13029 								}
13030 							}
13031 							else{
13032 								assert(0);
13033 							}
13034 
13035 							assert(cnt1>_cnt1);
13036 							_cnt1=cnt1;
13037 
13038 							if(cnt1>=minRestingHours)
13039 								break;
13040 						}
13041 					}
13042 					if(_cnt2<minRestingHours){
13043 						QList<int> removableActs;
13044 						for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d; h2--){
13045 							int ai2=newSubgroupsTimetable(sbg, (d-1>=0?d-1:gt.rules.nDaysPerWeek-1), h2);
13046 							if(ai2>=0 && !removableActs.contains(ai2) && !conflActivities[newtime].contains(ai2)){
13047 								if(!fixedTimeActivity[ai2] && !swappedActivities[ai2])
13048 									removableActs.append(ai2);
13049 								else
13050 									break;
13051 							}
13052 						}
13053 						/*for(int h2=0; h2<minRestingHours; h2++){
13054 							int ai2=newSubgroupsTimetable(sbg, d, h2);
13055 							if(ai2>=0 && ai2!=ai && !removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2] && !conflActivities[newtime].contains(ai2))
13056 								removableActs.append(ai2);
13057 						}*/
13058 
13059 						for(;;){
13060 							if(removableActs.count()==0){
13061 								okstudentsminrestinghours=false;
13062 								goto impossiblestudentsminrestinghours;
13063 							}
13064 
13065 							int ai2=removableActs.at(0);
13066 
13067 							int t=removableActs.removeAll(ai2);
13068 							assert(t==1);
13069 
13070 							assert(!conflActivities[newtime].contains(ai2));
13071 							conflActivities[newtime].append(ai2);
13072 							nConflActivities[newtime]++;
13073 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13074 
13075 							int cnt2=0;
13076 							if(d>=1-(circular?1:0) && h<minRestingHours){
13077 								/*for(int h2=0; h2<h; h2++){
13078 									int ai2=newSubgroupsTimetable(sbg, d, h2);
13079 									if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13080 										break;
13081 									else
13082 										cnt2++;
13083 								}*/
13084 								cnt2+=_cnt2d;
13085 								for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d; h2--){
13086 									int ai2=newSubgroupsTimetable(sbg, (d-1>=0?d-1:gt.rules.nDaysPerWeek-1), h2);
13087 									if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13088 										break;
13089 									else
13090 										cnt2++;
13091 								}
13092 							}
13093 							else{
13094 								assert(0);
13095 							}
13096 
13097 							assert(cnt2>_cnt2);
13098 							_cnt2=cnt2;
13099 
13100 							if(cnt2>=minRestingHours)
13101 								break;
13102 						}
13103 					}
13104 				}
13105 			}
13106 		}
13107 
13108 impossiblestudentsminrestinghours:
13109 		if(!okstudentsminrestinghours){
13110 			if(updateSubgroups || updateTeachers)
13111 				removeAiFromNewTimetable(ai, act, d, h);
13112 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
13113 
13114 			nConflActivities[newtime]=MAX_ACTIVITIES;
13115 			continue;
13116 		}
13117 
13118 		////////////////////////////END students min resting hours
13119 
13120 /////////////////////////////////////////////////////////////////////////////////////////////
13121 
13122 		////////////////////////////BEGIN students min resting hours between morning and afternoon
13123 
13124 		//Rodolfo Ribeiro Gomes's code (https://bitbucket.org/rodolforg/fet/src/dev/) was a source of inspiration for the following constraint
13125 
13126 		okstudentsminrestinghoursbetweenmorningandafternoon=true;
13127 
13128 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
13129 			for(int sbg : qAsConst(act->iSubgroupsList)){
13130 				double percentage=subgroupsMinRestingHoursBetweenMorningAndAfternoonPercentages[sbg];
13131 				int minRestingHours=subgroupsMinRestingHoursBetweenMorningAndAfternoonMinHours[sbg];
13132 
13133 				if(percentage>=0){
13134 					assert(percentage==100.0);
13135 					assert(minRestingHours<=2*gt.rules.nHoursPerDay);
13136 
13137 					if(d%2==0){ //morning
13138 						int _cnt1=0;
13139 						int _cnt1d=0;
13140 						if(h+act->duration-1 >= gt.rules.nHoursPerDay-minRestingHours){
13141 							for(int h2=gt.rules.nHoursPerDay-1; h2>h+act->duration-1; h2--){
13142 								int ai2=newSubgroupsTimetable(sbg, d, h2);
13143 								if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13144 									break;
13145 								else
13146 									_cnt1++;
13147 							}
13148 							_cnt1d=_cnt1;
13149 							for(int h2=0; h2<minRestingHours-_cnt1d && h2<gt.rules.nHoursPerDay; h2++){
13150 								int ai2=newSubgroupsTimetable(sbg, d+1, h2);
13151 								if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13152 									break;
13153 								else
13154 									_cnt1++;
13155 							}
13156 						}
13157 						else
13158 							_cnt1=minRestingHours;
13159 
13160 						if(_cnt1<minRestingHours){
13161 							QList<int> removableActs;
13162 							/*for(int h2=gt.rules.nHoursPerDay-minRestingHours; h2<gt.rules.nHoursPerDay; h2++){
13163 								int ai2=newTeachersTimetable(tch, d, h2);
13164 								if(ai2>=0 && ai2!=ai && !removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2] && !conflActivities[newtime].contains(ai2))
13165 									removableActs.append(ai2);
13166 							}*/
13167 							for(int h2=0; h2<minRestingHours-_cnt1d && h2<gt.rules.nHoursPerDay; h2++){
13168 								int ai2=newSubgroupsTimetable(sbg, d+1, h2);
13169 								if(ai2>=0 && !removableActs.contains(ai2) && !conflActivities[newtime].contains(ai2)){
13170 									if(!fixedTimeActivity[ai2] && !swappedActivities[ai2])
13171 										removableActs.append(ai2);
13172 									else
13173 										break;
13174 								}
13175 							}
13176 
13177 							for(;;){
13178 								if(removableActs.count()==0){
13179 									okstudentsminrestinghoursbetweenmorningandafternoon=false;
13180 									goto impossiblestudentsminrestinghoursbetweenmorningandafternoon;
13181 								}
13182 
13183 								int ai2=removableActs.at(0);
13184 
13185 								int t=removableActs.removeAll(ai2);
13186 								assert(t==1);
13187 
13188 								assert(!conflActivities[newtime].contains(ai2));
13189 								conflActivities[newtime].append(ai2);
13190 								nConflActivities[newtime]++;
13191 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13192 
13193 								int cnt1=0;
13194 								if(h+act->duration-1 >= gt.rules.nHoursPerDay-minRestingHours){
13195 									/*for(int h2=gt.rules.nHoursPerDay-1; h2>h+act->duration-1; h2--){
13196 										int ai2=newTeachersTimetable(tch, d, h2);
13197 										if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13198 											break;
13199 										else
13200 											cnt1++;
13201 									}*/
13202 									cnt1+=_cnt1d;
13203 									for(int h2=0; h2<minRestingHours-_cnt1d && h2<gt.rules.nHoursPerDay; h2++){
13204 										int ai2=newSubgroupsTimetable(sbg, d+1, h2);
13205 										if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13206 											break;
13207 										else
13208 											cnt1++;
13209 									}
13210 								}
13211 								else{
13212 									assert(0);
13213 								}
13214 
13215 								assert(cnt1>_cnt1);
13216 								_cnt1=cnt1;
13217 
13218 								if(cnt1>=minRestingHours)
13219 									break;
13220 							}
13221 						}
13222 					}
13223 					else{ //afternoon
13224 						assert(d%2==1);
13225 						int _cnt2=0;
13226 						int _cnt2d=0;
13227 						if(h<=minRestingHours-1){
13228 							for(int h2=0; h2<h; h2++){
13229 								int ai2=newSubgroupsTimetable(sbg, d, h2);
13230 								if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13231 									break;
13232 								else
13233 									_cnt2++;
13234 							}
13235 							_cnt2d=_cnt2;
13236 							for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d && h2>=0; h2--){
13237 								int ai2=newSubgroupsTimetable(sbg, d-1, h2);
13238 								if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13239 									break;
13240 								else
13241 									_cnt2++;
13242 							}
13243 						}
13244 						else
13245 							_cnt2=minRestingHours;
13246 
13247 						if(_cnt2<minRestingHours){
13248 							QList<int> removableActs;
13249 							for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d && h2>=0; h2--){
13250 								int ai2=newSubgroupsTimetable(sbg, d-1, h2);
13251 								if(ai2>=0 && !removableActs.contains(ai2) && !conflActivities[newtime].contains(ai2)){
13252 									if(!fixedTimeActivity[ai2] && !swappedActivities[ai2])
13253 										removableActs.append(ai2);
13254 									else
13255 										break;
13256 								}
13257 							}
13258 							/*for(int h2=0; h2<minRestingHours; h2++){
13259 								int ai2=newTeachersTimetable(tch, d, h2);
13260 								if(ai2>=0 && ai2!=ai && !removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2] && !conflActivities[newtime].contains(ai2))
13261 									removableActs.append(ai2);
13262 							}*/
13263 
13264 							for(;;){
13265 								if(removableActs.count()==0){
13266 									okstudentsminrestinghoursbetweenmorningandafternoon=false;
13267 									goto impossiblestudentsminrestinghoursbetweenmorningandafternoon;
13268 								}
13269 
13270 								int ai2=removableActs.at(0);
13271 
13272 								int t=removableActs.removeAll(ai2);
13273 								assert(t==1);
13274 
13275 								assert(!conflActivities[newtime].contains(ai2));
13276 								conflActivities[newtime].append(ai2);
13277 								nConflActivities[newtime]++;
13278 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13279 
13280 								int cnt2=0;
13281 								if(h<minRestingHours){
13282 									/*for(int h2=0; h2<h; h2++){
13283 										int ai2=newTeachersTimetable(tch, d, h2);
13284 										if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13285 											break;
13286 										else
13287 											cnt2++;
13288 									}*/
13289 									cnt2+=_cnt2d;
13290 									for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d && h2>=0; h2--){
13291 										int ai2=newSubgroupsTimetable(sbg, d-1, h2);
13292 										if(ai2>=0 && !conflActivities[newtime].contains(ai2))
13293 											break;
13294 										else
13295 											cnt2++;
13296 									}
13297 								}
13298 								else{
13299 									assert(0);
13300 								}
13301 
13302 								assert(cnt2>_cnt2);
13303 								_cnt2=cnt2;
13304 
13305 								if(cnt2>=minRestingHours)
13306 									break;
13307 							}
13308 						}
13309 					}
13310 				}
13311 			}
13312 		}
13313 
13314 impossiblestudentsminrestinghoursbetweenmorningandafternoon:
13315 		if(!okstudentsminrestinghoursbetweenmorningandafternoon){
13316 			if(updateSubgroups || updateTeachers)
13317 				removeAiFromNewTimetable(ai, act, d, h);
13318 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
13319 
13320 			nConflActivities[newtime]=MAX_ACTIVITIES;
13321 			continue;
13322 		}
13323 
13324 		////////////////////////////END students min resting hours between morning and afternoon
13325 
13326 /////////////////////////////////////////////////////////////////////////////////////////////
13327 
13328 		//not breaking students early max beginnings at second hour
13329 		//TODO: this should take care of students max gaps per day also. Very critical changes, so be very careful if you do them. Safer -> leave them as they are now.
13330 		//see file fet-v.v.v/doc/algorithm/improve-studentsmaxgapsperday.txt for advice and (unstable) code on how to make students max gaps per day constraint perfect
13331 		okstudentsearlymaxbeginningsatsecondhour=true;
13332 
13333 		for(int sbg : qAsConst(act->iSubgroupsList))
13334 			if(!skipRandom(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg])){
13335 				//preliminary check
13336 				int _nHours=0;
13337 				int _nFirstGapsOne=0;
13338 				int _nFirstGapsTwo=0;
13339 				int _nGaps=0;
13340 				int _nIllegalGaps=0;
13341 				for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
13342 					_nHours+=newSubgroupsDayNHours(sbg,d2);
13343 
13344 					if(newSubgroupsDayNFirstGaps(sbg, d2)==1){
13345 						_nFirstGapsOne++;
13346 					}
13347 					else if(newSubgroupsDayNFirstGaps(sbg, d2)>=2){
13348 						_nFirstGapsTwo++;
13349 						_nIllegalGaps++;
13350 						_nGaps+=newSubgroupsDayNFirstGaps(sbg, d2)-2;
13351 					}
13352 					_nGaps+=newSubgroupsDayNGaps(sbg,d2);
13353 				}
13354 
13355 				int _tt=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
13356 				if(_tt>=_nFirstGapsOne){
13357 					_tt-=_nFirstGapsOne;
13358 					_nFirstGapsOne=0;
13359 				}
13360 				else{
13361 					_nFirstGapsOne-=_tt;
13362 					_tt=0;
13363 				}
13364 				if(_tt>=_nFirstGapsTwo){
13365 					_tt-=_nFirstGapsTwo;
13366 					_nFirstGapsTwo=0;
13367 				}
13368 				else{
13369 					_nFirstGapsTwo-=_tt;
13370 					_tt=0;
13371 				}
13372 
13373 				if(_nFirstGapsTwo>0){
13374 					_nGaps+=_nFirstGapsTwo;
13375 					_nFirstGapsTwo=0;
13376 				}
13377 
13378 				int _nHoursGaps=0;
13379 				if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
13380 					assert(subgroupsMaxGapsPerWeekPercentage[sbg]==100);
13381 					if(_nGaps>subgroupsMaxGapsPerWeekMaxGaps[sbg])
13382 						_nHoursGaps=_nGaps-subgroupsMaxGapsPerWeekMaxGaps[sbg];
13383 				}
13384 
13385 				if(_nHours + _nFirstGapsOne + _nHoursGaps + _nIllegalGaps > nHoursPerSubgroup[sbg]){
13386 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
13387 						okstudentsearlymaxbeginningsatsecondhour=false;
13388 						goto impossiblestudentsearlymaxbeginningsatsecondhour;
13389 					}
13390 
13391 					getSbgTimetable(sbg, conflActivities[newtime]);
13392 					sbgGetNHoursGaps(sbg);
13393 
13394 					for(;;){
13395 						int nHours=0;
13396 						int nFirstGapsOne=0;
13397 						int nFirstGapsTwo=0;
13398 						int nGaps=0;
13399 						int nIllegalGaps=0;
13400 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
13401 							nHours+=sbgDayNHours[d2];
13402 
13403 							if(sbgDayNFirstGaps[d2]==1){
13404 								nFirstGapsOne++;
13405 							}
13406 							else if(sbgDayNFirstGaps[d2]>=2){
13407 								nFirstGapsTwo++;
13408 								nIllegalGaps++;
13409 								nGaps+=sbgDayNFirstGaps[d2]-2;
13410 							}
13411 							nGaps+=sbgDayNGaps[d2];
13412 						}
13413 
13414 						int tt=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
13415 						if(tt>=nFirstGapsOne){
13416 							tt-=nFirstGapsOne;
13417 							nFirstGapsOne=0;
13418 						}
13419 						else{
13420 							nFirstGapsOne-=tt;
13421 							tt=0;
13422 						}
13423 						if(tt>=nFirstGapsTwo){
13424 							tt-=nFirstGapsTwo;
13425 							nFirstGapsTwo=0;
13426 						}
13427 						else{
13428 							nFirstGapsTwo-=tt;
13429 							tt=0;
13430 						}
13431 
13432 						if(nFirstGapsTwo>0){
13433 							nGaps+=nFirstGapsTwo;
13434 							nFirstGapsTwo=0;
13435 						}
13436 
13437 						int nHoursGaps=0;
13438 						if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
13439 							assert(subgroupsMaxGapsPerWeekPercentage[sbg]==100);
13440 							if(nGaps>subgroupsMaxGapsPerWeekMaxGaps[sbg])
13441 								nHoursGaps=nGaps-subgroupsMaxGapsPerWeekMaxGaps[sbg];
13442 						}
13443 
13444 						int ai2=-1;
13445 
13446 						if(nHours + nFirstGapsOne + nHoursGaps + nIllegalGaps > nHoursPerSubgroup[sbg]){
13447 							//remove an activity
13448 							bool k=subgroupRemoveAnActivityFromEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
13449 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13450 							if(!k){
13451 								//Added on 2020-09-17 - logic improvement / theoretical bug fix.
13452 								bool k2=false;
13453 								//if(subgroupsMaxGapsPerWeekPercentage[sbg]<0 ||
13454 								// (subgroupsMaxGapsPerWeekPercentage[sbg]>=0 && subgroupsMaxGapsPerWeekMaxGaps[sbg]>0))
13455 								if(subgroupsMaxGapsPerWeekMaxGaps[sbg]!=0){ //-1 or >0
13456 									k2=subgroupRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
13457 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13458 								}
13459 
13460 								if(!k2){
13461 									if(level==0){
13462 										//this should not be displayed
13463 										//cout<<"WARNING - maybe bug - file "<<__FILE__<<" line "<<__LINE__<<endl;
13464 									}
13465 									okstudentsearlymaxbeginningsatsecondhour=false;
13466 									goto impossiblestudentsearlymaxbeginningsatsecondhour;
13467 								}
13468 							}
13469 						}
13470 						else{ //OK
13471 							break;
13472 						}
13473 
13474 						assert(ai2>=0);
13475 
13476 						removeAi2FromSbgTimetable(ai2);
13477 						updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
13478 					}
13479 				}
13480 			}
13481 
13482 impossiblestudentsearlymaxbeginningsatsecondhour:
13483 		if(!okstudentsearlymaxbeginningsatsecondhour){
13484 			if(updateSubgroups || updateTeachers)
13485 				removeAiFromNewTimetable(ai, act, d, h);
13486 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
13487 
13488 			nConflActivities[newtime]=MAX_ACTIVITIES;
13489 			continue;
13490 		}
13491 
13492 		////////////////////////////END students early max beginnings at second hour
13493 
13494 /////////////////////////////////////////////////////////////////////////////////////////////
13495 
13496 		//not breaking students max gaps per week
13497 		//TODO: this should take care of students max gaps per day also. Very critical changes, so be very careful if you do them. Safer -> leave them as they are now.
13498 		//see file fet-v.v.v/doc/algorithm/improve-studentsmaxgapsperday.txt for advice and (unstable) code on how to make students max gaps per day constraint perfect
13499 		okstudentsmaxgapsperweek=true;
13500 
13501 		for(int sbg : qAsConst(act->iSubgroupsList))
13502 			if(!skipRandom(subgroupsMaxGapsPerWeekPercentage[sbg])){
13503 				//preliminary test
13504 				int _nHours=0;
13505 				int _nGaps=0;
13506 				int _nFirstGaps=0;
13507 				for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
13508 					_nHours+=newSubgroupsDayNHours(sbg,d2);
13509 					_nGaps+=newSubgroupsDayNGaps(sbg,d2);
13510 					_nFirstGaps+=newSubgroupsDayNFirstGaps(sbg,d2);
13511 				}
13512 
13513 				int _nFirstHours=0;
13514 
13515 				if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
13516 					assert(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]==100);
13517 					if(_nFirstGaps>subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg])
13518 						_nFirstHours=_nFirstGaps-subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
13519 				}
13520 
13521 				if(_nGaps+_nHours+_nFirstHours > subgroupsMaxGapsPerWeekMaxGaps[sbg] + nHoursPerSubgroup[sbg]){
13522 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
13523 						okstudentsmaxgapsperweek=false;
13524 						goto impossiblestudentsmaxgapsperweek;
13525 					}
13526 
13527 					getSbgTimetable(sbg, conflActivities[newtime]);
13528 					sbgGetNHoursGaps(sbg);
13529 
13530 					for(;;){
13531 						int nHours=0;
13532 						int nGaps=0;
13533 						int nFirstGaps=0;
13534 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
13535 							nHours+=sbgDayNHours[d2];
13536 							nGaps+=sbgDayNGaps[d2];
13537 							nFirstGaps+=sbgDayNFirstGaps[d2];
13538 						}
13539 
13540 						int ai2=-1;
13541 
13542 						int nFirstHours=0;
13543 
13544 						if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
13545 							assert(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]==100);
13546 							if(nFirstGaps>subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg])
13547 								nFirstHours=nFirstGaps-subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
13548 						}
13549 
13550 						if(nGaps+nHours+nFirstHours > subgroupsMaxGapsPerWeekMaxGaps[sbg] + nHoursPerSubgroup[sbg]){
13551 							//remove an activity
13552 							bool k=subgroupRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
13553 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13554 							if(!k){
13555 								if(level==0){
13556 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
13557 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
13558 								}
13559 								okstudentsmaxgapsperweek=false;
13560 								goto impossiblestudentsmaxgapsperweek;
13561 							}
13562 						}
13563 						else{ //OK
13564 							break;
13565 						}
13566 
13567 						assert(ai2>=0);
13568 
13569 						removeAi2FromSbgTimetable(ai2);
13570 						updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
13571 					}
13572 				}
13573 			}
13574 
13575 impossiblestudentsmaxgapsperweek:
13576 		if(!okstudentsmaxgapsperweek){
13577 			if(updateSubgroups || updateTeachers)
13578 				removeAiFromNewTimetable(ai, act, d, h);
13579 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
13580 
13581 			nConflActivities[newtime]=MAX_ACTIVITIES;
13582 			continue;
13583 		}
13584 
13585 		////////////////////////////END students max gaps per week
13586 
13587 /////////////////////////////////////////////////////////////////////////////////////////////
13588 
13589 	//!!!NOT PERFECT constraint, in other places may be improved, like in min/max hours daily.
13590 	//see file fet-v.v.v/doc/algorithm/improve-studentsmaxgapsperday.txt for advice and (unstable) code on how to make students max gaps per day constraint perfect
13591 
13592 	//not causing more than subgroupsMaxGapsPerDay students gaps
13593 
13594 	//TODO: improve, check
13595 
13596 	okstudentsmaxgapsperday=true;
13597 
13598 	if(haveStudentsMaxGapsPerDay){
13599 		//okstudentsmaxgapsperday=true;
13600 		for(int sbg : qAsConst(act->iSubgroupsList))
13601 			if(!skipRandom(subgroupsMaxGapsPerDayPercentage[sbg])){
13602 				assert(subgroupsMaxGapsPerDayPercentage[sbg]==100);
13603 
13604 				//preliminary test
13605 				int _total=0;
13606 				int _remnf=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
13607 				bool _haveMaxBegs=(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0);
13608 				for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
13609 					_total+=newSubgroupsDayNHours(sbg,d2);
13610 					int _g=newSubgroupsDayNGaps(sbg,d2);
13611 					if(_haveMaxBegs){
13612 						int _fg=newSubgroupsDayNFirstGaps(sbg,d2);
13613 						if(_fg==0){
13614 							if(_g>subgroupsMaxGapsPerDayMaxGaps[sbg])
13615 								_total+=_g-subgroupsMaxGapsPerDayMaxGaps[sbg];
13616 						}
13617 						else if(_fg==1){
13618 							if(_remnf>0)
13619 								_remnf--;
13620 							else
13621 								_total++;
13622 							if(_g>subgroupsMaxGapsPerDayMaxGaps[sbg])
13623 								_total+=_g-subgroupsMaxGapsPerDayMaxGaps[sbg];
13624 						}
13625 						else if(_fg>=2){
13626 							if(_g + _fg - 1 <= subgroupsMaxGapsPerDayMaxGaps[sbg])
13627 								_total++;
13628 							else{
13629 								if(_remnf>0)
13630 									_remnf--;
13631 								else
13632 									_total++;
13633 								_total++;
13634 								assert(_g + _fg - 2 >= subgroupsMaxGapsPerDayMaxGaps[sbg]);
13635 								_total+=(_g + _fg - 2 - subgroupsMaxGapsPerDayMaxGaps[sbg]);
13636 							}
13637 						}
13638 						else
13639 							assert(0);
13640 					}
13641 					else{
13642 						if(_g > subgroupsMaxGapsPerDayMaxGaps[sbg])
13643 							_total+=_g-subgroupsMaxGapsPerDayMaxGaps[sbg];
13644 					}
13645 				}
13646 
13647 				if(_total<=nHoursPerSubgroup[sbg]) //OK
13648 					continue;
13649 
13650 				if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
13651 					okstudentsmaxgapsperday=false;
13652 					goto impossiblestudentsmaxgapsperday;
13653 				}
13654 
13655 				getSbgTimetable(sbg, conflActivities[newtime]);
13656 				sbgGetNHoursGaps(sbg);
13657 
13658 				for(;;){
13659 					int total=0;
13660 					int remnf=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
13661 					bool haveMaxBegs=(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0);
13662 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
13663 						total+=sbgDayNHours[d2];
13664 						int g=sbgDayNGaps[d2];
13665 						if(haveMaxBegs){
13666 							int fg=sbgDayNFirstGaps[d2];
13667 							if(fg==0){
13668 								if(g>subgroupsMaxGapsPerDayMaxGaps[sbg])
13669 									total+=g-subgroupsMaxGapsPerDayMaxGaps[sbg];
13670 							}
13671 							else if(fg==1){
13672 								if(remnf>0)
13673 									remnf--;
13674 								else
13675 									total++;
13676 								if(g>subgroupsMaxGapsPerDayMaxGaps[sbg])
13677 									total+=g-subgroupsMaxGapsPerDayMaxGaps[sbg];
13678 							}
13679 							else if(fg>=2){
13680 								if(g + fg - 1 <= subgroupsMaxGapsPerDayMaxGaps[sbg])
13681 									total++;
13682 								else{
13683 									if(remnf>0)
13684 										remnf--;
13685 									else
13686 										total++;
13687 									total++;
13688 									assert(g + fg - 2 >= subgroupsMaxGapsPerDayMaxGaps[sbg]);
13689 									total+=(g + fg - 2 - subgroupsMaxGapsPerDayMaxGaps[sbg]);
13690 								}
13691 							}
13692 							else
13693 								assert(0);
13694 						}
13695 						else{
13696 							if(g > subgroupsMaxGapsPerDayMaxGaps[sbg])
13697 								total+=g-subgroupsMaxGapsPerDayMaxGaps[sbg];
13698 						}
13699 					}
13700 
13701 					if(total<=nHoursPerSubgroup[sbg]) //OK
13702 						break;
13703 
13704 					//remove an activity from the beginning or from the end of a day
13705 					//following code is identical to maxgapsperweek
13706 					//remove an activity
13707 					int ai2=-1;
13708 
13709 					//it should also be allowed to take from anywhere, but it is risky to change now
13710 					// Addition 2020-09-18: I might be mistaking and should not be allowed to take from anywhere.
13711 					// Also, I think it should be allowed to take from anywhere only if
13712 					// the max allowed gaps per week/day are greater than 0 (or not existing max gaps per week,
13713 					// but this cannot happen, because I compute automatically max_per_week = n_days*max_per_day).
13714 					if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
13715 						bool k=subgroupRemoveAnActivityFromEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
13716 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13717 						if(!k){
13718 							bool kk;
13719 							if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0 &&
13720 							 (subgroupsMaxGapsPerWeekMaxGaps[sbg]==0 || subgroupsMaxGapsPerDayMaxGaps[sbg]==0))
13721 								kk=false;
13722 							else
13723 								kk=subgroupRemoveAnActivityFromBegin(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
13724 
13725 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13726 							if(!kk){
13727 								if(level==0){
13728 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
13729 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
13730 								}
13731 								okstudentsmaxgapsperday=false;
13732 								goto impossiblestudentsmaxgapsperday;
13733 							}
13734 						}
13735 					}
13736 					else{
13737 						bool k=subgroupRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
13738 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13739 						if(!k){
13740 							if(level==0){
13741 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
13742 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
13743 							}
13744 							okstudentsmaxgapsperday=false;
13745 							goto impossiblestudentsmaxgapsperday;
13746 						}
13747 					}
13748 
13749 					assert(ai2>=0);
13750 
13751 					removeAi2FromSbgTimetable(ai2);
13752 					updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
13753 				}
13754 			}
13755 	}
13756 
13757 impossiblestudentsmaxgapsperday:
13758 		if(!okstudentsmaxgapsperday){
13759 			if(updateSubgroups || updateTeachers)
13760 				removeAiFromNewTimetable(ai, act, d, h);
13761 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
13762 
13763 			nConflActivities[newtime]=MAX_ACTIVITIES;
13764 			continue;
13765 		}
13766 
13767 		////////////////////////////END max gaps per day
13768 
13769 /////////////////////////////////////////////////////////////////////////////////////////////
13770 
13771 //2020-07-29
13772 
13773 		//2018-10-29 - Maybe old comments below (duplicate from the constraint students max gaps per day).
13774 
13775 		//!!!NOT PERFECT constraint, in other places may be improved, like in min/max hours daily.
13776 		//see file fet-v.v.v/doc/algorithm/improve-studentsmaxgapsperday.txt for advice and (unstable) code on how to make students max gaps per day constraint perfect
13777 
13778 		//not causing more than subgroupsMaxGapsPerWeekForRealDays students gaps
13779 
13780 		//TODO: improve, check
13781 
13782 		okstudentsmaxgapsperweekforrealdays=true;
13783 
13784 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
13785 			if(haveStudentsMaxGapsPerRealDay){
13786 				//okstudentsmaxgapsperrealday=true;
13787 				for(int sbg : qAsConst(act->iSubgroupsList))
13788 					if(!skipRandom(subgroupsMaxGapsPerWeekForRealDaysPercentage[sbg])){
13789 						assert(subgroupsMaxGapsPerWeekForRealDaysPercentage[sbg]==100);
13790 
13791 						//preliminary test
13792 						int _total=0;
13793 						int _remnf=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
13794 						bool _haveMaxBegs=(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0);
13795 						for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
13796 							_total+=newSubgroupsRealDayNHours(sbg,d2)+newSubgroupsRealDayNGaps(sbg,d2);
13797 							if(_haveMaxBegs){
13798 								int _fg=newSubgroupsRealDayNFirstGaps(sbg,d2);
13799 								if(_fg==0){
13800 									//nothing
13801 								}
13802 								else if(_fg==1){
13803 									if(_remnf>0)
13804 										_remnf--;
13805 									else
13806 										_total++;
13807 								}
13808 								else if(_fg>=2){
13809 									if(_remnf>0){
13810 										_remnf--;
13811 										_total+=_fg-1;
13812 									}
13813 									else{
13814 										_total+=_fg;
13815 									}
13816 								}
13817 								else
13818 									assert(0);
13819 							}
13820 							else{
13821 								//nothing
13822 							}
13823 						}
13824 
13825 						if(_total<=nHoursPerSubgroup[sbg]+subgroupsMaxGapsPerWeekForRealDaysMaxGaps[sbg]) //OK
13826 							continue;
13827 
13828 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
13829 							okstudentsmaxgapsperweekforrealdays=false;
13830 							goto impossiblestudentsmaxgapsperweekforrealdays;
13831 						}
13832 
13833 						getSbgTimetable(sbg, conflActivities[newtime]);
13834 						sbgGetNHoursGapsRealDays(sbg);
13835 						sbgGetNHoursGaps(sbg); //bug fix on 2020-02-29, because the remove an activity functions below need to know the number of hours per half-day.
13836 
13837 						for(;;){
13838 							int total=0;
13839 							int remnf=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
13840 							bool haveMaxBegs=(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0);
13841 							for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
13842 								total+=sbgRealDayNHours[d2]+sbgRealDayNGaps[d2];
13843 								if(haveMaxBegs){
13844 									int fg=sbgRealDayNFirstGaps[d2];
13845 									if(fg==0){
13846 										//nothing
13847 									}
13848 									else if(fg==1){
13849 										if(remnf>0)
13850 											remnf--;
13851 										else
13852 											total++;
13853 									}
13854 									else if(fg>=2){
13855 										if(remnf>0){
13856 											remnf--;
13857 											total+=fg-1;
13858 										}
13859 										else{
13860 											total+=fg;
13861 										}
13862 									}
13863 									else
13864 										assert(0);
13865 								}
13866 								else{
13867 									//nothing
13868 								}
13869 							}
13870 
13871 							if(total<=nHoursPerSubgroup[sbg]+subgroupsMaxGapsPerWeekForRealDaysMaxGaps[sbg]) //OK
13872 								break;
13873 
13874 							//remove an activity from the beginning or from the end of a real day
13875 							//following code is identical to maxgapsperweek
13876 							//remove an activity
13877 							int ai2=-1;
13878 
13879 							//it should also be allowed to take from anywhere, but it is risky to change now
13880 							if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
13881 								bool k=subgroupRemoveAnActivityFromEndAfternoon(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
13882 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13883 								if(!k){
13884 									bool kk;
13885 									if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0 &&
13886 									 (subgroupsMaxGapsPerRealDayMaxGaps[sbg]==0 || subgroupsMaxGapsPerWeekForRealDaysMaxGaps[sbg]==0) )
13887 										kk=false;
13888 									else
13889 										kk=subgroupRemoveAnActivityFromBeginMorning(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
13890 
13891 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13892 									if(!kk){
13893 										if(level==0){
13894 											//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
13895 											//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
13896 										}
13897 										okstudentsmaxgapsperweekforrealdays=false;
13898 										goto impossiblestudentsmaxgapsperweekforrealdays;
13899 									}
13900 								}
13901 							}
13902 							else{
13903 								bool k=subgroupRemoveAnActivityFromBeginMorningOrEndAfternoon(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
13904 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
13905 								if(!k){
13906 									if(level==0){
13907 										//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
13908 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
13909 									}
13910 									okstudentsmaxgapsperweekforrealdays=false;
13911 									goto impossiblestudentsmaxgapsperweekforrealdays;
13912 								}
13913 							}
13914 
13915 							assert(ai2>=0);
13916 
13917 							removeAi2FromSbgTimetable(ai2);
13918 							updateSbgNHoursGapsRealDay(sbg, (c.times[ai2]%gt.rules.nDaysPerWeek)/2);
13919 							updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek); //bug fix on 2020-02-29, because the remove an activity functions above need to know the number of hours per half-day.
13920 						}
13921 					}
13922 			}
13923 		}
13924 
13925 impossiblestudentsmaxgapsperweekforrealdays:
13926 		if(!okstudentsmaxgapsperweekforrealdays){
13927 			if(updateSubgroups || updateTeachers)
13928 				removeAiFromNewTimetable(ai, act, d, h);
13929 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
13930 
13931 			nConflActivities[newtime]=MAX_ACTIVITIES;
13932 			continue;
13933 		}
13934 
13935 		////////////////////////////END students max gaps per week for real days
13936 
13937 /////////////////////////////////////////////////////////////////////////////////////////////
13938 
13939 		//2018-10-29 - Maybe old comments below (duplicate from the constraint students max gaps per day.
13940 
13941 		//!!!NOT PERFECT constraint, in other places may be improved, like in min/max hours daily.
13942 		//see file fet-v.v.v/doc/algorithm/improve-studentsmaxgapsperday.txt for advice and (unstable) code on how to make students max gaps per day constraint perfect
13943 
13944 		//not causing more than subgroupsMaxGapsPerRealDay students gaps
13945 
13946 		//TODO: improve, check
13947 
13948 		okstudentsmaxgapsperrealday=true;
13949 
13950 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
13951 			if(haveStudentsMaxGapsPerRealDay){
13952 				//okstudentsmaxgapsperrealday=true;
13953 				for(int sbg : qAsConst(act->iSubgroupsList))
13954 					if(!skipRandom(subgroupsMaxGapsPerRealDayPercentage[sbg])){
13955 						assert(subgroupsMaxGapsPerRealDayPercentage[sbg]==100);
13956 
13957 						//preliminary test
13958 						int _total=0;
13959 						int _remnf=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
13960 						bool _haveMaxBegs=(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0);
13961 						for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
13962 							_total+=newSubgroupsRealDayNHours(sbg,d2);
13963 							int _g=newSubgroupsRealDayNGaps(sbg,d2);
13964 							if(_haveMaxBegs){
13965 								int _fg=newSubgroupsRealDayNFirstGaps(sbg,d2);
13966 								if(_fg==0){
13967 									if(_g>subgroupsMaxGapsPerRealDayMaxGaps[sbg])
13968 										_total+=_g-subgroupsMaxGapsPerRealDayMaxGaps[sbg];
13969 								}
13970 								else if(_fg==1){
13971 									if(_remnf>0)
13972 										_remnf--;
13973 									else
13974 										_total++;
13975 									if(_g>subgroupsMaxGapsPerRealDayMaxGaps[sbg])
13976 										_total+=_g-subgroupsMaxGapsPerRealDayMaxGaps[sbg];
13977 								}
13978 								else if(_fg>=2){
13979 									if(_g + _fg - 1 <= subgroupsMaxGapsPerRealDayMaxGaps[sbg])
13980 										_total++;
13981 									else{
13982 										if(_remnf>0)
13983 											_remnf--;
13984 										else
13985 											_total++;
13986 										_total++;
13987 										assert(_g + _fg - 2 >= subgroupsMaxGapsPerRealDayMaxGaps[sbg]);
13988 										_total+=(_g + _fg - 2 - subgroupsMaxGapsPerRealDayMaxGaps[sbg]);
13989 									}
13990 								}
13991 								else
13992 									assert(0);
13993 							}
13994 							else{
13995 								if(_g > subgroupsMaxGapsPerRealDayMaxGaps[sbg])
13996 									_total+=_g-subgroupsMaxGapsPerRealDayMaxGaps[sbg];
13997 							}
13998 						}
13999 
14000 						if(_total<=nHoursPerSubgroup[sbg]) //OK
14001 							continue;
14002 
14003 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
14004 							okstudentsmaxgapsperrealday=false;
14005 							goto impossiblestudentsmaxgapsperrealday;
14006 						}
14007 
14008 						getSbgTimetable(sbg, conflActivities[newtime]);
14009 						sbgGetNHoursGapsRealDays(sbg);
14010 						sbgGetNHoursGaps(sbg); //bug fix on 2020-02-29, because the remove an activity functions below need to know the number of hours per half-day.
14011 
14012 						for(;;){
14013 							int total=0;
14014 							int remnf=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
14015 							bool haveMaxBegs=(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0);
14016 							for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
14017 								total+=sbgRealDayNHours[d2];
14018 								int g=sbgRealDayNGaps[d2];
14019 								if(haveMaxBegs){
14020 									int fg=sbgRealDayNFirstGaps[d2];
14021 									if(fg==0){
14022 										if(g>subgroupsMaxGapsPerRealDayMaxGaps[sbg])
14023 											total+=g-subgroupsMaxGapsPerRealDayMaxGaps[sbg];
14024 									}
14025 									else if(fg==1){
14026 										if(remnf>0)
14027 											remnf--;
14028 										else
14029 											total++;
14030 										if(g>subgroupsMaxGapsPerRealDayMaxGaps[sbg])
14031 											total+=g-subgroupsMaxGapsPerRealDayMaxGaps[sbg];
14032 									}
14033 									else if(fg>=2){
14034 										if(g + fg - 1 <= subgroupsMaxGapsPerRealDayMaxGaps[sbg])
14035 											total++;
14036 										else{
14037 											if(remnf>0)
14038 												remnf--;
14039 											else
14040 												total++;
14041 											total++;
14042 											assert(g + fg - 2 >= subgroupsMaxGapsPerRealDayMaxGaps[sbg]);
14043 											total+=(g + fg - 2 - subgroupsMaxGapsPerRealDayMaxGaps[sbg]);
14044 										}
14045 									}
14046 									else
14047 										assert(0);
14048 								}
14049 								else{
14050 									if(g > subgroupsMaxGapsPerRealDayMaxGaps[sbg])
14051 										total+=g-subgroupsMaxGapsPerRealDayMaxGaps[sbg];
14052 								}
14053 							}
14054 
14055 							if(total<=nHoursPerSubgroup[sbg]) //OK
14056 								break;
14057 
14058 							//remove an activity from the beginning or from the end of a real day
14059 							//following code is identical to maxgapsperweek
14060 							//remove an activity
14061 							int ai2=-1;
14062 
14063 							//it should also be allowed to take from anywhere, but it is risky to change now
14064 							if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
14065 								bool k=subgroupRemoveAnActivityFromEndAfternoon(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
14066 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
14067 								if(!k){
14068 									bool kk;
14069 									if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0 &&
14070 									 (subgroupsMaxGapsPerRealDayMaxGaps[sbg]==0 || subgroupsMaxGapsPerWeekForRealDaysMaxGaps[sbg]==0) )
14071 										kk=false;
14072 									else
14073 										kk=subgroupRemoveAnActivityFromBeginMorning(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
14074 
14075 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
14076 									if(!kk){
14077 										if(level==0){
14078 											//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
14079 											//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
14080 										}
14081 										okstudentsmaxgapsperrealday=false;
14082 										goto impossiblestudentsmaxgapsperrealday;
14083 									}
14084 								}
14085 							}
14086 							else{
14087 								bool k=subgroupRemoveAnActivityFromBeginMorningOrEndAfternoon(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
14088 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
14089 								if(!k){
14090 									if(level==0){
14091 										//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
14092 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
14093 									}
14094 									okstudentsmaxgapsperrealday=false;
14095 									goto impossiblestudentsmaxgapsperrealday;
14096 								}
14097 							}
14098 
14099 							assert(ai2>=0);
14100 
14101 							removeAi2FromSbgTimetable(ai2);
14102 							updateSbgNHoursGapsRealDay(sbg, (c.times[ai2]%gt.rules.nDaysPerWeek)/2);
14103 							updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek); //bug fix on 2020-02-29, because the remove an activity functions above need to know the number of hours per half-day.
14104 						}
14105 					}
14106 			}
14107 		}
14108 
14109 impossiblestudentsmaxgapsperrealday:
14110 		if(!okstudentsmaxgapsperrealday){
14111 			if(updateSubgroups || updateTeachers)
14112 				removeAiFromNewTimetable(ai, act, d, h);
14113 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
14114 
14115 			nConflActivities[newtime]=MAX_ACTIVITIES;
14116 			continue;
14117 		}
14118 
14119 		////////////////////////////END max gaps per real day
14120 
14121 /////////////////////////////////////////////////////////////////////////////////////////////
14122 
14123 		//to be put after max gaps and early!!! because of an assert
14124 
14125 		//allowed from students max hours daily
14126 		//TODO: this should take care of students max gaps per day also. Very critical changes, so be very careful if you do them. Safer -> leave them as they are now.
14127 		//see file fet-v.v.v/doc/algorithm/improve-studentsmaxgapsperday.txt for advice and (unstable) code on how to make students max gaps per day constraint perfect
14128 		okstudentsmaxhoursdaily=true;
14129 
14130 		for(int sbg : qAsConst(act->iSubgroupsList)){
14131 			for(int count=0; count<2; count++){
14132 				int limitHoursDaily;
14133 				double percentage;
14134 				if(count==0){
14135 					limitHoursDaily=subgroupsMaxHoursDailyMaxHours1[sbg];
14136 					percentage=subgroupsMaxHoursDailyPercentages1[sbg];
14137 				}
14138 				else{
14139 					limitHoursDaily=subgroupsMaxHoursDailyMaxHours2[sbg];
14140 					percentage=subgroupsMaxHoursDailyPercentages2[sbg];
14141 				}
14142 
14143 				if(limitHoursDaily<0)
14144 					continue;
14145 
14146 				//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
14147 				//	continue;
14148 
14149 				bool increased;
14150 				if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
14151 					if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14152 						//both
14153 						if(oldSubgroupsDayNHours(sbg,d)+oldSubgroupsDayNGaps(sbg,d)+oldSubgroupsDayNFirstGaps(sbg,d)<
14154 						  newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNGaps(sbg,d)+newSubgroupsDayNFirstGaps(sbg,d)
14155 						  || oldSubgroupsDayNHours(sbg,d)<newSubgroupsDayNHours(sbg,d))
14156 							increased=true;
14157 						else
14158 							increased=false;
14159 					}
14160 					else{
14161 						//only at beginning
14162 						if(oldSubgroupsDayNHours(sbg,d)+oldSubgroupsDayNFirstGaps(sbg,d)<
14163 						  newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNFirstGaps(sbg,d)
14164 						  || oldSubgroupsDayNHours(sbg,d)<newSubgroupsDayNHours(sbg,d))
14165 							increased=true;
14166 						else
14167 							increased=false;
14168 					}
14169 				}
14170 				else{
14171 					if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14172 						//only max gaps
14173 						if(oldSubgroupsDayNHours(sbg,d)+oldSubgroupsDayNGaps(sbg,d)<
14174 						  newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNGaps(sbg,d)
14175 						  || oldSubgroupsDayNHours(sbg,d)<newSubgroupsDayNHours(sbg,d))
14176 							increased=true;
14177 						else
14178 							increased=false;
14179 					}
14180 					else{
14181 						//none
14182 						if(oldSubgroupsDayNHours(sbg,d)<newSubgroupsDayNHours(sbg,d))
14183 							increased=true;
14184 						else
14185 							increased=false;
14186 					}
14187 				}
14188 
14189 				if(limitHoursDaily>=0 && !skipRandom(percentage) && increased){
14190 					if(limitHoursDaily<act->duration){
14191 						okstudentsmaxhoursdaily=false;
14192 						goto impossiblestudentsmaxhoursdaily;
14193 					}
14194 
14195 					if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0 && subgroupsMaxGapsPerWeekMaxGaps[sbg]==0){
14196 						if(newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNGaps(sbg,d)+newSubgroupsDayNFirstGaps(sbg,d) > limitHoursDaily){
14197 							okstudentsmaxhoursdaily=false;
14198 							goto impossiblestudentsmaxhoursdaily;
14199 						}
14200 						else //OK
14201 							continue;
14202 					}
14203 
14204 					//////////////////////////new
14205 					bool _ok;
14206 					if(newSubgroupsDayNHours(sbg,d)>limitHoursDaily){
14207 						_ok=false; //trivially
14208 					}
14209 					else{
14210 						//basically, see that the gaps are enough
14211 						// Comment added on 2020-09-15: This code was written a long time ago. It cares that the gaps are enough, but it is more like a heuristic,
14212 						// because the weight might be any real number below 100.0%. So on other days the constraints should be allowed to be broken.
14213 						// However, it is very risky to change now. I think that the best would be to allow max hours daily only with 100.0% weight,
14214 						// but unfortunately I think that many users have files with weight <100.0%.
14215 						// Also, don't forget that we might have two constraints max hours daily for each subgroup.
14216 						if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
14217 							if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14218 								//both
14219 								int rg=subgroupsMaxGapsPerWeekMaxGaps[sbg]+subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
14220 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
14221 									if(d2!=d){
14222 										int g=limitHoursDaily-newSubgroupsDayNHours(sbg,d2);
14223 										//TODO: if g lower than 0 make g 0
14224 										//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
14225 										g=newSubgroupsDayNFirstGaps(sbg,d2)+newSubgroupsDayNGaps(sbg,d2)-g;
14226 										if(g>0)
14227 											rg-=g;
14228 									}
14229 								}
14230 
14231 								if(rg<0)
14232 									rg=0;
14233 
14234 								int hg=newSubgroupsDayNGaps(sbg,d)+newSubgroupsDayNFirstGaps(sbg,d)-rg;
14235 								if(hg<0)
14236 									hg=0;
14237 
14238 								if(hg+newSubgroupsDayNHours(sbg,d) > limitHoursDaily){
14239 									_ok=false;
14240 								}
14241 								else
14242 									_ok=true;
14243 							}
14244 							else{
14245 								//only max beginnings
14246 								int lateBeginnings=0;
14247 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
14248 									if(d2!=d){
14249 										if(newSubgroupsDayNHours(sbg,d2)>=limitHoursDaily && newSubgroupsDayNFirstGaps(sbg,d2)==1)
14250 											lateBeginnings++;
14251 									}
14252 								}
14253 
14254 								int fg=0, ah=0; //first gaps, added hours
14255 								if(newSubgroupsDayNFirstGaps(sbg,d)==0){
14256 									fg=0;
14257 									ah=0;
14258 								}
14259 								else if(newSubgroupsDayNFirstGaps(sbg,d)==1){
14260 									fg=1;
14261 									ah=0;
14262 									if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0 ||
14263 									 (subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]>0 &&
14264 									 lateBeginnings>=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]))
14265 										ah+=fg;
14266 
14267 								}
14268 								else if(newSubgroupsDayNFirstGaps(sbg,d)>=2){
14269 									fg=0;
14270 									ah=1;
14271 								}
14272 
14273 								if(ah+newSubgroupsDayNHours(sbg,d) > limitHoursDaily){
14274 									_ok=false;
14275 								}
14276 								else
14277 									_ok=true;
14278 							}
14279 						}
14280 						else{
14281 							if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14282 								//only max gaps
14283 								int rg=subgroupsMaxGapsPerWeekMaxGaps[sbg];
14284 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
14285 									if(d2!=d){
14286 										int g=limitHoursDaily-newSubgroupsDayNHours(sbg,d2);
14287 										//TODO: if g lower than 0 make g 0
14288 										//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
14289 										g=newSubgroupsDayNGaps(sbg,d2)-g;
14290 										if(g>0)
14291 											rg-=g;
14292 									}
14293 								}
14294 
14295 								if(rg<0)
14296 									rg=0;
14297 
14298 								int hg=newSubgroupsDayNGaps(sbg,d)-rg;
14299 								if(hg<0)
14300 									hg=0;
14301 
14302 								if(hg+newSubgroupsDayNHours(sbg,d) > limitHoursDaily){
14303 									_ok=false;
14304 								}
14305 								else
14306 									_ok=true;
14307 							}
14308 							else{
14309 								//none
14310 								_ok=true;
14311 							}
14312 						}
14313 					}
14314 
14315 					/////////////////////////////
14316 
14317 					//preliminary test
14318 					if(_ok){
14319 						continue;
14320 					}
14321 
14322 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
14323 						okstudentsmaxhoursdaily=false;
14324 						goto impossiblestudentsmaxhoursdaily;
14325 					}
14326 
14327 					getSbgTimetable(sbg, conflActivities[newtime]);
14328 					sbgGetNHoursGaps(sbg);
14329 
14330 					//OLD COMMENT BELOW: (now things seem theoretically and practically OK)
14331 					//theoretically, it should be canTakeFromBegin = true all time and ctfAnywhere = true if max gaps per week is not 0.
14332 					//but practically, I tried these changes and it was 30% slower for a modified german sample (with max gaps per day=1,
14333 					//12 hours per day, removed subacts. pref. times, max hours daily 6 for students).
14334 					bool canTakeFromBegin=(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]!=0); //-1 or >0
14335 					bool canTakeFromEnd=true;
14336 					bool canTakeFromAnywhere=(subgroupsMaxGapsPerWeekMaxGaps[sbg]!=0 && subgroupsMaxGapsPerDayMaxGaps[sbg]!=0); //-1 or >0
14337 					bool canTakeFromBeginOrEndAnyDay=(subgroupsMaxGapsPerWeekMaxGaps[sbg]>=0 ||
14338 					 subgroupsMaxGapsPerDayMaxGaps[sbg]>=0 || subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]>=0);
14339 
14340 					for(;;){
14341 						//////////////////////////new
14342 						bool ok;
14343 						if(sbgDayNHours[d]>limitHoursDaily){
14344 							ok=false; //trivially
14345 						}
14346 						else{
14347 							//basically, see that the gaps are enough
14348 							// Comment added on 2020-09-15: This code was written a long time ago. It cares that the gaps are enough, but it is more like a heuristic,
14349 							// because the weight might be any real number below 100.0%. So on other days the constraints should be allowed to be broken.
14350 							// However, it is very risky to change now. I think that the best would be to allow max hours daily only with 100.0% weight,
14351 							// but unfortunately I think that many users have files with weight <100.0%.
14352 							// Also, don't forget that we might have two constraints max hours daily for each subgroup.
14353 							if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
14354 								if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14355 									//both
14356 									int rg=subgroupsMaxGapsPerWeekMaxGaps[sbg]+subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
14357 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
14358 										if(d2!=d){
14359 											int g=limitHoursDaily-sbgDayNHours[d2];
14360 											//TODO: if g lower than 0 make g 0
14361 											//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
14362 											g=sbgDayNFirstGaps[d2]+sbgDayNGaps[d2]-g;
14363 											if(g>0)
14364 												rg-=g;
14365 										}
14366 									}
14367 
14368 									if(rg<0)
14369 										rg=0;
14370 
14371 									int hg=sbgDayNGaps[d]+sbgDayNFirstGaps[d]-rg;
14372 									if(hg<0)
14373 										hg=0;
14374 
14375 									if(hg+sbgDayNHours[d] > limitHoursDaily){
14376 										ok=false;
14377 									}
14378 									else
14379 										ok=true;
14380 								}
14381 								else{
14382 									//only max beginnings
14383 									int lateBeginnings=0;
14384 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
14385 										if(d2!=d){
14386 											if(sbgDayNHours[d2]>=limitHoursDaily && sbgDayNFirstGaps[d2]==1)
14387 												lateBeginnings++;
14388 										}
14389 									}
14390 
14391 									int fg=0, ah=0; //first gaps, added hours
14392 									if(sbgDayNFirstGaps[d]==0){
14393 										fg=0;
14394 										ah=0;
14395 									}
14396 									else if(sbgDayNFirstGaps[d]==1){
14397 										fg=1;
14398 										ah=0;
14399 										if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0 ||
14400 										 (subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]>0 &&
14401 										 lateBeginnings>=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]))
14402 											ah+=fg;
14403 
14404 									}
14405 									else if(sbgDayNFirstGaps[d]>=2){
14406 										fg=0;
14407 										ah=1;
14408 									}
14409 
14410 									if(ah+sbgDayNHours[d] > limitHoursDaily){
14411 										ok=false;
14412 									}
14413 									else
14414 										ok=true;
14415 								}
14416 							}
14417 							else{
14418 								if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14419 									//only max gaps
14420 									int rg=subgroupsMaxGapsPerWeekMaxGaps[sbg];
14421 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
14422 										if(d2!=d){
14423 											int g=limitHoursDaily-sbgDayNHours[d2];
14424 											//TODO: if g lower than 0 make g 0
14425 											//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
14426 											g=sbgDayNGaps[d2]-g;
14427 											if(g>0)
14428 												rg-=g;
14429 										}
14430 									}
14431 
14432 									if(rg<0)
14433 										rg=0;
14434 
14435 									int hg=sbgDayNGaps[d]-rg;
14436 									if(hg<0)
14437 										hg=0;
14438 
14439 									if(hg+sbgDayNHours[d] > limitHoursDaily){
14440 										ok=false;
14441 									}
14442 									else
14443 										ok=true;
14444 								}
14445 								else{
14446 									//none
14447 									ok=true;
14448 								}
14449 							}
14450 						}
14451 						/////////////////////////////
14452 
14453 						if(ok){
14454 							break;
14455 						}
14456 
14457 						int ai2=-1;
14458 
14459 						bool kk=false;
14460 						if(canTakeFromEnd)
14461 							kk=subgroupRemoveAnActivityFromEndCertainDay(d, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
14462 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
14463 						if(!kk){
14464 							canTakeFromEnd=false;
14465 							bool k=false;
14466 							if(canTakeFromBegin){
14467 								k=subgroupRemoveAnActivityFromBeginCertainDay(d, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
14468 								if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]>0)
14469 									canTakeFromBegin=false;
14470 							}
14471 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
14472 							if(!k){
14473 								canTakeFromBegin=false;
14474 								bool ka=false;
14475 								if(canTakeFromAnywhere)
14476 									ka=subgroupRemoveAnActivityFromAnywhereCertainDay(d, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
14477 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
14478 
14479 								if(!ka){
14480 									canTakeFromAnywhere=false;
14481 									bool kaa=false;
14482 									if(canTakeFromBeginOrEndAnyDay && sbgDayNHours[d]<=limitHoursDaily)
14483 										//Fix on 2017-08-26, to solve Volker Dirr's bug report
14484 										kaa=subgroupRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
14485 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
14486 
14487 									if(!kaa){
14488 										canTakeFromBeginOrEndAnyDay=false; //useless
14489 										if(level==0){
14490 											//this should not be displayed
14491 											//cout<<"WARNING - file "<<__FILE__<<" line "<<__LINE__<<endl;
14492 										}
14493 										okstudentsmaxhoursdaily=false;
14494 										goto impossiblestudentsmaxhoursdaily;
14495 									}
14496 								}
14497 							}
14498 						}
14499 
14500 						assert(ai2>=0);
14501 
14502 						removeAi2FromSbgTimetable(ai2);
14503 						updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
14504 					}
14505 				}
14506 			}
14507 		}
14508 
14509 impossiblestudentsmaxhoursdaily:
14510 		if(!okstudentsmaxhoursdaily){
14511 			if(updateSubgroups || updateTeachers)
14512 				removeAiFromNewTimetable(ai, act, d, h);
14513 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
14514 
14515 			nConflActivities[newtime]=MAX_ACTIVITIES;
14516 			continue;
14517 		}
14518 
14519 /////////////////////////////////////////////////////////////////////////////////////////////
14520 
14521 		//to be put after max gaps and early!!! because of an assert
14522 
14523 		//allowed from students max hours daily real days
14524 		//TODO: this should take care of students max gaps per day also. Very critical changes, so be very careful if you do them. Safer -> leave them as they are now.
14525 		//see file fet-v.v.v/doc/algorithm/improve-studentsmaxgapsperday.txt for advice and (unstable) code on how to make students max gaps per day constraint perfect
14526 		okstudentsmaxhoursdailyrealdays=true;
14527 
14528 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
14529 			for(int sbg : qAsConst(act->iSubgroupsList)){
14530 				for(int count=0; count<2; count++){
14531 					int limitHoursDaily;
14532 					double percentage;
14533 					if(count==0){
14534 						limitHoursDaily=subgroupsMaxHoursDailyRealDaysMaxHours1[sbg];
14535 						percentage=subgroupsMaxHoursDailyRealDaysPercentages1[sbg];
14536 					}
14537 					else{
14538 						limitHoursDaily=subgroupsMaxHoursDailyRealDaysMaxHours2[sbg];
14539 						percentage=subgroupsMaxHoursDailyRealDaysPercentages2[sbg];
14540 					}
14541 
14542 					if(limitHoursDaily<0)
14543 						continue;
14544 
14545 					//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
14546 					//	continue;
14547 
14548 					bool increased;
14549 					/*if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
14550 						if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14551 							//both
14552 							if(oldSubgroupsDayNHours(sbg,d)+oldSubgroupsDayNGaps(sbg,d)+oldSubgroupsDayNFirstGaps(sbg,d)<
14553 							  newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNGaps(sbg,d)+newSubgroupsDayNFirstGaps(sbg,d)
14554 							  || oldSubgroupsDayNHours(sbg,d)<newSubgroupsDayNHours(sbg,d))
14555 								increased=true;
14556 							else
14557 								increased=false;
14558 						}
14559 						else{
14560 							//only at beginning
14561 							if(oldSubgroupsDayNHours(sbg,d)+oldSubgroupsDayNFirstGaps(sbg,d)<
14562 							  newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNFirstGaps(sbg,d)
14563 							  || oldSubgroupsDayNHours(sbg,d)<newSubgroupsDayNHours(sbg,d))
14564 								increased=true;
14565 							else
14566 								increased=false;
14567 						}
14568 					}
14569 					else{
14570 						if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14571 							//only max gaps
14572 							if(oldSubgroupsDayNHours(sbg,d)+oldSubgroupsDayNGaps(sbg,d)<
14573 							  newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNGaps(sbg,d)
14574 							  || oldSubgroupsDayNHours(sbg,d)<newSubgroupsDayNHours(sbg,d))
14575 								increased=true;
14576 							else
14577 								increased=false;
14578 						}
14579 						else{
14580 							//none
14581 							if(oldSubgroupsDayNHours(sbg,d)<newSubgroupsDayNHours(sbg,d))
14582 								increased=true;
14583 							else
14584 								increased=false;
14585 						}
14586 					}*/
14587 
14588 					//Liviu Lalescu 2021-03-29: I think this needs to remain true, because of gaps per day/real day/real day per week.
14589 					increased=true; //?????
14590 
14591 					if(limitHoursDaily>=0 && !skipRandom(percentage) && increased){
14592 						if(limitHoursDaily<act->duration){
14593 							okstudentsmaxhoursdailyrealdays=false;
14594 							goto impossiblestudentsmaxhoursdailyrealdays;
14595 						}
14596 
14597 						if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0 && subgroupsMaxGapsPerWeekMaxGaps[sbg]==0){
14598 							if(newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNGaps(sbg,d)+newSubgroupsDayNFirstGaps(sbg,d)+
14599 							  newSubgroupsDayNHours(sbg,dpair)+newSubgroupsDayNGaps(sbg,dpair)+newSubgroupsDayNFirstGaps(sbg,dpair)> limitHoursDaily){
14600 								okstudentsmaxhoursdailyrealdays=false;
14601 								goto impossiblestudentsmaxhoursdailyrealdays;
14602 							}
14603 							else //OK
14604 								continue;
14605 						}
14606 
14607 						//////////////////////////new
14608 						bool _ok;
14609 						if(newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNHours(sbg,dpair)>limitHoursDaily){
14610 							_ok=false; //trivially
14611 						}
14612 						//basically, see that the gaps are enough
14613 						else{
14614 							if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
14615 								if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14616 									//both
14617 									int rg=subgroupsMaxGapsPerWeekMaxGaps[sbg]+subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
14618 									for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
14619 										if(d2!=d/2){
14620 											int dfet1=d2*2, dfet2=d2*2+1;
14621 											int g=limitHoursDaily-newSubgroupsDayNHours(sbg,dfet1)-newSubgroupsDayNHours(sbg,dfet2);
14622 											//TODO: if g lower than 0 make g 0
14623 											//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
14624 											g=newSubgroupsDayNFirstGaps(sbg,dfet1)+newSubgroupsDayNGaps(sbg,dfet1)+
14625 											  newSubgroupsDayNFirstGaps(sbg,dfet2)+newSubgroupsDayNGaps(sbg,dfet2)-g;
14626 											if(g>0)
14627 												rg-=g;
14628 										}
14629 									}
14630 
14631 									if(rg<0)
14632 										rg=0;
14633 
14634 									int hg=newSubgroupsDayNGaps(sbg,d)+newSubgroupsDayNFirstGaps(sbg,d)+
14635 									  newSubgroupsDayNGaps(sbg,dpair)+newSubgroupsDayNFirstGaps(sbg,dpair)-rg;
14636 									if(hg<0)
14637 										hg=0;
14638 
14639 									if(hg+newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNHours(sbg,dpair) > limitHoursDaily){
14640 										_ok=false;
14641 									}
14642 									else
14643 										_ok=true;
14644 								}
14645 								else{
14646 									//only max beginnings
14647 									/*
14648 									int lateBeginnings=0;
14649 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
14650 										if(d2!=d){
14651 											if(newSubgroupsDayNHours(sbg,d2)>=limitHoursDaily && newSubgroupsDayNFirstGaps(sbg,d2)==1)
14652 												lateBeginnings++;
14653 										}
14654 									}
14655 
14656 									int fg=0, ah=0; //first gaps, added hours
14657 									if(newSubgroupsDayNFirstGaps(sbg,d)==0){
14658 										fg=0;
14659 										ah=0;
14660 									}
14661 									else if(newSubgroupsDayNFirstGaps(sbg,d)==1){
14662 										fg=1;
14663 										ah=0;
14664 										if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0 ||
14665 										 (subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]>0 &&
14666 										 lateBeginnings>=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]))
14667 											ah+=fg;
14668 
14669 									}
14670 									else if(newSubgroupsDayNFirstGaps(sbg,d)>=2){
14671 										fg=0;
14672 										ah=1;
14673 									}*/
14674 
14675 									int ah=0;
14676 									if(ah+newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNHours(sbg,dpair) > limitHoursDaily){
14677 										_ok=false;
14678 									}
14679 									else
14680 										_ok=true;
14681 								}
14682 							}
14683 							else{
14684 								if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14685 									//only max gaps
14686 									int rg=subgroupsMaxGapsPerWeekMaxGaps[sbg];
14687 									for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
14688 										if(d2!=d/2){
14689 											int dfet1=d2*2, dfet2=d2*2+1;
14690 											int g=limitHoursDaily-newSubgroupsDayNHours(sbg,dfet1)-newSubgroupsDayNHours(sbg,dfet2);
14691 											//TODO: if g lower than 0 make g 0
14692 											//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
14693 											g=newSubgroupsDayNGaps(sbg,dfet1)+newSubgroupsDayNGaps(sbg,dfet2)-g;
14694 											if(g>0)
14695 												rg-=g;
14696 										}
14697 									}
14698 
14699 									if(rg<0)
14700 										rg=0;
14701 
14702 									int hg=newSubgroupsDayNGaps(sbg,d)+newSubgroupsDayNGaps(sbg,dpair)-rg;
14703 									if(hg<0)
14704 										hg=0;
14705 
14706 									if(hg+newSubgroupsDayNHours(sbg,d)+newSubgroupsDayNHours(sbg,dpair) > limitHoursDaily){
14707 										_ok=false;
14708 									}
14709 									else
14710 										_ok=true;
14711 								}
14712 								else{
14713 									//none
14714 									_ok=true;
14715 								}
14716 							}
14717 						}
14718 
14719 						/////////////////////////////
14720 
14721 						//preliminary test
14722 						if(_ok){
14723 							continue;
14724 						}
14725 
14726 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
14727 							okstudentsmaxhoursdailyrealdays=false;
14728 							goto impossiblestudentsmaxhoursdailyrealdays;
14729 						}
14730 
14731 						getSbgTimetable(sbg, conflActivities[newtime]);
14732 						sbgGetNHoursGaps(sbg);
14733 
14734 						//OLD COMMENT BELOW: (now things seem theoretically and practically OK)
14735 						//theoretically, it should be canTakeFromBegin = true all time and ctfAnywhere = true if max gaps per week is not 0.
14736 						//but practically, I tried these changes and it was 30% slower for a modified german sample (with max gaps per day=1,
14737 						//12 hours per day, removed subacts. pref. times, max hours daily 6 for students).
14738 						bool canTakeFromBegin=(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]!=0); //-1 or >0
14739 						bool canTakeFromEnd=true;
14740 						bool canTakeFromAnywhere=(subgroupsMaxGapsPerWeekMaxGaps[sbg]!=0 && subgroupsMaxGapsPerDayMaxGaps[sbg]!=0); //-1 or >0
14741 						bool canTakeFromBeginOrEndAnyDay=(subgroupsMaxGapsPerWeekMaxGaps[sbg]>=0 ||
14742 						 subgroupsMaxGapsPerDayMaxGaps[sbg]>=0 || subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]>=0);
14743 
14744 						for(;;){
14745 							//////////////////////////new
14746 							bool ok;
14747 
14748 							if(sbgDayNHours[d]+sbgDayNHours[dpair]>limitHoursDaily){
14749 								ok=false;
14750 							}
14751 							else{
14752 								if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
14753 									if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14754 										//both
14755 										int rg=subgroupsMaxGapsPerWeekMaxGaps[sbg]+subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg];
14756 										for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
14757 											if(d2!=d/2){
14758 												int dfet1=d2*2, dfet2=d2*2+1;
14759 												int g=limitHoursDaily-sbgDayNHours[dfet1]-sbgDayNHours[dfet2];
14760 												//TODO: if g lower than 0 make g 0
14761 												//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
14762 												g=sbgDayNFirstGaps[dfet1]+sbgDayNGaps[dfet1]+sbgDayNFirstGaps[dfet2]+sbgDayNGaps[dfet2]-g;
14763 												if(g>0)
14764 													rg-=g;
14765 											}
14766 										}
14767 
14768 										if(rg<0)
14769 											rg=0;
14770 
14771 										int hg=sbgDayNGaps[d]+sbgDayNFirstGaps[d]
14772 										  +sbgDayNGaps[dpair]+sbgDayNFirstGaps[dpair]-rg;
14773 										if(hg<0)
14774 											hg=0;
14775 
14776 										if(hg+sbgDayNHours[d]+sbgDayNHours[dpair] > limitHoursDaily){
14777 											ok=false;
14778 										}
14779 										else
14780 											ok=true;
14781 									}
14782 									else{
14783 										//only max beginnings
14784 										/*
14785 										int lateBeginnings=0;
14786 										for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
14787 											if(d2!=d){
14788 												if(sbgDayNHours[d2]>=limitHoursDaily && sbgDayNFirstGaps[d2]==1)
14789 													lateBeginnings++;
14790 											}
14791 										}
14792 
14793 										int fg=0, ah=0; //first gaps, added hours
14794 										if(sbgDayNFirstGaps[d]==0){
14795 											fg=0;
14796 											ah=0;
14797 										}
14798 										else if(sbgDayNFirstGaps[d]==1){
14799 											fg=1;
14800 											ah=0;
14801 											if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0 ||
14802 											 (subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]>0 &&
14803 											 lateBeginnings>=subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]))
14804 												ah+=fg;
14805 
14806 										}
14807 										else if(sbgDayNFirstGaps[d]>=2){
14808 											fg=0;
14809 											ah=1;
14810 										}*/
14811 
14812 										int ah=0;
14813 
14814 										if(ah+sbgDayNHours[d]+sbgDayNHours[dpair] > limitHoursDaily){
14815 											ok=false;
14816 										}
14817 										else
14818 											ok=true;
14819 									}
14820 								}
14821 								else{
14822 									if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
14823 										//only max gaps
14824 										int rg=subgroupsMaxGapsPerWeekMaxGaps[sbg];
14825 										for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
14826 											if(d2!=d/2){
14827 												int dfet1=2*d2, dfet2=2*d2+1;
14828 												int g=limitHoursDaily-sbgDayNHours[dfet1]-sbgDayNHours[dfet2];
14829 												//TODO: if g lower than 0 make g 0
14830 												//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
14831 												g=sbgDayNGaps[dfet1]+sbgDayNGaps[dfet2]-g;
14832 												if(g>0)
14833 													rg-=g;
14834 											}
14835 										}
14836 
14837 										if(rg<0)
14838 											rg=0;
14839 
14840 										int hg=sbgDayNGaps[d]+sbgDayNGaps[dpair]-rg;
14841 										if(hg<0)
14842 											hg=0;
14843 
14844 										if(hg+sbgDayNHours[d]+sbgDayNHours[dpair] > limitHoursDaily){
14845 											ok=false;
14846 										}
14847 										else
14848 											ok=true;
14849 									}
14850 									else{
14851 										//none
14852 										ok=true;
14853 									}
14854 								}
14855 							}
14856 							/////////////////////////////
14857 
14858 							if(ok){
14859 								break;
14860 							}
14861 
14862 							int ai2=-1;
14863 
14864 							bool kk=false;
14865 							if(canTakeFromEnd)
14866 								kk=subgroupRemoveAnActivityFromEndCertainTwoDays(d, dpair, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
14867 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
14868 							if(!kk){
14869 								canTakeFromEnd=false;
14870 								bool k=false;
14871 								if(canTakeFromBegin){
14872 									k=subgroupRemoveAnActivityFromBeginCertainTwoDays(d, dpair, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
14873 									if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]>0)
14874 										canTakeFromBegin=false;
14875 								}
14876 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
14877 								if(!k){
14878 									canTakeFromBegin=false;
14879 									bool ka=false;
14880 									if(canTakeFromAnywhere)
14881 										ka=subgroupRemoveAnActivityFromAnywhereCertainTwoDays(d, dpair, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
14882 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
14883 
14884 									if(!ka){
14885 										canTakeFromAnywhere=false;
14886 										bool kaa=false;
14887 										if(canTakeFromBeginOrEndAnyDay && sbgDayNHours[d]+sbgDayNHours[dpair]<=limitHoursDaily)
14888 											//Fix on 2017-08-26, to solve Volker Dirr's bug report
14889 											kaa=subgroupRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
14890 										assert(conflActivities[newtime].count()==nConflActivities[newtime]);
14891 
14892 										if(!kaa){
14893 											canTakeFromBeginOrEndAnyDay=false; //useless
14894 											if(level==0){
14895 												/*cout<<"subgroup=="<<qPrintable(gt.rules.internalSubgroupsList[sbg]->name)<<endl;
14896 												cout<<"d=="<<d<<endl;
14897 												cout<<"H="<<H<<endl;
14898 												cout<<"Timetable:"<<endl;
14899 												for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
14900 													for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
14901 														cout<<"\t"<<sbgTimetable(d2,h2)<<"\t";
14902 													cout<<endl;
14903 												}*/
14904 
14905 												//this should not be displayed
14906 												//cout<<"WARNING - file "<<__FILE__<<" line "<<__LINE__<<endl;
14907 											}
14908 											okstudentsmaxhoursdailyrealdays=false;
14909 											goto impossiblestudentsmaxhoursdailyrealdays;
14910 										}
14911 									}
14912 								}
14913 							}
14914 
14915 							assert(ai2>=0);
14916 
14917 							removeAi2FromSbgTimetable(ai2);
14918 							updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
14919 						}
14920 					}
14921 				}
14922 			}
14923 		}
14924 
14925 impossiblestudentsmaxhoursdailyrealdays:
14926 		if(!okstudentsmaxhoursdailyrealdays){
14927 			if(updateSubgroups || updateTeachers)
14928 				removeAiFromNewTimetable(ai, act, d, h);
14929 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
14930 
14931 			nConflActivities[newtime]=MAX_ACTIVITIES;
14932 			continue;
14933 		}
14934 
14935 /////////////////////////////////////////////////////////////////////////////////////////////
14936 
14937 		//allowed from students max hours continuously
14938 
14939 		okstudentsmaxhourscontinuously=true;
14940 
14941 		for(int sbg : qAsConst(act->iSubgroupsList)){
14942 			for(int count=0; count<2; count++){
14943 				int limitHoursCont;
14944 				double percentage;
14945 				if(count==0){
14946 					limitHoursCont=subgroupsMaxHoursContinuouslyMaxHours1[sbg];
14947 					percentage=subgroupsMaxHoursContinuouslyPercentages1[sbg];
14948 				}
14949 				else{
14950 					limitHoursCont=subgroupsMaxHoursContinuouslyMaxHours2[sbg];
14951 					percentage=subgroupsMaxHoursContinuouslyPercentages2[sbg];
14952 				}
14953 
14954 				if(limitHoursCont<0) //no constraint
14955 					continue;
14956 
14957 				//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
14958 				//	continue;
14959 
14960 				bool increased;
14961 				int h2;
14962 				for(h2=h; h2<h+act->duration; h2++){
14963 					assert(h2<gt.rules.nHoursPerDay);
14964 					if(subgroupsTimetable(sbg,d,h2)==-1)
14965 						break;
14966 				}
14967 				if(h2<h+act->duration)
14968 					increased=true;
14969 				else
14970 					increased=false;
14971 
14972 				QList<int> removableActs;
14973 
14974 				int nc=act->duration;
14975 				for(h2=h-1; h2>=0; h2--){
14976 					int ai2=subgroupsTimetable(sbg,d,h2);
14977 					assert(ai2==newSubgroupsTimetable(sbg,d,h2));
14978 					assert(ai2!=ai);
14979 					if(ai2>=0 && !conflActivities[newtime].contains(ai2)){
14980 						nc++;
14981 
14982 						if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
14983 							removableActs.append(ai2);
14984 					}
14985 					else
14986 						break;
14987 				}
14988 				for(h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
14989 					int ai2=subgroupsTimetable(sbg,d,h2);
14990 					assert(ai2==newSubgroupsTimetable(sbg,d,h2));
14991 					assert(ai2!=ai);
14992 					if(ai2>=0 && !conflActivities[newtime].contains(ai2)){
14993 						nc++;
14994 
14995 						if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
14996 							removableActs.append(ai2);
14997 					}
14998 					else
14999 						break;
15000 				}
15001 
15002 				if(!increased && percentage==100.0)
15003 					assert(nc<=limitHoursCont);
15004 
15005 				if(!increased || nc<=limitHoursCont) //OK
15006 					continue;
15007 
15008 				assert(limitHoursCont>=0);
15009 
15010 				if(!skipRandom(percentage) && increased){
15011 					if(act->duration>limitHoursCont){
15012 						okstudentsmaxhourscontinuously=false;
15013 						goto impossiblestudentsmaxhourscontinuously;
15014 					}
15015 
15016 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
15017 						okstudentsmaxhourscontinuously=false;
15018 						goto impossiblestudentsmaxhourscontinuously;
15019 					}
15020 
15021 					while(true){
15022 						if(removableActs.count()==0){
15023 							okstudentsmaxhourscontinuously=false;
15024 							goto impossiblestudentsmaxhourscontinuously;
15025 						}
15026 
15027 						int j=-1;
15028 
15029 						if(level==0){
15030 							int optMinWrong=INF;
15031 
15032 							QList<int> tl;
15033 
15034 							for(int q=0; q<removableActs.count(); q++){
15035 								int ai2=removableActs.at(q);
15036 								if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
15037 									optMinWrong=triedRemovals(ai2,c.times[ai2]);
15038 								}
15039 							}
15040 
15041 							for(int q=0; q<removableActs.count(); q++){
15042 								int ai2=removableActs.at(q);
15043 								if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
15044 									tl.append(q);
15045 							}
15046 
15047 							assert(tl.count()>=1);
15048 							j=tl.at(rng.intMRG32k3a(tl.count()));
15049 
15050 							assert(j>=0 && j<removableActs.count());
15051 						}
15052 						else{
15053 							j=rng.intMRG32k3a(removableActs.count());
15054 						}
15055 
15056 						assert(j>=0);
15057 
15058 						int ai2=removableActs.at(j);
15059 
15060 						int t=removableActs.removeAll(ai2);
15061 						assert(t==1);
15062 
15063 						assert(!conflActivities[newtime].contains(ai2));
15064 						conflActivities[newtime].append(ai2);
15065 						nConflActivities[newtime]++;
15066 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
15067 
15068 						////////////
15069 						removableActs.clear();
15070 
15071 						int nc=act->duration;
15072 						int h2;
15073 						for(h2=h-1; h2>=0; h2--){
15074 							int ai2=subgroupsTimetable(sbg,d,h2);
15075 							assert(ai2==newSubgroupsTimetable(sbg,d,h2));
15076 							assert(ai2!=ai);
15077 							if(ai2>=0 && !conflActivities[newtime].contains(ai2)){
15078 								nc++;
15079 
15080 								if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
15081 									removableActs.append(ai2);
15082 							}
15083 							else
15084 								break;
15085 						}
15086 						for(h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
15087 							int ai2=subgroupsTimetable(sbg,d,h2);
15088 							assert(ai2==newSubgroupsTimetable(sbg,d,h2));
15089 							assert(ai2!=ai);
15090 							if(ai2>=0 && !conflActivities[newtime].contains(ai2)){
15091 								nc++;
15092 
15093 								if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
15094 									removableActs.append(ai2);
15095 							}
15096 							else
15097 								break;
15098 						}
15099 
15100 						if(nc<=limitHoursCont) //OK
15101 							break;
15102 						////////////
15103 					}
15104 				}
15105 			}
15106 		}
15107 
15108 impossiblestudentsmaxhourscontinuously:
15109 		if(!okstudentsmaxhourscontinuously){
15110 			if(updateSubgroups || updateTeachers)
15111 				removeAiFromNewTimetable(ai, act, d, h);
15112 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
15113 
15114 			nConflActivities[newtime]=MAX_ACTIVITIES;
15115 			continue;
15116 		}
15117 
15118 /////////////////////////////////////////////////////////////////////////////////////////////
15119 
15120 		//allowed from students activity tag max hours daily
15121 
15122 		//!!!NOT PERFECT, there is room for improvement
15123 
15124 		okstudentsactivitytagmaxhoursdaily=true;
15125 
15126 		if(haveStudentsActivityTagMaxHoursDaily){
15127 
15128 			for(int sbg : qAsConst(act->iSubgroupsList)){
15129 				for(int cnt=0; cnt<subgroupsActivityTagMaxHoursDailyMaxHours[sbg].count(); cnt++){
15130 					int activityTag=subgroupsActivityTagMaxHoursDailyActivityTag[sbg].at(cnt);
15131 
15132 					if(!gt.rules.internalActivitiesList[ai].iActivityTagsSet.contains(activityTag))
15133 						continue;
15134 
15135 					int limitHoursDaily=subgroupsActivityTagMaxHoursDailyMaxHours[sbg].at(cnt);
15136 					double percentage=subgroupsActivityTagMaxHoursDailyPercentage[sbg].at(cnt);
15137 
15138 					assert(limitHoursDaily>=0);
15139 					assert(percentage>=0);
15140 					assert(activityTag>=0 /*&& activityTag<gt.rules.nInternalActivityTags*/);
15141 
15142 					//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
15143 					//	continue;
15144 
15145 					bool increased;
15146 
15147 					int nold=0, nnew=0;
15148 					///////////
15149 					for(int h2=0; h2<h; h2++){
15150 						if(newSubgroupsTimetable(sbg,d,h2)>=0){
15151 							int ai2=newSubgroupsTimetable(sbg,d,h2);
15152 							assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15153 							Activity* act=&gt.rules.internalActivitiesList[ai2];
15154 							if(act->iActivityTagsSet.contains(activityTag)){
15155 								nold++;
15156 								nnew++;
15157 							}
15158 						}
15159 					}
15160 					for(int h2=h; h2<h+act->duration; h2++){
15161 						if(oldSubgroupsTimetable(sbg,d,h2)>=0){
15162 							int ai2=oldSubgroupsTimetable(sbg,d,h2);
15163 							assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15164 							Activity* act=&gt.rules.internalActivitiesList[ai2];
15165 							if(act->iActivityTagsSet.contains(activityTag))
15166 								nold++;
15167 						}
15168 					}
15169 					for(int h2=h; h2<h+act->duration; h2++){
15170 						if(newSubgroupsTimetable(sbg,d,h2)>=0){
15171 							int ai2=newSubgroupsTimetable(sbg,d,h2);
15172 							assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15173 							Activity* act=&gt.rules.internalActivitiesList[ai2];
15174 							if(act->iActivityTagsSet.contains(activityTag))
15175 								nnew++;
15176 						}
15177 					}
15178 					for(int h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
15179 						if(newSubgroupsTimetable(sbg,d,h2)>=0){
15180 							int ai2=newSubgroupsTimetable(sbg,d,h2);
15181 							assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15182 							Activity* act=&gt.rules.internalActivitiesList[ai2];
15183 							if(act->iActivityTagsSet.contains(activityTag)){
15184 								nold++;
15185 								nnew++;
15186 							}
15187 						}
15188 					}
15189 					/////////
15190 					if(nold<nnew)
15191 						increased=true;
15192 					else
15193 						increased=false;
15194 
15195 					if(percentage==100.0)
15196 						assert(nold<=limitHoursDaily);
15197 					if(!increased && percentage==100.0)
15198 						assert(nnew<=limitHoursDaily);
15199 
15200 					if(!increased || nnew<=limitHoursDaily) //OK
15201 						continue;
15202 
15203 					assert(limitHoursDaily>=0);
15204 
15205 					assert(increased);
15206 					assert(nnew>limitHoursDaily);
15207 					if(!skipRandom(percentage)){
15208 						if(act->duration>limitHoursDaily){
15209 							okstudentsactivitytagmaxhoursdaily=false;
15210 							goto impossiblestudentsactivitytagmaxhoursdaily;
15211 						}
15212 
15213 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
15214 							okstudentsactivitytagmaxhoursdaily=false;
15215 							goto impossiblestudentsactivitytagmaxhoursdaily;
15216 						}
15217 
15218 						getSbgTimetable(sbg, conflActivities[newtime]);
15219 						sbgGetNHoursGaps(sbg);
15220 
15221 						while(true){
15222 							int ncrt=0;
15223 							for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
15224 								if(sbgTimetable(d,h2)>=0){
15225 									int ai2=sbgTimetable(d,h2);
15226 									assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15227 									Activity* act=&gt.rules.internalActivitiesList[ai2];
15228 									if(act->iActivityTagsSet.contains(activityTag))
15229 										ncrt++;
15230 								}
15231 							}
15232 
15233 							if(ncrt<=limitHoursDaily)
15234 								break;
15235 
15236 							int ai2=-1;
15237 
15238 							bool ke=subgroupRemoveAnActivityFromAnywhereCertainDayCertainActivityTag(d, activityTag, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
15239 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
15240 
15241 							if(!ke){
15242 								if(level==0){
15243 									//...this is not too good, but hopefully there is no problem
15244 								}
15245 								okstudentsactivitytagmaxhoursdaily=false;
15246 								goto impossiblestudentsactivitytagmaxhoursdaily;
15247 							}
15248 
15249 							assert(ai2>=0);
15250 
15251 							assert(gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag));
15252 
15253 							removeAi2FromSbgTimetable(ai2);
15254 							updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
15255 						}
15256 					}
15257 				}
15258 			}
15259 
15260 		}
15261 
15262 impossiblestudentsactivitytagmaxhoursdaily:
15263 		if(!okstudentsactivitytagmaxhoursdaily){
15264 			if(updateSubgroups || updateTeachers)
15265 				removeAiFromNewTimetable(ai, act, d, h);
15266 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
15267 
15268 			nConflActivities[newtime]=MAX_ACTIVITIES;
15269 			continue;
15270 		}
15271 
15272 /////////////////////////////////////////////////////////////////////////////////////////////
15273 
15274 		//allowed from students activity tag max hours daily per real day
15275 
15276 		//!!!NOT PERFECT, there is room for improvement
15277 
15278 		okstudentsactivitytagmaxhoursdailyrealdays=true;
15279 
15280 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
15281 			if(haveStudentsActivityTagMaxHoursDailyRealDays){
15282 				for(int sbg : qAsConst(act->iSubgroupsList)){
15283 					for(int cnt=0; cnt<subgroupsActivityTagMaxHoursDailyRealDaysMaxHours[sbg].count(); cnt++){
15284 						int activityTag=subgroupsActivityTagMaxHoursDailyRealDaysActivityTag[sbg].at(cnt);
15285 
15286 						if(!gt.rules.internalActivitiesList[ai].iActivityTagsSet.contains(activityTag))
15287 							continue;
15288 
15289 						int limitHoursDaily=subgroupsActivityTagMaxHoursDailyRealDaysMaxHours[sbg].at(cnt);
15290 						double percentage=subgroupsActivityTagMaxHoursDailyRealDaysPercentage[sbg].at(cnt);
15291 
15292 						assert(limitHoursDaily>=0);
15293 						assert(percentage>=0);
15294 						assert(activityTag>=0 /*&& activityTag<gt.rules.nInternalActivityTags*/);
15295 
15296 						//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
15297 						//	continue;
15298 
15299 						bool increased;
15300 
15301 						int nold=0, nnew=0;
15302 						///////////
15303 						for(int h2=0; h2<h; h2++){
15304 							if(newSubgroupsTimetable(sbg,d,h2)>=0){
15305 								int ai2=newSubgroupsTimetable(sbg,d,h2);
15306 								assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15307 								Activity* act=&gt.rules.internalActivitiesList[ai2];
15308 								if(act->iActivityTagsSet.contains(activityTag)){
15309 									nold++;
15310 									nnew++;
15311 								}
15312 							}
15313 						}
15314 						for(int h2=h; h2<h+act->duration; h2++){
15315 							if(oldSubgroupsTimetable(sbg,d,h2)>=0){
15316 								int ai2=oldSubgroupsTimetable(sbg,d,h2);
15317 								assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15318 								Activity* act=&gt.rules.internalActivitiesList[ai2];
15319 								if(act->iActivityTagsSet.contains(activityTag))
15320 									nold++;
15321 							}
15322 						}
15323 						for(int h2=h; h2<h+act->duration; h2++){
15324 							if(newSubgroupsTimetable(sbg,d,h2)>=0){
15325 								int ai2=newSubgroupsTimetable(sbg,d,h2);
15326 								assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15327 								Activity* act=&gt.rules.internalActivitiesList[ai2];
15328 								if(act->iActivityTagsSet.contains(activityTag))
15329 									nnew++;
15330 							}
15331 						}
15332 						for(int h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
15333 							if(newSubgroupsTimetable(sbg,d,h2)>=0){
15334 								int ai2=newSubgroupsTimetable(sbg,d,h2);
15335 								assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15336 								Activity* act=&gt.rules.internalActivitiesList[ai2];
15337 								if(act->iActivityTagsSet.contains(activityTag)){
15338 									nold++;
15339 									nnew++;
15340 								}
15341 							}
15342 						}
15343 						for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
15344 							if(newSubgroupsTimetable(sbg,dpair,h2)>=0){
15345 								int ai2=newSubgroupsTimetable(sbg,dpair,h2);
15346 								assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15347 								Activity* act=&gt.rules.internalActivitiesList[ai2];
15348 								if(act->iActivityTagsSet.contains(activityTag)){
15349 									nold++;
15350 									nnew++;
15351 								}
15352 							}
15353 						}
15354 						/////////
15355 						if(nold<nnew)
15356 							increased=true;
15357 						else
15358 							increased=false;
15359 
15360 						if(percentage==100.0)
15361 							assert(nold<=limitHoursDaily);
15362 						if(!increased && percentage==100.0)
15363 							assert(nnew<=limitHoursDaily);
15364 
15365 						if(!increased || nnew<=limitHoursDaily) //OK
15366 							continue;
15367 
15368 						assert(limitHoursDaily>=0);
15369 
15370 						assert(increased);
15371 						assert(nnew>limitHoursDaily);
15372 						if(!skipRandom(percentage)){
15373 							if(act->duration>limitHoursDaily){
15374 								okstudentsactivitytagmaxhoursdailyrealdays=false;
15375 								goto impossiblestudentsactivitytagmaxhoursdailyrealdays;
15376 							}
15377 
15378 							if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
15379 								okstudentsactivitytagmaxhoursdailyrealdays=false;
15380 								goto impossiblestudentsactivitytagmaxhoursdailyrealdays;
15381 							}
15382 
15383 							getSbgTimetable(sbg, conflActivities[newtime]);
15384 							sbgGetNHoursGaps(sbg);
15385 
15386 							while(true){
15387 								int ncrt=0;
15388 								for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
15389 									if(sbgTimetable(d,h2)>=0){
15390 										int ai2=sbgTimetable(d,h2);
15391 										assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15392 										Activity* act=&gt.rules.internalActivitiesList[ai2];
15393 										if(act->iActivityTagsSet.contains(activityTag))
15394 											ncrt++;
15395 									}
15396 								}
15397 								for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
15398 									if(sbgTimetable(dpair,h2)>=0){
15399 										int ai2=sbgTimetable(dpair,h2);
15400 										assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
15401 										Activity* act=&gt.rules.internalActivitiesList[ai2];
15402 										if(act->iActivityTagsSet.contains(activityTag))
15403 											ncrt++;
15404 									}
15405 								}
15406 
15407 								if(ncrt<=limitHoursDaily)
15408 									break;
15409 
15410 								int ai2=-1;
15411 
15412 								bool ke=subgroupRemoveAnActivityFromAnywhereCertainDayDayPairCertainActivityTag(d, dpair, activityTag, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
15413 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
15414 
15415 								if(!ke){
15416 									if(level==0){
15417 										//...this is not too good, but hopefully there is no problem
15418 									}
15419 									okstudentsactivitytagmaxhoursdailyrealdays=false;
15420 									goto impossiblestudentsactivitytagmaxhoursdailyrealdays;
15421 								}
15422 
15423 								assert(ai2>=0);
15424 
15425 								assert(gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag));
15426 
15427 								removeAi2FromSbgTimetable(ai2);
15428 								updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
15429 							}
15430 						}
15431 					}
15432 				}
15433 			}
15434 		}
15435 
15436 impossiblestudentsactivitytagmaxhoursdailyrealdays:
15437 		if(!okstudentsactivitytagmaxhoursdailyrealdays){
15438 			if(updateSubgroups || updateTeachers)
15439 				removeAiFromNewTimetable(ai, act, d, h);
15440 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
15441 
15442 			nConflActivities[newtime]=MAX_ACTIVITIES;
15443 			continue;
15444 		}
15445 
15446 /////////////////////////////////////////////////////////////////////////////////////////////
15447 
15448 		//allowed from students activity tag max hours continuously
15449 
15450 		okstudentsactivitytagmaxhourscontinuously=true;
15451 
15452 		if(haveStudentsActivityTagMaxHoursContinuously){
15453 
15454 			for(int sbg : qAsConst(act->iSubgroupsList)){
15455 				for(int cnt=0; cnt<subgroupsActivityTagMaxHoursContinuouslyMaxHours[sbg].count(); cnt++){
15456 					int activityTag=subgroupsActivityTagMaxHoursContinuouslyActivityTag[sbg].at(cnt);
15457 
15458 					//if(gt.rules.internalActivitiesList[ai].activityTagIndex!=activityTag)
15459 					//	continue;
15460 					if(!gt.rules.internalActivitiesList[ai].iActivityTagsSet.contains(activityTag))
15461 						continue;
15462 
15463 					int limitHoursCont=subgroupsActivityTagMaxHoursContinuouslyMaxHours[sbg].at(cnt);
15464 					double percentage=subgroupsActivityTagMaxHoursContinuouslyPercentage[sbg].at(cnt);
15465 
15466 					assert(limitHoursCont>=0);
15467 					assert(percentage>=0);
15468 					assert(activityTag>=0 /*&& activityTag<gt.rules.nInternalActivityTags*/);
15469 
15470 					//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
15471 					//	continue;
15472 
15473 					bool increased;
15474 					int h2;
15475 					for(h2=h; h2<h+act->duration; h2++){
15476 						assert(h2<gt.rules.nHoursPerDay);
15477 						if(subgroupsTimetable(sbg,d,h2)==-1)
15478 							break;
15479 						int ai2=subgroupsTimetable(sbg,d,h2);
15480 						assert(ai2>=0);
15481 						if(!gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag))
15482 							break;
15483 					}
15484 					if(h2<h+act->duration)
15485 						increased=true;
15486 					else
15487 						increased=false;
15488 
15489 					QList<int> removableActs;
15490 
15491 					int nc=act->duration;
15492 					for(h2=h-1; h2>=0; h2--){
15493 						int ai2=subgroupsTimetable(sbg,d,h2);
15494 						assert(ai2==newSubgroupsTimetable(sbg,d,h2));
15495 						assert(ai2!=ai);
15496 						if(ai2<0)
15497 							break;
15498 						if(ai2>=0 && !conflActivities[newtime].contains(ai2) &&
15499 						 gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag)){
15500 							nc++;
15501 
15502 							if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
15503 								removableActs.append(ai2);
15504 						}
15505 						else
15506 							break;
15507 					}
15508 					for(h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
15509 						int ai2=subgroupsTimetable(sbg,d,h2);
15510 						assert(ai2==newSubgroupsTimetable(sbg,d,h2));
15511 						assert(ai2!=ai);
15512 						if(ai2<0)
15513 							break;
15514 						if(ai2>=0 && !conflActivities[newtime].contains(ai2) &&
15515 						 gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag)){
15516 							nc++;
15517 
15518 							if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
15519 								removableActs.append(ai2);
15520 						}
15521 						else
15522 							break;
15523 					}
15524 
15525 					if(!increased && percentage==100.0)
15526 						assert(nc<=limitHoursCont);
15527 
15528 					if(!increased || nc<=limitHoursCont) //OK
15529 						continue;
15530 
15531 					assert(limitHoursCont>=0);
15532 
15533 					if(!skipRandom(percentage) && increased){
15534 						if(act->duration>limitHoursCont){
15535 							okstudentsactivitytagmaxhourscontinuously=false;
15536 							goto impossiblestudentsactivitytagmaxhourscontinuously;
15537 						}
15538 
15539 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
15540 							okstudentsactivitytagmaxhourscontinuously=false;
15541 							goto impossiblestudentsactivitytagmaxhourscontinuously;
15542 						}
15543 
15544 						while(true){
15545 							if(removableActs.count()==0){
15546 								okstudentsactivitytagmaxhourscontinuously=false;
15547 								goto impossiblestudentsactivitytagmaxhourscontinuously;
15548 							}
15549 
15550 							int j=-1;
15551 
15552 							if(level==0){
15553 								int optMinWrong=INF;
15554 
15555 								QList<int> tl;
15556 
15557 								for(int q=0; q<removableActs.count(); q++){
15558 									int ai2=removableActs.at(q);
15559 									if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
15560 									 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
15561 									}
15562 								}
15563 
15564 								for(int q=0; q<removableActs.count(); q++){
15565 									int ai2=removableActs.at(q);
15566 									if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
15567 										tl.append(q);
15568 								}
15569 
15570 								assert(tl.count()>=1);
15571 								j=tl.at(rng.intMRG32k3a(tl.count()));
15572 
15573 								assert(j>=0 && j<removableActs.count());
15574 							}
15575 							else{
15576 								j=rng.intMRG32k3a(removableActs.count());
15577 							}
15578 
15579 							assert(j>=0);
15580 
15581 							int ai2=removableActs.at(j);
15582 
15583 							int t=removableActs.removeAll(ai2);
15584 							assert(t==1);
15585 
15586 							assert(!conflActivities[newtime].contains(ai2));
15587 							conflActivities[newtime].append(ai2);
15588 							nConflActivities[newtime]++;
15589 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
15590 
15591 							////////////
15592 							removableActs.clear();
15593 
15594 							int nc=act->duration;
15595 							int h2;
15596 							for(h2=h-1; h2>=0; h2--){
15597 								int ai2=subgroupsTimetable(sbg,d,h2);
15598 								assert(ai2==newSubgroupsTimetable(sbg,d,h2));
15599 								assert(ai2!=ai);
15600 								if(ai2<0)
15601 									break;
15602 								if(ai2>=0 && !conflActivities[newtime].contains(ai2) &&
15603 								 gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag)){
15604 									nc++;
15605 
15606 									if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
15607 										removableActs.append(ai2);
15608 								}
15609 								else
15610 									break;
15611 							}
15612 							for(h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
15613 								int ai2=subgroupsTimetable(sbg,d,h2);
15614 								assert(ai2==newSubgroupsTimetable(sbg,d,h2));
15615 								assert(ai2!=ai);
15616 								if(ai2<0)
15617 									break;
15618 								if(ai2>=0 && !conflActivities[newtime].contains(ai2) &&
15619 								 //gt.rules.internalActivitiesList[ai2].activityTagIndex==activityTag){
15620 								 gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag)){
15621 									nc++;
15622 
15623 									if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
15624 										removableActs.append(ai2);
15625 								}
15626 								else
15627 									break;
15628 							}
15629 
15630 							if(nc<=limitHoursCont) //OK
15631 								break;
15632 							////////////
15633 						}
15634 					}
15635 				}
15636 			}
15637 
15638 		}
15639 
15640 impossiblestudentsactivitytagmaxhourscontinuously:
15641 		if(!okstudentsactivitytagmaxhourscontinuously){
15642 			if(updateSubgroups || updateTeachers)
15643 				removeAiFromNewTimetable(ai, act, d, h);
15644 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
15645 
15646 			nConflActivities[newtime]=MAX_ACTIVITIES;
15647 			continue;
15648 		}
15649 
15650 /////////////////////////////////////////////////////////////////////////////////////////////
15651 
15652 		/////////begin students min hours daily
15653 
15654 		//TODO: this should take care of students max gaps per day also. Very critical changes, so be very careful if you do them. Safer -> leave them as they are now.
15655 		//see file fet-v.v.v/doc/algorithm/improve-studentsmaxgapsperday.txt for advice and (unstable) code on how to make students max gaps per day constraint perfect
15656 		okstudentsminhoursdaily=true;
15657 
15658 		if(gt.rules.mode!=MORNINGS_AFTERNOONS){
15659 			for(int sbg : qAsConst(act->iSubgroupsList)){
15660 				if(subgroupsMinHoursDailyMinHours[sbg][MIN_HOURS_DAILY_INDEX_IN_ARRAY]>=0){
15661 					assert(subgroupsMinHoursDailyPercentages[sbg][MIN_HOURS_DAILY_INDEX_IN_ARRAY]==100);
15662 
15663 					bool allowEmptyDays=subgroupsMinHoursDailyAllowEmptyDays[sbg];
15664 					int minLimitSbg=subgroupsMinHoursDailyMinHours[sbg][MIN_HOURS_DAILY_INDEX_IN_ARRAY];
15665 
15666 					bool skip=skipRandom(subgroupsMinHoursDailyPercentages[sbg][MIN_HOURS_DAILY_INDEX_IN_ARRAY]);
15667 					if(!skip){
15668 						//preliminary test
15669 						bool _ok;
15670 						if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
15671 							if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
15672 								//both limitations
15673 								int remG=0, totalH=0;
15674 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
15675 									int remGDay=newSubgroupsDayNFirstGaps(sbg,d2)+newSubgroupsDayNGaps(sbg,d2);
15676 									if(/*1*/ !allowEmptyDays || newSubgroupsDayNHours(sbg,d2)>0){
15677 										if(newSubgroupsDayNHours(sbg,d2)<minLimitSbg){
15678 											remGDay-=minLimitSbg-newSubgroupsDayNHours(sbg,d2);
15679 											totalH+=minLimitSbg;
15680 										}
15681 										else
15682 											totalH+=newSubgroupsDayNHours(sbg,d2);
15683 									}
15684 									if(remGDay>0)
15685 										remG+=remGDay;
15686 								}
15687 								if((remG+totalH <= nHoursPerSubgroup[sbg]
15688 								  +subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
15689 								  && (totalH <= nHoursPerSubgroup[sbg]))
15690 									_ok=true;
15691 								else
15692 									_ok=false;
15693 							}
15694 							else{
15695 								//only first gaps limitation
15696 								int remG=0, totalH=0;
15697 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
15698 									int remGDay=0;
15699 									if(/*1*/ !allowEmptyDays || newSubgroupsDayNHours(sbg,d2)>0){
15700 										if(newSubgroupsDayNHours(sbg,d2)<minLimitSbg){
15701 											remGDay=0;
15702 											totalH+=minLimitSbg;
15703 										}
15704 										else{
15705 											totalH+=newSubgroupsDayNHours(sbg,d2);
15706 											if(newSubgroupsDayNFirstGaps(sbg,d2)==0)
15707 												remGDay=0;
15708 											else if(newSubgroupsDayNFirstGaps(sbg,d2)==1)
15709 												remGDay=1;
15710 											else if(newSubgroupsDayNFirstGaps(sbg,d2)>=2){
15711 												remGDay=0;
15712 												totalH++;
15713 											}
15714 										}
15715 									}
15716 									if(remGDay>0)
15717 										remG+=remGDay;
15718 								}
15719 								if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg])
15720 								  && (totalH <= nHoursPerSubgroup[sbg]))
15721 									_ok=true;
15722 								else
15723 									_ok=false;
15724 							}
15725 						}
15726 						else{
15727 							if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
15728 								//only max gaps per week limitation
15729 								int remG=0, totalH=0;
15730 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
15731 									int remGDay=newSubgroupsDayNGaps(sbg,d2);
15732 									if(/*1*/ !allowEmptyDays || newSubgroupsDayNHours(sbg,d2)>0){
15733 										if(newSubgroupsDayNHours(sbg,d2)<minLimitSbg){
15734 											remGDay-=minLimitSbg-newSubgroupsDayNHours(sbg,d2);
15735 											totalH+=minLimitSbg;
15736 										}
15737 										else
15738 											totalH+=newSubgroupsDayNHours(sbg,d2);
15739 									}
15740 									if(remGDay>0)
15741 										remG+=remGDay;
15742 								}
15743 								if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
15744 								  && (totalH <= nHoursPerSubgroup[sbg]))
15745 									_ok=true;
15746 								else
15747 									_ok=false;
15748 							}
15749 							else{
15750 								//no limitation
15751 								int totalH=0;
15752 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
15753 									if(/*1*/ !allowEmptyDays || newSubgroupsDayNHours(sbg,d2)>0){
15754 										if(newSubgroupsDayNHours(sbg,d2)<minLimitSbg)
15755 											totalH+=minLimitSbg;
15756 										else
15757 											totalH+=newSubgroupsDayNHours(sbg,d2);
15758 									}
15759 								}
15760 								if(totalH <= nHoursPerSubgroup[sbg])
15761 									_ok=true;
15762 								else
15763 									_ok=false;
15764 							}
15765 						}
15766 
15767 						if(_ok)
15768 							continue;
15769 
15770 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
15771 							okstudentsminhoursdaily=false;
15772 							goto impossiblestudentsminhoursdaily;
15773 						}
15774 
15775 						getSbgTimetable(sbg, conflActivities[newtime]);
15776 						sbgGetNHoursGaps(sbg);
15777 
15778 						for(;;){
15779 							bool ok;
15780 							////////////////////////////
15781 							if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
15782 								if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
15783 									//both limitations
15784 									int remG=0, totalH=0;
15785 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
15786 										int remGDay=sbgDayNFirstGaps[d2]+sbgDayNGaps[d2];
15787 										if(/*1*/ !allowEmptyDays || sbgDayNHours[d2]>0){
15788 											if(sbgDayNHours[d2]<minLimitSbg){
15789 												remGDay-=minLimitSbg-sbgDayNHours[d2];
15790 												totalH+=minLimitSbg;
15791 											}
15792 											else
15793 												totalH+=sbgDayNHours[d2];
15794 										}
15795 										if(remGDay>0)
15796 											remG+=remGDay;
15797 									}
15798 									if((remG+totalH <= nHoursPerSubgroup[sbg]
15799 									  +subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
15800 									  && (totalH <= nHoursPerSubgroup[sbg]))
15801 										ok=true;
15802 									else
15803 										ok=false;
15804 								}
15805 								else{
15806 									//only first gaps limitation
15807 									int remG=0, totalH=0;
15808 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
15809 										int remGDay=0;
15810 										if(/*1*/ !allowEmptyDays || sbgDayNHours[d2]>0){
15811 											if(sbgDayNHours[d2]<minLimitSbg){
15812 												remGDay=0;
15813 												totalH+=minLimitSbg;
15814 											}
15815 											else{
15816 												totalH+=sbgDayNHours[d2];
15817 												if(sbgDayNFirstGaps[d2]==0)
15818 													remGDay=0;
15819 												else if(sbgDayNFirstGaps[d2]==1)
15820 													remGDay=1;
15821 												else if(sbgDayNFirstGaps[d2]>=2){
15822 													remGDay=0;
15823 													totalH++;
15824 												}
15825 											}
15826 										}
15827 										if(remGDay>0)
15828 											remG+=remGDay;
15829 									}
15830 									if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg])
15831 									  && (totalH <= nHoursPerSubgroup[sbg]))
15832 										ok=true;
15833 									else
15834 										ok=false;
15835 								}
15836 							}
15837 							else{
15838 								if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
15839 									//only max gaps per week limitation
15840 									int remG=0, totalH=0;
15841 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
15842 										int remGDay=sbgDayNGaps[d2];
15843 										if(/*1*/ !allowEmptyDays || sbgDayNHours[d2]>0){
15844 											if(sbgDayNHours[d2]<minLimitSbg){
15845 												remGDay-=minLimitSbg-sbgDayNHours[d2];
15846 												totalH+=minLimitSbg;
15847 											}
15848 											else
15849 												totalH+=sbgDayNHours[d2];
15850 										}
15851 										if(remGDay>0)
15852 											remG+=remGDay;
15853 									}
15854 									if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
15855 									  && (totalH <= nHoursPerSubgroup[sbg]))
15856 										ok=true;
15857 									else
15858 										ok=false;
15859 								}
15860 								else{
15861 									//no limitation
15862 									int totalH=0;
15863 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
15864 										if(/*1*/ !allowEmptyDays || sbgDayNHours[d2]>0){
15865 											if(sbgDayNHours[d2]<minLimitSbg)
15866 												totalH+=minLimitSbg;
15867 											else
15868 												totalH+=sbgDayNHours[d2];
15869 										}
15870 									}
15871 									if(totalH <= nHoursPerSubgroup[sbg])
15872 										ok=true;
15873 									else
15874 										ok=false;
15875 								}
15876 							}
15877 							////////////////////////////
15878 
15879 							if(ok)
15880 								break; //ok
15881 
15882 							int ai2=-1;
15883 
15884 							bool kk=subgroupRemoveAnActivityFromEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
15885 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
15886 							if(!kk){
15887 								bool k=subgroupRemoveAnActivityFromBegin(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
15888 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
15889 								if(!k){
15890 									bool ka=subgroupRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
15891 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
15892 
15893 									if(!ka){
15894 										if(level==0){
15895 											//this should not be displayed
15896 											//cout<<"WARNING - unlikely situation - file "<<__FILE__<<" line "<<__LINE__<<endl;
15897 										}
15898 										okstudentsminhoursdaily=false;
15899 										goto impossiblestudentsminhoursdaily;
15900 									}
15901 								}
15902 							}
15903 
15904 							assert(ai2>=0);
15905 
15906 							removeAi2FromSbgTimetable(ai2);
15907 							updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
15908 						}
15909 					}
15910 				}
15911 			}
15912 		}
15913 		else{
15914 			for(int sbg : qAsConst(act->iSubgroupsList)){
15915 				if(subgroupsMinHoursDailyMinHours[sbg][1]>=0){
15916 					assert(subgroupsMinHoursDailyPercentages[sbg][1]==100);
15917 
15918 					bool allowEmptyDays=subgroupsMinHoursDailyAllowEmptyDays[sbg];
15919 					bool allowEmptyMornings=subgroupsMinHoursPerMorningAllowEmptyMornings[sbg];
15920 
15921 					int mhd=1; //min hours per day
15922 					if(subgroupsMinHoursDailyMinHours[sbg][1]>=0)
15923 						mhd=subgroupsMinHoursDailyMinHours[sbg][1];
15924 					assert(mhd>=1);
15925 					int mhm=mhd; //min hours per morning
15926 					if(subgroupsMinHoursDailyMinHours[sbg][0]>=0)
15927 						mhm=subgroupsMinHoursDailyMinHours[sbg][0];
15928 					assert(mhm>=mhd);
15929 
15930 					bool skip=skipRandom(subgroupsMinHoursDailyPercentages[sbg][1]);
15931 					if(!skip){
15932 						//preliminary test
15933 						bool _ok=true;
15934 						if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
15935 							if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
15936 								//both limitations
15937 								//if(allowEmptyDays){
15938 								if(1){
15939 									int remG=0, totalH=0;
15940 
15941 									int nUsedMornings=0;
15942 									int nUsedAfternoons=0;
15943 
15944 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
15945 										int remGDay=newSubgroupsDayNFirstGaps(sbg,d2)+newSubgroupsDayNGaps(sbg,d2);
15946 										if(/*1*/ /*!allowEmptyDays ||*/ newSubgroupsDayNHours(sbg,d2)>0 || (!allowEmptyMornings && (d2%2)==0)){
15947 											if(newSubgroupsDayNHours(sbg,d2)<subgroupsMinHoursDailyMinHours[sbg][d2%2]){
15948 												remGDay-=subgroupsMinHoursDailyMinHours[sbg][d2%2]-newSubgroupsDayNHours(sbg,d2);
15949 												totalH+=subgroupsMinHoursDailyMinHours[sbg][d2%2];
15950 											}
15951 											else
15952 												totalH+=newSubgroupsDayNHours(sbg,d2);
15953 										}
15954 										if(remGDay>0)
15955 											remG+=remGDay;
15956 
15957 										if(newSubgroupsDayNHours(sbg,d2)>0){
15958 											if(d2%2==0)
15959 												nUsedMornings++;
15960 											else
15961 												nUsedAfternoons++;
15962 										}
15963 									}
15964 
15965 									if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
15966 										if(subgroupsMinMorningsPerWeekMinMornings[sbg]>nUsedMornings)
15967 											totalH+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-nUsedMornings)*mhm;
15968 
15969 									if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
15970 										if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>nUsedAfternoons)
15971 											totalH+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-nUsedAfternoons)*mhd;
15972 
15973 									if((remG+totalH <= nHoursPerSubgroup[sbg]
15974 									  +subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
15975 									  && (totalH <= nHoursPerSubgroup[sbg]))
15976 										;//_ok=true;
15977 									else
15978 										_ok=false;
15979 								}
15980 								if(!allowEmptyDays){
15981 									//real days
15982 									int remG=0, totalH=0;
15983 
15984 									for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
15985 										int remGDay=newSubgroupsDayNFirstGaps(sbg,2*d2)+newSubgroupsDayNGaps(sbg,2*d2)
15986 										 + newSubgroupsDayNFirstGaps(sbg,2*d2+1)+newSubgroupsDayNGaps(sbg,2*d2+1);
15987 										if(/*1*/ !allowEmptyDays || newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1)>0){
15988 											if(newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1) < mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/){
15989 												remGDay-=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/-(newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1));
15990 												totalH+=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/;
15991 											}
15992 											else
15993 												totalH+=newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1);
15994 										}
15995 										if(remGDay>0)
15996 											remG+=remGDay;
15997 									}
15998 									if((remG+totalH <= nHoursPerSubgroup[sbg]
15999 									  +subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
16000 									  && (totalH <= nHoursPerSubgroup[sbg]))
16001 										;//_ok=true;
16002 									else
16003 										_ok=false;
16004 								}
16005 							}
16006 							else{
16007 								//only first gaps limitation
16008 								//if(allowEmptyDays){
16009 								if(1){
16010 									int remG=0, totalH=0;
16011 
16012 									int nUsedMornings=0;
16013 									int nUsedAfternoons=0;
16014 
16015 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16016 										int remGDay=0;
16017 										if(/*1*/ /*!allowEmptyDays ||*/ newSubgroupsDayNHours(sbg,d2)>0 || (!allowEmptyMornings && (d2%2)==0)){
16018 											if(newSubgroupsDayNHours(sbg,d2)<subgroupsMinHoursDailyMinHours[sbg][d2%2]){
16019 												remGDay=0;
16020 												totalH+=subgroupsMinHoursDailyMinHours[sbg][d2%2];
16021 											}
16022 											else{
16023 												totalH+=newSubgroupsDayNHours(sbg,d2);
16024 												if(newSubgroupsDayNFirstGaps(sbg,d2)==0)
16025 													remGDay=0;
16026 												else if(newSubgroupsDayNFirstGaps(sbg,d2)==1)
16027 													remGDay=1;
16028 												else if(newSubgroupsDayNFirstGaps(sbg,d2)>=2){
16029 													remGDay=0;
16030 													totalH++;
16031 												}
16032 											}
16033 										}
16034 										if(remGDay>0)
16035 											remG+=remGDay;
16036 
16037 										if(newSubgroupsDayNHours(sbg,d2)>0){
16038 											if(d2%2==0)
16039 												nUsedMornings++;
16040 											else
16041 												nUsedAfternoons++;
16042 										}
16043 									}
16044 									if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
16045 										if(subgroupsMinMorningsPerWeekMinMornings[sbg]>nUsedMornings)
16046 											totalH+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-nUsedMornings)*mhm;
16047 
16048 									if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
16049 										if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>nUsedAfternoons)
16050 											totalH+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-nUsedAfternoons)*mhd;
16051 
16052 									if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg])
16053 									  && (totalH <= nHoursPerSubgroup[sbg]))
16054 										;//_ok=true;
16055 									else
16056 										_ok=false;
16057 								}
16058 								if(!allowEmptyDays){
16059 									//real days
16060 									int remG=0, totalH=0;
16061 									for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
16062 										int remGDay=0;
16063 										if(/*1*/ !allowEmptyDays || newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1)>0){
16064 											if(newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1)<mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/){
16065 												remGDay=0;
16066 												totalH+=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/;
16067 											}
16068 											else{
16069 												totalH+=newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1);
16070 												remGDay=0;
16071 												if(newSubgroupsDayNFirstGaps(sbg,2*d2)==0)
16072 													remGDay+=0;
16073 												else if(newSubgroupsDayNFirstGaps(sbg,2*d2)==1)
16074 													remGDay+=1;
16075 												else if(newSubgroupsDayNFirstGaps(sbg,2*d2)>=2){
16076 													remGDay+=0;
16077 													totalH++;
16078 												}
16079 
16080 												if(newSubgroupsDayNFirstGaps(sbg,2*d2+1)==0)
16081 													remGDay+=0;
16082 												else if(newSubgroupsDayNFirstGaps(sbg,2*d2+1)==1)
16083 													remGDay+=1;
16084 												else if(newSubgroupsDayNFirstGaps(sbg,2*d2+1)>=2){
16085 													remGDay+=0;
16086 													totalH++;
16087 												}
16088 											}
16089 										}
16090 										if(remGDay>0)
16091 											remG+=remGDay;
16092 									}
16093 									if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg])
16094 									  && (totalH <= nHoursPerSubgroup[sbg]))
16095 										;//_ok=true;
16096 									else
16097 										_ok=false;
16098 								}
16099 							}
16100 						}
16101 						else{
16102 							if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
16103 								//only max gaps per week limitation
16104 								//if(allowEmptyDays){
16105 								if(1){
16106 									int remG=0, totalH=0;
16107 
16108 									int nUsedMornings=0;
16109 									int nUsedAfternoons=0;
16110 
16111 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16112 										int remGDay=newSubgroupsDayNGaps(sbg,d2);
16113 										if(/*1*/ /*!allowEmptyDays ||*/ newSubgroupsDayNHours(sbg,d2)>0 || (!allowEmptyMornings && (d2%2)==0)){
16114 											if(newSubgroupsDayNHours(sbg,d2)<subgroupsMinHoursDailyMinHours[sbg][d2%2]){
16115 												remGDay-=subgroupsMinHoursDailyMinHours[sbg][d2%2]-newSubgroupsDayNHours(sbg,d2);
16116 												totalH+=subgroupsMinHoursDailyMinHours[sbg][d2%2];
16117 											}
16118 											else
16119 												totalH+=newSubgroupsDayNHours(sbg,d2);
16120 										}
16121 										if(remGDay>0)
16122 											remG+=remGDay;
16123 
16124 										if(newSubgroupsDayNHours(sbg,d2)>0){
16125 											if(d2%2==0)
16126 												nUsedMornings++;
16127 											else
16128 												nUsedAfternoons++;
16129 										}
16130 									}
16131 									if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
16132 										if(subgroupsMinMorningsPerWeekMinMornings[sbg]>nUsedMornings)
16133 											totalH+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-nUsedMornings)*mhm;
16134 
16135 									if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
16136 										if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>nUsedAfternoons)
16137 											totalH+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-nUsedAfternoons)*mhd;
16138 
16139 									if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
16140 									  && (totalH <= nHoursPerSubgroup[sbg]))
16141 										;//_ok=true;
16142 									else
16143 										_ok=false;
16144 								}
16145 								if(!allowEmptyDays){
16146 									//real days
16147 									int remG=0, totalH=0;
16148 									for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
16149 										int remGDay=newSubgroupsDayNGaps(sbg,2*d2)+newSubgroupsDayNGaps(sbg,2*d2+1);
16150 										if(/*1*/ !allowEmptyDays || newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1)>0){
16151 											if(newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1)<mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/){
16152 												remGDay-=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/-newSubgroupsDayNHours(sbg,2*d2)-newSubgroupsDayNHours(sbg,2*d2+1);
16153 												totalH+=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/;
16154 											}
16155 											else
16156 												totalH+=newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1);
16157 										}
16158 										if(remGDay>0)
16159 											remG+=remGDay;
16160 									}
16161 									if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
16162 									  && (totalH <= nHoursPerSubgroup[sbg]))
16163 										;//_ok=true;
16164 									else
16165 										_ok=false;
16166 								}
16167 							}
16168 							else{
16169 								//no limitation
16170 								//if(allowEmptyDays){
16171 								if(1){
16172 									int totalH=0;
16173 
16174 									int nUsedMornings=0;
16175 									int nUsedAfternoons=0;
16176 
16177 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16178 										if(/*1*/ /*!allowEmptyDays ||*/ newSubgroupsDayNHours(sbg,d2)>0 || (!allowEmptyMornings && (d2%2)==0)){
16179 											if(newSubgroupsDayNHours(sbg,d2)<subgroupsMinHoursDailyMinHours[sbg][d2%2])
16180 												totalH+=subgroupsMinHoursDailyMinHours[sbg][d2%2];
16181 											else
16182 												totalH+=newSubgroupsDayNHours(sbg,d2);
16183 										}
16184 
16185 										if(newSubgroupsDayNHours(sbg,d2)>0){
16186 											if(d2%2==0)
16187 												nUsedMornings++;
16188 											else
16189 												nUsedAfternoons++;
16190 										}
16191 									}
16192 									if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
16193 										if(subgroupsMinMorningsPerWeekMinMornings[sbg]>nUsedMornings)
16194 											totalH+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-nUsedMornings)*mhm;
16195 
16196 									if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
16197 										if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>nUsedAfternoons)
16198 											totalH+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-nUsedAfternoons)*mhd;
16199 
16200 									if(totalH <= nHoursPerSubgroup[sbg])
16201 										;//_ok=true;
16202 									else
16203 										_ok=false;
16204 
16205 									//cout<<"totalH=="<<totalH<<", nHoursPerSubgroup[sbg]=="<<nHoursPerSubgroup[sbg]<<", _ok=="<<_ok<<endl;
16206 								}
16207 								if(!allowEmptyDays){
16208 									//real days
16209 									int totalH=0;
16210 									for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
16211 										if(/*1*/ !allowEmptyDays || newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1)>0){
16212 											if(newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1) < mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/)
16213 												totalH+=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/;
16214 											else
16215 												totalH+=newSubgroupsDayNHours(sbg,2*d2)+newSubgroupsDayNHours(sbg,2*d2+1);
16216 										}
16217 									}
16218 									if(totalH <= nHoursPerSubgroup[sbg])
16219 										;//_ok=true;
16220 									else
16221 										_ok=false;
16222 
16223 									//cout<<"totalH=="<<totalH<<", nHoursPerSubgroup[sbg]=="<<nHoursPerSubgroup[sbg]<<", _ok=="<<_ok<<endl;
16224 								}
16225 							}
16226 						}
16227 
16228 						if(_ok)
16229 							continue;
16230 
16231 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
16232 							okstudentsminhoursdaily=false;
16233 							goto impossiblestudentsminhoursdaily;
16234 						}
16235 
16236 						getSbgTimetable(sbg, conflActivities[newtime]);
16237 						sbgGetNHoursGaps(sbg);
16238 
16239 						for(;;){
16240 							bool ok=true;
16241 							////////////////////////////
16242 							if(subgroupsEarlyMaxBeginningsAtSecondHourPercentage[sbg]>=0){
16243 								if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
16244 									//both limitations
16245 									//if(allowEmptyDays){
16246 									if(1){
16247 										int remG=0, totalH=0;
16248 
16249 										int nUsedMornings=0;
16250 										int nUsedAfternoons=0;
16251 
16252 										for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16253 											int remGDay=sbgDayNFirstGaps[d2]+sbgDayNGaps[d2];
16254 											if(/*1*/ /*!allowEmptyDays ||*/ sbgDayNHours[d2]>0 || (!allowEmptyMornings && (d2%2)==0)){
16255 												if(sbgDayNHours[d2]<subgroupsMinHoursDailyMinHours[sbg][d2%2]){
16256 													remGDay-=subgroupsMinHoursDailyMinHours[sbg][d2%2]-sbgDayNHours[d2];
16257 													totalH+=subgroupsMinHoursDailyMinHours[sbg][d2%2];
16258 												}
16259 												else
16260 													totalH+=sbgDayNHours[d2];
16261 											}
16262 											if(remGDay>0)
16263 												remG+=remGDay;
16264 
16265 											if(sbgDayNHours[d2]>0){
16266 												if(d2%2==0)
16267 													nUsedMornings++;
16268 												else
16269 													nUsedAfternoons++;
16270 											}
16271 										}
16272 
16273 										if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
16274 											if(subgroupsMinMorningsPerWeekMinMornings[sbg]>nUsedMornings)
16275 												totalH+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-nUsedMornings)*mhm;
16276 
16277 										if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
16278 											if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>nUsedAfternoons)
16279 												totalH+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-nUsedAfternoons)*mhd;
16280 
16281 										if((remG+totalH <= nHoursPerSubgroup[sbg]
16282 										  +subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
16283 										  && (totalH <= nHoursPerSubgroup[sbg]))
16284 											;//ok=true;
16285 										else
16286 											ok=false;
16287 									}
16288 									if(!allowEmptyDays){
16289 										//real days
16290 										int remG=0, totalH=0;
16291 										for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
16292 											int remGDay=sbgDayNFirstGaps[2*d2]+sbgDayNFirstGaps[2*d2+1]+sbgDayNGaps[2*d2]+sbgDayNGaps[2*d2+1];
16293 											if(/*1*/ !allowEmptyDays || sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1]>0){
16294 												if(sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1]<mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/){
16295 													remGDay-=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/-sbgDayNHours[2*d2]-sbgDayNHours[2*d2+1];
16296 													totalH+=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/;
16297 												}
16298 												else
16299 													totalH+=sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1];
16300 											}
16301 											if(remGDay>0)
16302 												remG+=remGDay;
16303 										}
16304 										if((remG+totalH <= nHoursPerSubgroup[sbg]
16305 										  +subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
16306 										  && (totalH <= nHoursPerSubgroup[sbg]))
16307 											;//ok=true;
16308 										else
16309 											ok=false;
16310 									}
16311 								}
16312 								else{
16313 									//only first gaps limitation
16314 									//if(allowEmptyDays){
16315 									if(1){
16316 										int remG=0, totalH=0;
16317 
16318 										int nUsedMornings=0;
16319 										int nUsedAfternoons=0;
16320 
16321 										for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16322 											int remGDay=0;
16323 											if(/*1*/ /*!allowEmptyDays ||*/ sbgDayNHours[d2]>0 || (!allowEmptyMornings && (d2%2)==0)){
16324 												if(sbgDayNHours[d2]<subgroupsMinHoursDailyMinHours[sbg][d2%2]){
16325 													remGDay=0;
16326 													totalH+=subgroupsMinHoursDailyMinHours[sbg][d2%2];
16327 												}
16328 												else{
16329 													totalH+=sbgDayNHours[d2];
16330 													if(sbgDayNFirstGaps[d2]==0)
16331 														remGDay=0;
16332 													else if(sbgDayNFirstGaps[d2]==1)
16333 														remGDay=1;
16334 													else if(sbgDayNFirstGaps[d2]>=2){
16335 														remGDay=0;
16336 														totalH++;
16337 													}
16338 												}
16339 											}
16340 											if(remGDay>0)
16341 												remG+=remGDay;
16342 
16343 											if(sbgDayNHours[d2]>0){
16344 												if(d2%2==0)
16345 													nUsedMornings++;
16346 												else
16347 													nUsedAfternoons++;
16348 											}
16349 										}
16350 
16351 										if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
16352 											if(subgroupsMinMorningsPerWeekMinMornings[sbg]>nUsedMornings)
16353 												totalH+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-nUsedMornings)*mhm;
16354 
16355 										if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
16356 											if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>nUsedAfternoons)
16357 												totalH+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-nUsedAfternoons)*mhd;
16358 
16359 										if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg])
16360 										  && (totalH <= nHoursPerSubgroup[sbg]))
16361 											;//ok=true;
16362 										else
16363 											ok=false;
16364 									}
16365 									if(!allowEmptyDays){
16366 										//real days
16367 										int remG=0, totalH=0;
16368 										for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
16369 											int remGDay=0;
16370 											if(/*1*/ !allowEmptyDays || sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1]>0){
16371 												if(sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1]<mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/){
16372 													remGDay=0;
16373 													totalH+=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/;
16374 												}
16375 												else{
16376 													totalH+=sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1];
16377 													remGDay=0;
16378 													if(sbgDayNFirstGaps[2*d2]==0)
16379 														remGDay+=0;
16380 													else if(sbgDayNFirstGaps[2*d2]==1)
16381 														remGDay+=1;
16382 													else if(sbgDayNFirstGaps[2*d2]>=2){
16383 														remGDay+=0;
16384 														totalH++;
16385 													}
16386 
16387 													if(sbgDayNFirstGaps[2*d2+1]==0)
16388 														remGDay+=0;
16389 													else if(sbgDayNFirstGaps[2*d2+1]==1)
16390 														remGDay+=1;
16391 													else if(sbgDayNFirstGaps[2*d2+1]>=2){
16392 														remGDay+=0;
16393 														totalH++;
16394 													}
16395 												}
16396 											}
16397 											if(remGDay>0)
16398 												remG+=remGDay;
16399 										}
16400 										if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg])
16401 										  && (totalH <= nHoursPerSubgroup[sbg]))
16402 											;//ok=true;
16403 										else
16404 											ok=false;
16405 									}
16406 								}
16407 							}
16408 							else{
16409 								if(subgroupsMaxGapsPerWeekPercentage[sbg]>=0){
16410 									//only max gaps per week limitation
16411 									//if(allowEmptyDays){
16412 									if(1){
16413 										int remG=0, totalH=0;
16414 
16415 										int nUsedMornings=0;
16416 										int nUsedAfternoons=0;
16417 
16418 										for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16419 											int remGDay=sbgDayNGaps[d2];
16420 											if(/*1*/ /*!allowEmptyDays ||*/ sbgDayNHours[d2]>0 || (!allowEmptyMornings && (d2%2)==0)){
16421 											if(sbgDayNHours[d2]<subgroupsMinHoursDailyMinHours[sbg][d2%2]){
16422 													remGDay-=subgroupsMinHoursDailyMinHours[sbg][d2%2]-sbgDayNHours[d2];
16423 													totalH+=subgroupsMinHoursDailyMinHours[sbg][d2%2];
16424 												}
16425 												else
16426 													totalH+=sbgDayNHours[d2];
16427 											}
16428 											if(remGDay>0)
16429 												remG+=remGDay;
16430 
16431 											if(sbgDayNHours[d2]>0){
16432 												if(d2%2==0)
16433 													nUsedMornings++;
16434 												else
16435 													nUsedAfternoons++;
16436 											}
16437 										}
16438 
16439 										if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
16440 											if(subgroupsMinMorningsPerWeekMinMornings[sbg]>nUsedMornings)
16441 												totalH+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-nUsedMornings)*mhm;
16442 
16443 										if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
16444 											if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>nUsedAfternoons)
16445 												totalH+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-nUsedAfternoons)*mhd;
16446 
16447 										if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
16448 										  && (totalH <= nHoursPerSubgroup[sbg]))
16449 											;//ok=true;
16450 										else
16451 											ok=false;
16452 									}
16453 									if(!allowEmptyDays){
16454 										//real days
16455 										int remG=0, totalH=0;
16456 										for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
16457 											int remGDay=sbgDayNGaps[2*d2]+sbgDayNGaps[2*d2+1];
16458 											if(/*1*/ !allowEmptyDays || sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1]>0){
16459 											if(sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1]<mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/){
16460 													remGDay-=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/-sbgDayNHours[2*d2]-sbgDayNHours[2*d2+1];
16461 													totalH+=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/;
16462 												}
16463 												else
16464 													totalH+=sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1];
16465 											}
16466 											if(remGDay>0)
16467 												remG+=remGDay;
16468 										}
16469 										if((remG+totalH <= nHoursPerSubgroup[sbg]+subgroupsMaxGapsPerWeekMaxGaps[sbg])
16470 										  && (totalH <= nHoursPerSubgroup[sbg]))
16471 											;//ok=true;
16472 										else
16473 											ok=false;
16474 									}
16475 								}
16476 								else{
16477 									//no limitation
16478 									//if(allowEmptyDays){
16479 									if(1){
16480 										int totalH=0;
16481 
16482 										int nUsedMornings=0;
16483 										int nUsedAfternoons=0;
16484 
16485 										for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16486 											if(/*1*/ /*!allowEmptyDays ||*/ sbgDayNHours[d2]>0 || (!allowEmptyMornings && (d2%2)==0)){
16487 												if(sbgDayNHours[d2]<subgroupsMinHoursDailyMinHours[sbg][d2%2])
16488 													totalH+=subgroupsMinHoursDailyMinHours[sbg][d2%2];
16489 												else
16490 													totalH+=sbgDayNHours[d2];
16491 											}
16492 
16493 											if(sbgDayNHours[d2]>0){
16494 												if(d2%2==0)
16495 													nUsedMornings++;
16496 												else
16497 													nUsedAfternoons++;
16498 											}
16499 										}
16500 										if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0)
16501 											if(subgroupsMinMorningsPerWeekMinMornings[sbg]>nUsedMornings)
16502 												totalH+=(subgroupsMinMorningsPerWeekMinMornings[sbg]-nUsedMornings)*mhm;
16503 
16504 										if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0)
16505 											if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>nUsedAfternoons)
16506 												totalH+=(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]-nUsedAfternoons)*mhd;
16507 
16508 										if(totalH <= nHoursPerSubgroup[sbg])
16509 											;//ok=true;
16510 										else
16511 											ok=false;
16512 									}
16513 									if(!allowEmptyDays){
16514 										//real days
16515 										int totalH=0;
16516 										for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
16517 											if(/*1*/ !allowEmptyDays || sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1]>0){
16518 												if(sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1]<mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/)
16519 													totalH+=mhd /*subgroupsMinHoursDailyMinHours[sbg][1]*/;
16520 												else
16521 													totalH+=sbgDayNHours[2*d2]+sbgDayNHours[2*d2+1];
16522 											}
16523 										}
16524 
16525 										if(totalH <= nHoursPerSubgroup[sbg])
16526 											;//ok=true;
16527 										else
16528 											ok=false;
16529 									}
16530 								}
16531 							}
16532 							////////////////////////////
16533 
16534 							if(ok)
16535 								break; //ok
16536 
16537 							int ai2=-1;
16538 
16539 							bool kk=subgroupRemoveAnActivityFromEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
16540 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
16541 							if(!kk){
16542 								bool k=subgroupRemoveAnActivityFromBegin(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
16543 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
16544 								if(!k){
16545 									bool ka=subgroupRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
16546 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
16547 
16548 									if(!ka){
16549 										if(level==0){
16550 											/*cout<<"d=="<<d<<", h=="<<h<<", ai=="<<ai<<endl;
16551 											for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
16552 												for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
16553 													cout<<"\t"<<sbgTimetable(d2,h2);
16554 												cout<<endl;
16555 											}*/
16556 
16557 											//this should not be displayed
16558 											//cout<<"WARNING - unlikely situation - file "<<__FILE__<<" line "<<__LINE__<<endl;
16559 										}
16560 										okstudentsminhoursdaily=false;
16561 										goto impossiblestudentsminhoursdaily;
16562 									}
16563 								}
16564 							}
16565 
16566 							assert(ai2>=0);
16567 
16568 							removeAi2FromSbgTimetable(ai2);
16569 							updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
16570 						}
16571 					}
16572 				}
16573 			}
16574 		}
16575 
16576 impossiblestudentsminhoursdaily:
16577 		if(!okstudentsminhoursdaily){
16578 			if(updateSubgroups || updateTeachers)
16579 				removeAiFromNewTimetable(ai, act, d, h);
16580 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
16581 
16582 			nConflActivities[newtime]=MAX_ACTIVITIES;
16583 			continue;
16584 		}
16585 
16586 		/////////end students(s) min hours daily
16587 
16588 /////////////////////////////////////////////////////////////////////////////////////////////
16589 
16590 		okstudentsminmorningsafternoonsperweek=true;
16591 
16592 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
16593 			for(int sbg : qAsConst(act->iSubgroupsList)){
16594 				if(subgroupsMinMorningsPerWeekMinMornings[sbg]>=0 || subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>=0){
16595 					//assert(teachersMinHoursDailyPercentages[tch]==100);
16596 					int mhd[2];
16597 					mhd[1]=1; //afternoon
16598 					mhd[0]=1; //morning, at least as large as for daily
16599 					if(subgroupsMinHoursDailyPercentages[sbg][1]==100){
16600 						assert(mhd[1]<subgroupsMinHoursDailyMinHours[sbg][1]);
16601 						mhd[1]=subgroupsMinHoursDailyMinHours[sbg][1];
16602 					}
16603 					if(subgroupsMinHoursDailyPercentages[sbg][0]==100){
16604 						assert(mhd[0]<subgroupsMinHoursDailyMinHours[sbg][0]);
16605 						mhd[0]=subgroupsMinHoursDailyMinHours[sbg][0];
16606 					}
16607 					//assert(teachersMaxGapsPerWeekMaxGaps[tch]==0 || teachersMaxGapsPerDayMaxGaps[tch]==0);
16608 
16609 					bool skip=false;
16610 					if(!skip){
16611 						//preliminary test
16612 						bool _ok;
16613 
16614 						int requested=0;
16615 						int filledMornings=0;
16616 						int filledAfternoons=0;
16617 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16618 							int p=/*newTeachersDayNGaps(tch, d2)+*/newSubgroupsDayNHours(sbg, d2);
16619 							if(subgroupsMaxGapsPerWeekMaxGaps[sbg]==0 || subgroupsMaxGapsPerDayMaxGaps[sbg]==0 ||
16620 							 subgroupsMaxGapsPerRealDayMaxGaps[sbg]==0 || subgroupsMaxGapsPerWeekForRealDaysMaxGaps[sbg]==0)
16621 								p+=newSubgroupsDayNGaps(sbg, d2);
16622 							if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0)
16623 								p+=newSubgroupsDayNFirstGaps(sbg, d2);
16624 							if(p>0 && p<mhd[d2%2])
16625 								p=mhd[d2%2];
16626 							requested+=p;
16627 							if(p>0){
16628 								if(d2%2==0)
16629 									filledMornings++;
16630 								else
16631 									filledAfternoons++;
16632 							}
16633 						}
16634 
16635 						if(subgroupsMinMorningsPerWeekMinMornings[sbg]>0){
16636 							if(filledMornings<subgroupsMinMorningsPerWeekMinMornings[sbg])
16637 								requested+=mhd[0]*(-filledMornings+subgroupsMinMorningsPerWeekMinMornings[sbg]);
16638 						}
16639 						if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>0){
16640 							if(filledAfternoons<subgroupsMinAfternoonsPerWeekMinAfternoons[sbg])
16641 								requested+=mhd[1]*(-filledAfternoons+subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]);
16642 						}
16643 
16644 						_ok=(requested<=nHoursPerSubgroup[sbg]);
16645 
16646 						if(_ok)
16647 							continue;
16648 
16649 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
16650 							okstudentsminmorningsafternoonsperweek=false;
16651 							goto impossiblestudentsminmorningsafternoonsperweek;
16652 						}
16653 
16654 						getSbgTimetable(sbg, conflActivities[newtime]);
16655 						sbgGetNHoursGaps(sbg);
16656 
16657 						for(;;){
16658 							bool ok;
16659 							int requested=0;
16660 							int filledMornings=0;
16661 							int filledAfternoons=0;
16662 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16663 								int p=/*newTeachersDayNGaps(tch, d2)+*/sbgDayNHours[d2];
16664 								if(subgroupsMaxGapsPerWeekMaxGaps[sbg]==0 || subgroupsMaxGapsPerDayMaxGaps[sbg]==0 ||
16665 								 subgroupsMaxGapsPerRealDayMaxGaps[sbg]==0 || subgroupsMaxGapsPerWeekForRealDaysMaxGaps[sbg]==0)
16666 									p+=sbgDayNGaps[d2];
16667 								if(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0)
16668 									p+=sbgDayNFirstGaps[d2];
16669 								if(p>0 && p<mhd[d2%2])
16670 									p=mhd[d2%2];
16671 								requested+=p;
16672 								if(p>0){
16673 									if(d2%2==0)
16674 										filledMornings++;
16675 									else
16676 										filledAfternoons++;
16677 								}
16678 							}
16679 							if(subgroupsMinMorningsPerWeekMinMornings[sbg]>0){
16680 								if(filledMornings<subgroupsMinMorningsPerWeekMinMornings[sbg])
16681 									requested+=mhd[0]*(-filledMornings+subgroupsMinMorningsPerWeekMinMornings[sbg]);
16682 							}
16683 							if(subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]>0){
16684 								if(filledAfternoons<subgroupsMinAfternoonsPerWeekMinAfternoons[sbg])
16685 									requested+=mhd[1]*(-filledAfternoons+subgroupsMinAfternoonsPerWeekMinAfternoons[sbg]);
16686 							}
16687 
16688 							ok=(requested<=nHoursPerSubgroup[sbg]);
16689 
16690 							if(ok)
16691 								break;
16692 
16693 							int ai2=-1;
16694 
16695 							if(!(subgroupsEarlyMaxBeginningsAtSecondHourMaxBeginnings[sbg]==0)){
16696 								bool k=subgroupRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
16697 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
16698 
16699 								if(!k){
16700 									bool k2=false;
16701 									if(!(subgroupsMaxGapsPerWeekMaxGaps[sbg]==0 || subgroupsMaxGapsPerDayMaxGaps[sbg]==0 ||
16702 									 subgroupsMaxGapsPerRealDayMaxGaps[sbg]==0 || subgroupsMaxGapsPerWeekForRealDaysMaxGaps[sbg]==0)){
16703 										k2=subgroupRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
16704 										assert(conflActivities[newtime].count()==nConflActivities[newtime]);
16705 									}
16706 
16707 									if(!k2){
16708 										if(level==0){
16709 											//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
16710 											//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
16711 										}
16712 										okstudentsminmorningsafternoonsperweek=false;
16713 										goto impossiblestudentsminmorningsafternoonsperweek;
16714 									}
16715 								}
16716 							}
16717 							else{
16718 								bool k=subgroupRemoveAnActivityFromEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
16719 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
16720 
16721 								if(!k){
16722 									bool k2=false;
16723 									if(!(subgroupsMaxGapsPerWeekMaxGaps[sbg]==0 || subgroupsMaxGapsPerDayMaxGaps[sbg]==0 ||
16724 									 subgroupsMaxGapsPerRealDayMaxGaps[sbg]==0 || subgroupsMaxGapsPerWeekForRealDaysMaxGaps[sbg]==0)){
16725 										k2=subgroupRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
16726 										assert(conflActivities[newtime].count()==nConflActivities[newtime]);
16727 									}
16728 
16729 									if(!k2){
16730 										if(level==0){
16731 											//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
16732 											//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
16733 										}
16734 										okstudentsminmorningsafternoonsperweek=false;
16735 										goto impossiblestudentsminmorningsafternoonsperweek;
16736 									}
16737 								}
16738 							}
16739 
16740 							assert(ai2>=0);
16741 
16742 							/*Activity* act2=&gt.rules.internalActivitiesList[ai2];
16743 							int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
16744 							int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
16745 
16746 							for(int dur2=0; dur2<act2->duration; dur2++){
16747 								assert(tchTimetable(d2,h2+dur2)==ai2);
16748 								tchTimetable(d2,h2+dur2)=-1;
16749 							}*/
16750 
16751 							removeAi2FromSbgTimetable(ai2);
16752 							updateSbgNHoursGaps(sbg, c.times[ai2]%gt.rules.nDaysPerWeek);
16753 						}
16754 					}
16755 				}
16756 			}
16757 		}
16758 
16759 impossiblestudentsminmorningsafternoonsperweek:
16760 		if(!okstudentsminmorningsafternoonsperweek){
16761 			if(updateSubgroups || updateTeachers)
16762 				removeAiFromNewTimetable(ai, act, d, h);
16763 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
16764 
16765 			nConflActivities[newtime]=MAX_ACTIVITIES;
16766 			continue;
16767 		}
16768 
16769 		/////////end students (set) min mornings or afternoons per week
16770 
16771 /////////////////////////////////////////////////////////////////////////////////////////////
16772 
16773 		//2020-06-28
16774 		//students max hours per all afternoons.
16775 
16776 		okstudentsmaxhoursperallafternoons=true;
16777 
16778 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
16779 			for(int sbg : qAsConst(act->iSubgroupsList)){
16780 				if(subgroupsMaxHoursPerAllAfternoonsMaxHours[sbg]>=0){
16781 					assert(subgroupsMaxHoursPerAllAfternoonsPercentages[sbg]==100.0);
16782 
16783 					if(d%2==1){ //afternoon
16784 						//preliminary
16785 						int _nOcc=0;
16786 
16787 						for(int d2=1; d2<gt.rules.nDaysPerWeek; d2+=2){ //afternoon
16788 							for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
16789 								int ai2=newSubgroupsTimetable(sbg,d2,h2);
16790 								if(ai2>=0)
16791 									_nOcc++;
16792 							}
16793 						}
16794 
16795 						if(_nOcc<=subgroupsMaxHoursPerAllAfternoonsMaxHours[sbg])
16796 							continue; //ok
16797 
16798 						getSbgTimetable(sbg, conflActivities[newtime]);
16799 
16800 						int nOccupied=0;
16801 
16802 						QSet<int> candidates;
16803 
16804 						//static int slotActivity[MAX_DAYS_PER_WEEK*MAX_HOURS_PER_DAY];
16805 
16806 						for(int d2=1; d2<gt.rules.nDaysPerWeek; d2+=2){ //afternoon
16807 							for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
16808 								int t=d2+gt.rules.nDaysPerWeek*h2;
16809 
16810 								int ai2=sbgTimetable(d2,h2);
16811 								slotActivity[t]=ai2;
16812 								if(ai2>=0){
16813 									nOccupied++;
16814 									if(ai2!=ai && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
16815 										candidates.insert(t);
16816 								}
16817 							}
16818 						}
16819 
16820 						if(nOccupied > subgroupsMaxHoursPerAllAfternoonsMaxHours[sbg]){
16821 							int target=nOccupied - subgroupsMaxHoursPerAllAfternoonsMaxHours[sbg];
16822 
16823 							while(target>0){
16824 								bool decreased=false;
16825 
16826 								if(candidates.count()==0){
16827 									okstudentsmaxhoursperallafternoons=false;
16828 									goto impossiblestudentsmaxhoursperallafternoons;
16829 								}
16830 
16831 								//To keep the generation identical on all computers
16832 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
16833 								QList<int> tmpSortedList=QList<int>(candidates.constBegin(), candidates.constEnd());
16834 #else
16835 								QList<int> tmpSortedList=candidates.toList();
16836 #endif
16837 								std::stable_sort(tmpSortedList.begin(), tmpSortedList.end());
16838 
16839 								int t=-1;
16840 								if(level>0){
16841 									assert(candidates.count()==tmpSortedList.count());
16842 									int q=rng.intMRG32k3a(candidates.count());
16843 									t=tmpSortedList.at(q);
16844 								}
16845 								else{
16846 									assert(level==0);
16847 
16848 									int optMinWrong=INF;
16849 									QList<int> tl;
16850 
16851 									for(int t2 : qAsConst(tmpSortedList)){
16852 										int ai3=slotActivity[t2];
16853 										if(optMinWrong>triedRemovals(ai3,c.times[ai3])){
16854 											optMinWrong=triedRemovals(ai3,c.times[ai3]);
16855 											tl.clear();
16856 											tl.append(t2);
16857 										}
16858 										else if(optMinWrong==triedRemovals(ai3,c.times[ai3])){
16859 											tl.append(t2);
16860 										}
16861 									}
16862 
16863 									assert(tl.count()>0);
16864 									int q=rng.intMRG32k3a(tl.count());
16865 									t=tl.at(q);
16866 								}
16867 
16868 								assert(t>=0);
16869 								int ai2=slotActivity[t];
16870 
16871 								assert(ai2>=0);
16872 								assert(ai2!=ai);
16873 								assert(c.times[ai2]!=UNALLOCATED_TIME);
16874 								assert(!fixedTimeActivity[ai2] && !swappedActivities[ai2]);
16875 
16876 								for(int tt=c.times[ai2]; tt<c.times[ai2]+gt.rules.internalActivitiesList[ai2].duration*gt.rules.nDaysPerWeek; tt+=gt.rules.nDaysPerWeek){
16877 									assert(slotActivity[tt]==ai2);
16878 									slotActivity[tt]=-1;
16879 									assert(candidates.contains(tt));
16880 									candidates.remove(tt);
16881 									target--;
16882 
16883 									decreased=true;
16884 								}
16885 
16886 								assert(!conflActivities[newtime].contains(ai2));
16887 								conflActivities[newtime].append(ai2);
16888 								nConflActivities[newtime]++;
16889 								assert(nConflActivities[newtime]==conflActivities[newtime].count());
16890 
16891 								removeAi2FromSbgTimetable(ai2); //not really needed
16892 
16893 								assert(decreased);
16894 							}
16895 						}
16896 					}
16897 				}
16898 			}
16899 		}
16900 
16901 impossiblestudentsmaxhoursperallafternoons:
16902 		if(!okstudentsmaxhoursperallafternoons){
16903 			if(updateSubgroups || updateTeachers)
16904 				removeAiFromNewTimetable(ai, act, d, h);
16905 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
16906 
16907 			nConflActivities[newtime]=MAX_ACTIVITIES;
16908 			continue;
16909 		}
16910 
16911 /////////////////////////////////////////////////////////////////////////////////////////////
16912 
16913 		//allowed from students activity tag min hours daily
16914 
16915 		//!!!NOT PERFECT, there is room for improvement
16916 
16917 		okstudentsactivitytagminhoursdaily=true;
16918 
16919 		if(haveStudentsActivityTagMinHoursDaily){
16920 			for(int sbg : qAsConst(act->iSubgroupsList)){
16921 				for(SubgroupActivityTagMinHoursDaily_item* item : qAsConst(satmhdListForSubgroup[sbg])){
16922 					if(!gt.rules.internalActivitiesList[ai].iActivityTagsSet.contains(item->activityTag))
16923 						continue;
16924 
16925 					//int sbgDayNHoursWithTag[MAX_DAYS_PER_WEEK];
16926 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
16927 						sbgDayNHoursWithTag[d2]=0;
16928 
16929 					//bool possibleToEmptyDay[MAX_DAYS_PER_WEEK];
16930 
16931 					//code similar to getSbgTimetable
16932 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16933 						possibleToEmptyDay[d2]=true;
16934 						for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
16935 							int ai2=newSubgroupsTimetable(sbg,d2,h2);
16936 							if(ai2>=0 && !conflActivities[newtime].contains(ai2)){
16937 								if(gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(item->activityTag)){
16938 									sbgTimetable(d2,h2)=ai2;
16939 									sbgDayNHoursWithTag[d2]++;
16940 
16941 									if(item->allowEmptyDays && (ai2==ai || fixedTimeActivity[ai2] || swappedActivities[ai2]))
16942 										possibleToEmptyDay[d2]=false;
16943 								}
16944 								else{
16945 									sbgTimetable(d2,h2)=-1;
16946 								}
16947 							}
16948 							else{
16949 								sbgTimetable(d2,h2)=-1;
16950 							}
16951 						}
16952 					}
16953 
16954 					int necessary=0;
16955 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16956 						if(sbgDayNHoursWithTag[d2]>0 || (sbgDayNHoursWithTag[d2]==0 && !item->allowEmptyDays)){
16957 							necessary+=max(item->minHoursDaily, sbgDayNHoursWithTag[d2]);
16958 						}
16959 					}
16960 
16961 					if(necessary > item->durationOfActivitiesWithActivityTagForSubgroup){
16962 						//not OK
16963 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
16964 							okstudentsactivitytagminhoursdaily=false;
16965 							goto impossiblestudentsactivitytagminhoursdaily;
16966 						}
16967 
16968 						QSet<int> candidatesSet;
16969 
16970 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
16971 							if(sbgDayNHoursWithTag[d2]>0){
16972 								if((item->allowEmptyDays && possibleToEmptyDay[d2]) || sbgDayNHoursWithTag[d2] > item->minHoursDaily){
16973 									for(int h2=0; h2<gt.rules.nHoursPerDay; ){
16974 										int ai2=sbgTimetable(d2,h2);
16975 										if(ai2>=0){
16976 											if(ai2!=ai && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
16977 												candidatesSet.insert(ai2);
16978 											h2+=gt.rules.internalActivitiesList[ai2].duration;
16979 										}
16980 										else{
16981 											h2++;
16982 										}
16983 									}
16984 								}
16985 							}
16986 						}
16987 
16988 						for(;;){
16989 							if(candidatesSet.count()==0){
16990 								okstudentsactivitytagminhoursdaily=false;
16991 								goto impossiblestudentsactivitytagminhoursdaily;
16992 							}
16993 							else{
16994 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
16995 								QList<int> candidatesList(candidatesSet.constBegin(), candidatesSet.constEnd());
16996 #else
16997 								QList<int> candidatesList=candidatesSet.toList();
16998 #endif
16999 								std::stable_sort(candidatesList.begin(), candidatesList.end()); //To keep the generation identical on all computers.
17000 
17001 								int ai2;
17002 								if(level>0){
17003 									int q=rng.intMRG32k3a(candidatesList.count());
17004 									ai2=candidatesList.at(q);
17005 								}
17006 								else{
17007 									assert(level==0);
17008 
17009 									int optMinWrong=INF;
17010 
17011 									QList<int> tl;
17012 
17013 									for(int ai3 : qAsConst(candidatesList)){
17014 										if(optMinWrong>triedRemovals(ai3,c.times[ai3])){
17015 										 	optMinWrong=triedRemovals(ai3,c.times[ai3]);
17016 										 	tl.clear();
17017 										 	tl.append(ai3);
17018 										}
17019 										else if(optMinWrong==triedRemovals(ai3,c.times[ai3])){
17020 										 	tl.append(ai3);
17021 										}
17022 									}
17023 
17024 									assert(tl.count()>0);
17025 									int q=rng.intMRG32k3a(tl.count());
17026 									ai2=tl.at(q);
17027 								}
17028 
17029 								assert(ai2!=ai);
17030 								assert(!fixedTimeActivity[ai2] && !swappedActivities[ai2]);
17031 
17032 								assert(!conflActivities[newtime].contains(ai2));
17033 								conflActivities[newtime].append(ai2);
17034 
17035 								nConflActivities[newtime]++;
17036 								assert(nConflActivities[newtime]==conflActivities[newtime].count());
17037 
17038 								int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
17039 								int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
17040 								int dur2=gt.rules.internalActivitiesList[ai2].duration;
17041 								sbgDayNHoursWithTag[d2]-=dur2;
17042 								assert(sbgDayNHoursWithTag[d2]>=0);
17043 
17044 								if(sbgDayNHoursWithTag[d2]==0 && item->allowEmptyDays){
17045 									necessary-=max(item->minHoursDaily, dur2);
17046 									assert(necessary>=0);
17047 								}
17048 								else{
17049 									int oldNecessary=necessary;
17050 									necessary-=max(item->minHoursDaily, sbgDayNHoursWithTag[d2]+dur2);
17051 									assert(necessary>=0);
17052 									necessary+=max(item->minHoursDaily, sbgDayNHoursWithTag[d2]);
17053 									if(!item->allowEmptyDays)
17054 										assert(oldNecessary>necessary);
17055 								}
17056 
17057 								for(int h3=h2; h3<h2+dur2; h3++){
17058 									assert(sbgTimetable(d2,h3)==ai2);
17059 									sbgTimetable(d2,h3)=-1;
17060 								}
17061 
17062 								if(necessary <= item->durationOfActivitiesWithActivityTagForSubgroup)
17063 									break; //OK
17064 
17065 								bool tr=candidatesSet.remove(ai2);
17066 								assert(tr);
17067 
17068 								if(sbgDayNHoursWithTag[d2]>0 && sbgDayNHoursWithTag[d2]<=item->minHoursDaily &&
17069 								 !(item->allowEmptyDays && possibleToEmptyDay[d2])){
17070 									for(int h3=0; h3<gt.rules.nHoursPerDay; ){
17071 										int ai3=sbgTimetable(d2,h3);
17072 										if(ai3>=0){
17073 											if(ai3!=ai && ai3!=ai2 && !fixedTimeActivity[ai3] && !swappedActivities[ai3]){
17074 												assert(candidatesSet.contains(ai3));
17075 												candidatesSet.remove(ai3);
17076 											}
17077 											h3+=gt.rules.internalActivitiesList[ai3].duration;
17078 										}
17079 										else{
17080 											h3++;
17081 										}
17082 									}
17083 								}
17084 							}
17085 						}
17086 					}
17087 				}
17088 			}
17089 		}
17090 
17091 impossiblestudentsactivitytagminhoursdaily:
17092 		if(!okstudentsactivitytagminhoursdaily){
17093 			if(updateSubgroups || updateTeachers)
17094 				removeAiFromNewTimetable(ai, act, d, h);
17095 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
17096 
17097 			nConflActivities[newtime]=MAX_ACTIVITIES;
17098 			continue;
17099 		}
17100 
17101 /////////////////////////////////////////////////////////////////////////////////////////////
17102 
17103 		//allowed from students min gaps between ordered pair of activity tags
17104 
17105 		okstudentsmingapsbetweenorderedpairofactivitytags=true;
17106 
17107 		if(1){
17108 			for(StudentsMinGapsBetweenOrderedPairOfActivityTags_item* item : qAsConst(smgbopoatListForActivity[ai])){
17109 				bool first, second;
17110 				if(act->iActivityTagsSet.contains(item->firstActivityTag))
17111 					first=true;
17112 				else
17113 					first=false;
17114 
17115 				if(act->iActivityTagsSet.contains(item->secondActivityTag))
17116 					second=true;
17117 				else
17118 					second=false;
17119 
17120 				assert((first && !second) || (!first && second));
17121 
17122 				if(first){
17123 					assert(!second);
17124 					//after the first activity tag we need to have at least minGaps until the second activity tag.
17125 					for(int sbg : qAsConst(act->iSubgroupsList)){
17126 						if(item->canonicalSetOfSubgroups.contains(sbg)){
17127 							for(int startSecond=h+act->duration; startSecond<gt.rules.nHoursPerDay; startSecond++){
17128 								if(startSecond-h-act->duration >= item->minGaps)
17129 									break;
17130 								int ai2=subgroupsTimetable(sbg,d,startSecond);
17131 								if(ai2>=0){
17132 									if(gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(item->secondActivityTag)){
17133 										if(!conflActivities[newtime].contains(ai2)){
17134 											if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
17135 												okstudentsmingapsbetweenorderedpairofactivitytags=false;
17136 												goto impossiblestudentsmingapsbetweenorderedpairofactivitytags;
17137 											}
17138 											else{
17139 												conflActivities[newtime].append(ai2);
17140 												nConflActivities[newtime]++;
17141 												assert(conflActivities[newtime].count()==nConflActivities[newtime]);
17142 											}
17143 										}
17144 									}
17145 								}
17146 							}
17147 						}
17148 					}
17149 				}
17150 				else{
17151 					assert(second);
17152 					//before the second activity tag we need to have at least minGaps until the first activity tag.
17153 					for(int sbg : qAsConst(act->iSubgroupsList)){
17154 						if(item->canonicalSetOfSubgroups.contains(sbg)){
17155 							for(int endFirst=h-1; endFirst>=0; endFirst--){
17156 								if(h-1-endFirst >= item->minGaps)
17157 									break;
17158 								int ai2=subgroupsTimetable(sbg,d,endFirst);
17159 								if(ai2>=0){
17160 									if(gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(item->firstActivityTag)){
17161 										if(!conflActivities[newtime].contains(ai2)){
17162 											if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
17163 												okstudentsmingapsbetweenorderedpairofactivitytags=false;
17164 												goto impossiblestudentsmingapsbetweenorderedpairofactivitytags;
17165 											}
17166 											else{
17167 												conflActivities[newtime].append(ai2);
17168 												nConflActivities[newtime]++;
17169 												assert(conflActivities[newtime].count()==nConflActivities[newtime]);
17170 											}
17171 										}
17172 									}
17173 								}
17174 							}
17175 						}
17176 					}
17177 				}
17178 			}
17179 		}
17180 
17181 impossiblestudentsmingapsbetweenorderedpairofactivitytags:
17182 		if(!okstudentsmingapsbetweenorderedpairofactivitytags){
17183 			if(updateSubgroups || updateTeachers)
17184 				removeAiFromNewTimetable(ai, act, d, h);
17185 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
17186 
17187 			nConflActivities[newtime]=MAX_ACTIVITIES;
17188 			continue;
17189 		}
17190 
17191 /////////////////////////////////////////////////////////////////////////////////////////////
17192 
17193 
17194 				////////////TEACHERS////////////////
17195 
17196 /////////////////////////////////////////////////////////////////////////////////////////////
17197 
17198 		//not breaking the teachers max days per week constraints
17199 		////////////////////////////BEGIN max days per week for teachers
17200 		okteachersmaxdaysperweek=true;
17201 		for(int tch : qAsConst(teachersWithMaxDaysPerWeekForActivities[ai])){
17202 			if(skipRandom(teachersMaxDaysPerWeekWeightPercentages[tch]))
17203 				continue;
17204 
17205 			int maxDays=teachersMaxDaysPerWeekMaxDays[tch];
17206 			assert(maxDays>=0); //the list contains real information
17207 
17208 			//preliminary test
17209 			int _nOc=0;
17210 			for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
17211 				//if(newTeachersDayNHours(tch,d2)>0)
17212 
17213 				//IT IS VITAL TO USE teacherActivitiesOfTheDay as a QList<int> (tch,d2)!!!!!!!
17214 				//The order of evaluation of activities is changed,
17215 				//with activities which were moved forward and back again
17216 				//being put at the end.
17217 				//If you do not follow this, you'll get impossible timetables
17218 				//for the Italian example Italy/2007/difficult/highschool-Ancona.fet or the examples from
17219 				//South Africa: South-Africa/difficult/Collegiate_Junior_School2.fet or
17220 				//South-Africa/difficult/Insight_Learning_Centre2.fet, I am not sure which of these 3
17221 
17222 				if(teacherActivitiesOfTheDay(tch,d2).count()>0 || d2==d)
17223 					_nOc++;
17224 			if(_nOc<=maxDays)
17225 				continue; //OK, preliminary
17226 
17227 			if(maxDays>=0){
17228 				assert(maxDays>0);
17229 
17230 				if(level>0){
17231 					///getTchTimetable(tch, conflActivities[newtime]);
17232 					///tchGetNHoursGaps(tch);
17233 
17234 					//bool occupiedDay[MAX_DAYS_PER_WEEK];
17235 					//bool canEmptyDay[MAX_DAYS_PER_WEEK];
17236 
17237 					///int _minWrong[MAX_DAYS_PER_WEEK];
17238 					///int _nWrong[MAX_DAYS_PER_WEEK];
17239 					//int _nConflActivities[MAX_DAYS_PER_WEEK];
17240 					///int _minIndexAct[MAX_DAYS_PER_WEEK];
17241 
17242 					//QList<int> _activitiesForDay[MAX_DAYS_PER_WEEK];
17243 
17244 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
17245 						if(d2==d)
17246 							continue;
17247 
17248 						occupiedDay[d2]=false;
17249 						canEmptyDay[d2]=true;
17250 
17251 						//_minWrong[d2]=INF;
17252 						//_nWrong[d2]=0;
17253 						_nConflActivities[d2]=0;
17254 						//_minIndexAct[d2]=gt.rules.nInternalActivities;
17255 						_activitiesForDay[d2].clear();
17256 
17257 						for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
17258 							if(ai2>=0){
17259 								if(!conflActivities[newtime].contains(ai2)){
17260 									occupiedDay[d2]=true;
17261 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
17262 										canEmptyDay[d2]=false;
17263 									else if(!_activitiesForDay[d2].contains(ai2)){
17264 										//_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
17265 										//_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
17266 										//_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
17267 										_nConflActivities[d2]++;
17268 										_activitiesForDay[d2].append(ai2);
17269 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
17270 									}
17271 								}
17272 							}
17273 						}
17274 
17275 						if(!occupiedDay[d2])
17276 							canEmptyDay[d2]=false;
17277 					}
17278 					occupiedDay[d]=true;
17279 					canEmptyDay[d]=false;
17280 
17281 					int nOc=0;
17282 					bool canChooseDay=false;
17283 
17284 					for(int j=0; j<gt.rules.nDaysPerWeek; j++)
17285 						if(occupiedDay[j]){
17286 							nOc++;
17287 							if(canEmptyDay[j]){
17288 								canChooseDay=true;
17289 							}
17290 						}
17291 
17292 					if(nOc>maxDays){
17293 						assert(nOc==maxDays+1);
17294 
17295 						if(!canChooseDay){
17296 							if(level==0){
17297 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
17298 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
17299 							}
17300 							okteachersmaxdaysperweek=false;
17301 							goto impossibleteachersmaxdaysperweek;
17302 						}
17303 
17304 						int d2=-1;
17305 
17306 						////////////////
17307 						//choose a random day from those with minimum number of conflicting activities
17308 						QList<int> candidateDays;
17309 
17310 						int m=gt.rules.nInternalActivities;
17311 
17312 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
17313 							if(canEmptyDay[kk])
17314 								if(m>_nConflActivities[kk])
17315 									m=_nConflActivities[kk];
17316 
17317 						candidateDays.clear();
17318 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
17319 							if(canEmptyDay[kk])
17320 								if(m==_nConflActivities[kk])
17321 									candidateDays.append(kk);
17322 
17323 						assert(candidateDays.count()>0);
17324 						d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
17325 						/////////////////
17326 
17327 						assert(d2>=0);
17328 
17329 						assert(_activitiesForDay[d2].count()>0);
17330 
17331 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
17332 							assert(ai2!=ai);
17333 							assert(!swappedActivities[ai2]);
17334 							assert(!fixedTimeActivity[ai2]);
17335 							assert(!conflActivities[newtime].contains(ai2));
17336 							conflActivities[newtime].append(ai2);
17337 							nConflActivities[newtime]++;
17338 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
17339 							//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
17340 						}
17341 					}
17342 				}
17343 				else{
17344 					assert(level==0);
17345 
17346 					///getTchTimetable(tch, conflActivities[newtime]);
17347 					///tchGetNHoursGaps(tch);
17348 
17349 					//bool occupiedDay[MAX_DAYS_PER_WEEK];
17350 					//bool canEmptyDay[MAX_DAYS_PER_WEEK];
17351 
17352 					//int _minWrong[MAX_DAYS_PER_WEEK];
17353 					//int _nWrong[MAX_DAYS_PER_WEEK];
17354 					//int _nConflActivities[MAX_DAYS_PER_WEEK];
17355 					//int _minIndexAct[MAX_DAYS_PER_WEEK];
17356 
17357 					//QList<int> _activitiesForDay[MAX_DAYS_PER_WEEK];
17358 
17359 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
17360 						if(d2==d)
17361 							continue;
17362 
17363 						occupiedDay[d2]=false;
17364 						canEmptyDay[d2]=true;
17365 
17366 						_minWrong[d2]=INF;
17367 						_nWrong[d2]=0;
17368 						_nConflActivities[d2]=0;
17369 						_minIndexAct[d2]=gt.rules.nInternalActivities;
17370 						_activitiesForDay[d2].clear();
17371 
17372 						for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
17373 							if(ai2>=0){
17374 								if(!conflActivities[newtime].contains(ai2)){
17375 									occupiedDay[d2]=true;
17376 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
17377 										canEmptyDay[d2]=false;
17378 									else if(!_activitiesForDay[d2].contains(ai2)){
17379 										_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
17380 										_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
17381 										_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
17382 										_nConflActivities[d2]++;
17383 										_activitiesForDay[d2].append(ai2);
17384 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
17385 									}
17386 								}
17387 							}
17388 						}
17389 
17390 						if(!occupiedDay[d2])
17391 							canEmptyDay[d2]=false;
17392 					}
17393 					occupiedDay[d]=true;
17394 					canEmptyDay[d]=false;
17395 
17396 					int nOc=0;
17397 					bool canChooseDay=false;
17398 
17399 					for(int j=0; j<gt.rules.nDaysPerWeek; j++)
17400 						if(occupiedDay[j]){
17401 							nOc++;
17402 							if(canEmptyDay[j]){
17403 								canChooseDay=true;
17404 							}
17405 						}
17406 
17407 					if(nOc>maxDays){
17408 						assert(nOc==maxDays+1);
17409 
17410 						if(!canChooseDay){
17411 							if(level==0){
17412 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
17413 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
17414 							}
17415 							okteachersmaxdaysperweek=false;
17416 							goto impossibleteachersmaxdaysperweek;
17417 						}
17418 
17419 						int d2=-1;
17420 
17421 						///////////////////
17422 						QList<int> candidateDays;
17423 
17424 						int _mW=INF;
17425 						int _nW=INF;
17426 						int _mCA=gt.rules.nInternalActivities;
17427 						int _mIA=gt.rules.nInternalActivities;
17428 
17429 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
17430 							if(canEmptyDay[kk]){
17431 								if(_mW>_minWrong[kk] ||
17432 								(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
17433 								(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
17434 								(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
17435 									_mW=_minWrong[kk];
17436 									_nW=_nWrong[kk];
17437 									_mCA=_nConflActivities[kk];
17438 									_mIA=_minIndexAct[kk];
17439 								}
17440 							}
17441 
17442 						assert(_mW<INF);
17443 
17444 						candidateDays.clear();
17445 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
17446 							if(canEmptyDay[kk])
17447 								if(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
17448 									candidateDays.append(kk);
17449 
17450 						assert(candidateDays.count()>0);
17451 						d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
17452 						//////////////////
17453 
17454 						assert(d2>=0);
17455 
17456 						assert(_activitiesForDay[d2].count()>0);
17457 
17458 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
17459 							assert(ai2!=ai);
17460 							assert(!swappedActivities[ai2]);
17461 							assert(!fixedTimeActivity[ai2]);
17462 							assert(!conflActivities[newtime].contains(ai2));
17463 							conflActivities[newtime].append(ai2);
17464 							nConflActivities[newtime]++;
17465 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
17466 							//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
17467 						}
17468 					}
17469 				}
17470 			}
17471 		}
17472 impossibleteachersmaxdaysperweek:
17473 		if(!okteachersmaxdaysperweek){
17474 			if(updateSubgroups || updateTeachers)
17475 				removeAiFromNewTimetable(ai, act, d, h);
17476 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
17477 
17478 			nConflActivities[newtime]=MAX_ACTIVITIES;
17479 			continue;
17480 		}
17481 
17482 		////////////////////////////END teachers max days per week
17483 
17484 /////////////////////////////////////////////////////////////////////////////////////////////
17485 
17486 		//not breaking the teachers max three consecutive days
17487 		////////////////////////////BEGIN max three consecutive days for teachers
17488 		okteachersmaxthreeconsecutivedays=true;
17489 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
17490 			for(int tch : qAsConst(teachersWithMaxThreeConsecutiveDaysForActivities[ai])){
17491 				if(!skipRandom(teachersMaxThreeConsecutiveDaysPercentages[tch])){
17492 					int maxDays=3;
17493 					bool allowExceptionAMAM=teachersMaxThreeConsecutiveDaysAllowAMAMException[tch];
17494 
17495 					//preliminary test
17496 					int _nOc=1;
17497 					int _dstart=d, _dend=d;
17498 					for(int d2=d-1; d2>=0; d2--){
17499 						if(teacherActivitiesOfTheDay(tch,d2).count()>0){
17500 							_nOc++;
17501 							_dstart=d2;
17502 						}
17503 						else{
17504 							break;
17505 						}
17506 					}
17507 					for(int d2=d+1; d2<gt.rules.nDaysPerWeek; d2++){
17508 						if(teacherActivitiesOfTheDay(tch,d2).count()>0){
17509 							_nOc++;
17510 							_dend=d2;
17511 						}
17512 						else{
17513 							break;
17514 						}
17515 					}
17516 
17517 					assert(_dstart>=0);
17518 					assert(_dend>=0);
17519 
17520 					if(_nOc<=maxDays || (allowExceptionAMAM && _dend-_dstart==maxDays && _dstart%2==1 && _dend%2==0))
17521 						continue; //OK, preliminary
17522 
17523 					if(level>0){
17524 						occupiedDay[d]=true;
17525 						canEmptyDay[d]=false;
17526 
17527 						for(int d2=d-1; d2>=0; d2--){
17528 							occupiedDay[d2]=false;
17529 							canEmptyDay[d2]=true;
17530 
17531 							_nConflActivities[d2]=0;
17532 							_activitiesForDay[d2].clear();
17533 
17534 							if(teacherActivitiesOfTheDay(tch,d2).count()>0){
17535 								for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
17536 									if(ai2>=0){
17537 										if(!conflActivities[newtime].contains(ai2)){
17538 											occupiedDay[d2]=true;
17539 											if(fixedTimeActivity[ai2] || swappedActivities[ai2])
17540 												canEmptyDay[d2]=false;
17541 											else if(!_activitiesForDay[d2].contains(ai2)){
17542 												_nConflActivities[d2]++;
17543 												_activitiesForDay[d2].append(ai2);
17544 												assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
17545 											}
17546 										}
17547 									}
17548 								}
17549 							}
17550 
17551 							if(!occupiedDay[d2])
17552 								break;
17553 						}
17554 
17555 						for(int d2=d+1; d2<gt.rules.nDaysPerWeek; d2++){
17556 							occupiedDay[d2]=false;
17557 							canEmptyDay[d2]=true;
17558 
17559 							_nConflActivities[d2]=0;
17560 							_activitiesForDay[d2].clear();
17561 
17562 							if(teacherActivitiesOfTheDay(tch,d2).count()>0){
17563 								for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
17564 									if(ai2>=0){
17565 										if(!conflActivities[newtime].contains(ai2)){
17566 											occupiedDay[d2]=true;
17567 											if(fixedTimeActivity[ai2] || swappedActivities[ai2])
17568 												canEmptyDay[d2]=false;
17569 											else if(!_activitiesForDay[d2].contains(ai2)){
17570 												_nConflActivities[d2]++;
17571 												_activitiesForDay[d2].append(ai2);
17572 												assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
17573 											}
17574 										}
17575 									}
17576 								}
17577 							}
17578 
17579 							if(!occupiedDay[d2])
17580 								break;
17581 						}
17582 
17583 						for(;;){
17584 							int dstart=d, dend=d;
17585 							int nOc=1;
17586 
17587 							for(int d2=d-1; d2>=0; d2--){
17588 								if(occupiedDay[d2]){
17589 									nOc++;
17590 									dstart=d2;
17591 								}
17592 								else{
17593 									break;
17594 								}
17595 							}
17596 							for(int d2=d+1; d2<gt.rules.nDaysPerWeek; d2++){
17597 								if(occupiedDay[d2]){
17598 									nOc++;
17599 									dend=d2;
17600 								}
17601 								else{
17602 									break;
17603 								}
17604 							}
17605 
17606 							if(nOc<=maxDays || (allowExceptionAMAM && dend-dstart==maxDays && dstart%2==1 && dend%2==0))
17607 								break;
17608 
17609 							bool canChooseDay=false;
17610 
17611 							for(int j=dstart; j<=dend; j++)
17612 								if(occupiedDay[j]){
17613 									if(canEmptyDay[j]){
17614 										canChooseDay=true;
17615 									}
17616 								}
17617 
17618 							if(!canChooseDay){
17619 								if(level==0){
17620 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
17621 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
17622 								}
17623 								okteachersmaxthreeconsecutivedays=false;
17624 								goto impossibleteachersmaxthreeconsecutivedays;
17625 							}
17626 
17627 							int d2=-1;
17628 
17629 							////////////////
17630 							//choose a random day from those with minimum number of conflicting activities
17631 							QList<int> candidateDays;
17632 
17633 							int m=gt.rules.nInternalActivities;
17634 
17635 							for(int kk=dstart; kk<=dend; kk++)
17636 								if(occupiedDay[kk] && canEmptyDay[kk])
17637 									if(m>_nConflActivities[kk])
17638 										m=_nConflActivities[kk];
17639 
17640 							candidateDays.clear();
17641 							for(int kk=dstart; kk<=dend; kk++)
17642 								if(occupiedDay[kk] && canEmptyDay[kk])
17643 									if(m==_nConflActivities[kk])
17644 										candidateDays.append(kk);
17645 
17646 							assert(candidateDays.count()>0);
17647 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
17648 							/////////////////
17649 
17650 							assert(d2>=0);
17651 
17652 							assert(_activitiesForDay[d2].count()>0);
17653 
17654 							for(int ai2 : qAsConst(_activitiesForDay[d2])){
17655 								assert(ai2!=ai);
17656 								assert(!swappedActivities[ai2]);
17657 								assert(!fixedTimeActivity[ai2]);
17658 								assert(!conflActivities[newtime].contains(ai2));
17659 								conflActivities[newtime].append(ai2);
17660 								nConflActivities[newtime]++;
17661 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
17662 							}
17663 
17664 							occupiedDay[d2]=false;
17665 							canEmptyDay[d2]=true;
17666 						}
17667 					}
17668 					else{
17669 						assert(level==0);
17670 
17671 						occupiedDay[d]=true;
17672 						canEmptyDay[d]=false;
17673 
17674 						for(int d2=d-1; d2>=0; d2--){
17675 							occupiedDay[d2]=false;
17676 							canEmptyDay[d2]=true;
17677 
17678 							_minWrong[d2]=INF;
17679 							_nWrong[d2]=0;
17680 							_nConflActivities[d2]=0;
17681 							_minIndexAct[d2]=gt.rules.nInternalActivities;
17682 							_activitiesForDay[d2].clear();
17683 
17684 							if(teacherActivitiesOfTheDay(tch,d2).count()>0){
17685 								for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
17686 									if(ai2>=0){
17687 										if(!conflActivities[newtime].contains(ai2)){
17688 											occupiedDay[d2]=true;
17689 											if(fixedTimeActivity[ai2] || swappedActivities[ai2])
17690 												canEmptyDay[d2]=false;
17691 											else if(!_activitiesForDay[d2].contains(ai2)){
17692 												_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
17693 												_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
17694 												_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
17695 												_nConflActivities[d2]++;
17696 												_activitiesForDay[d2].append(ai2);
17697 												assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
17698 											}
17699 										}
17700 									}
17701 								}
17702 							}
17703 
17704 							if(!occupiedDay[d2])
17705 								break;
17706 						}
17707 
17708 						for(int d2=d+1; d2<gt.rules.nDaysPerWeek; d2++){
17709 							occupiedDay[d2]=false;
17710 							canEmptyDay[d2]=true;
17711 
17712 							_minWrong[d2]=INF;
17713 							_nWrong[d2]=0;
17714 							_nConflActivities[d2]=0;
17715 							_minIndexAct[d2]=gt.rules.nInternalActivities;
17716 							_activitiesForDay[d2].clear();
17717 
17718 							if(teacherActivitiesOfTheDay(tch,d2).count()>0){
17719 								for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
17720 									if(ai2>=0){
17721 										if(!conflActivities[newtime].contains(ai2)){
17722 											occupiedDay[d2]=true;
17723 											if(fixedTimeActivity[ai2] || swappedActivities[ai2])
17724 												canEmptyDay[d2]=false;
17725 											else if(!_activitiesForDay[d2].contains(ai2)){
17726 												_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
17727 												_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
17728 												_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
17729 												_nConflActivities[d2]++;
17730 												_activitiesForDay[d2].append(ai2);
17731 												assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
17732 											}
17733 										}
17734 									}
17735 								}
17736 							}
17737 
17738 							if(!occupiedDay[d2])
17739 								break;
17740 						}
17741 
17742 						for(;;){
17743 							int dstart=d, dend=d;
17744 							int nOc=1;
17745 
17746 							for(int d2=d-1; d2>=0; d2--){
17747 								if(occupiedDay[d2]){
17748 									nOc++;
17749 									dstart=d2;
17750 								}
17751 								else{
17752 									break;
17753 								}
17754 							}
17755 							for(int d2=d+1; d2<gt.rules.nDaysPerWeek; d2++){
17756 								if(occupiedDay[d2]){
17757 									nOc++;
17758 									dend=d2;
17759 								}
17760 								else{
17761 									break;
17762 								}
17763 							}
17764 
17765 							if(nOc<=maxDays || (allowExceptionAMAM && dend-dstart==maxDays && dstart%2==1 && dend%2==0))
17766 								break;
17767 
17768 							bool canChooseDay=false;
17769 
17770 							for(int j=dstart; j<=dend; j++)
17771 								if(occupiedDay[j]){
17772 									if(canEmptyDay[j]){
17773 										canChooseDay=true;
17774 									}
17775 								}
17776 
17777 							if(!canChooseDay){
17778 								if(level==0){
17779 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
17780 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
17781 								}
17782 								okteachersmaxthreeconsecutivedays=false;
17783 								goto impossibleteachersmaxthreeconsecutivedays;
17784 							}
17785 
17786 							int d2=-1;
17787 
17788 							////////////////
17789 							//choose a random day from those with minimum number of conflicting activities
17790 							QList<int> candidateDays;
17791 
17792 							int _mW=INF;
17793 							int _nW=INF;
17794 							int _mCA=gt.rules.nInternalActivities;
17795 							int _mIA=gt.rules.nInternalActivities;
17796 
17797 							for(int kk=dstart; kk<=dend; kk++)
17798 								if(occupiedDay[kk] && canEmptyDay[kk])
17799 									if(_mW>_minWrong[kk] ||
17800 									 (_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
17801 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
17802 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
17803 										_mW=_minWrong[kk];
17804 										_nW=_nWrong[kk];
17805 										_mCA=_nConflActivities[kk];
17806 										_mIA=_minIndexAct[kk];
17807 									}
17808 
17809 							candidateDays.clear();
17810 							for(int kk=dstart; kk<=dend; kk++)
17811 								if(occupiedDay[kk] && canEmptyDay[kk])
17812 									if(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
17813 										candidateDays.append(kk);
17814 
17815 							assert(candidateDays.count()>0);
17816 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
17817 							/////////////////
17818 
17819 							assert(d2>=0);
17820 
17821 							assert(_activitiesForDay[d2].count()>0);
17822 
17823 							for(int ai2 : qAsConst(_activitiesForDay[d2])){
17824 								assert(ai2!=ai);
17825 								assert(!swappedActivities[ai2]);
17826 								assert(!fixedTimeActivity[ai2]);
17827 								assert(!conflActivities[newtime].contains(ai2));
17828 								conflActivities[newtime].append(ai2);
17829 								nConflActivities[newtime]++;
17830 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
17831 							}
17832 
17833 							occupiedDay[d2]=false;
17834 							canEmptyDay[d2]=true;
17835 						}
17836 					}
17837 				}
17838 			}
17839 		}
17840 impossibleteachersmaxthreeconsecutivedays:
17841 		if(!okteachersmaxthreeconsecutivedays){
17842 			if(updateSubgroups || updateTeachers)
17843 				removeAiFromNewTimetable(ai, act, d, h);
17844 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
17845 
17846 			nConflActivities[newtime]=MAX_ACTIVITIES;
17847 			continue;
17848 		}
17849 
17850 		////////////////////////////END teachers max three consecutive days
17851 
17852 /////////////////////////////////////////////////////////////////////////////////////////////
17853 
17854 		//not breaking the teacher max real days per week constraints
17855 		////////////////////////////BEGIN max real days per week for teachers
17856 		okteachermaxrealdaysperweek=true;
17857 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
17858 			//for(int tch : qAsConst(act->iTeachersList)){
17859 			for(int tch : qAsConst(teachersWithMaxRealDaysPerWeekForActivities[ai])){
17860 				if(skipRandom(teachersMaxRealDaysPerWeekWeightPercentages[tch]))
17861 					continue;
17862 
17863 				int maxDays=teachersMaxRealDaysPerWeekMaxDays[tch];
17864 				assert(maxDays>=0); //the list contains real information
17865 
17866 				//preliminary test
17867 				int _nOc=0;
17868 				for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
17869 					//if(newTeachersDayNHours(tch,d2)>0)
17870 
17871 					//IT IS VITAL TO USE teacherActivitiesOfTheDay as a QList<int> (tch,d2)!!!!!!!
17872 					//The order of evaluation of activities is changed,
17873 					//with activities which were moved forward and back again
17874 					//being put at the end.
17875 					//If you do not follow this, you'll get impossible timetables
17876 					//for the Italian example Italy/2007/difficult/highschool-Ancona.fet or the examples from
17877 					//South Africa: South-Africa/difficult/Collegiate_Junior_School2.fet or
17878 					//South-Africa/difficult/Insight_Learning_Centre2.fet, I am not sure which of these 3
17879 
17880 					if(teacherActivitiesOfTheDay(tch,2*d2).count()+teacherActivitiesOfTheDay(tch,2*d2+1).count()>0 || d2==d/2)
17881 						_nOc++;
17882 				if(_nOc<=maxDays)
17883 					continue; //OK, preliminary
17884 
17885 				if(maxDays>=0){
17886 					assert(maxDays>0);
17887 
17888 					//getTchTimetable(tch, conflActivities[newtime]);
17889 					//tchGetNHoursGaps(tch);
17890 
17891 					//bool occupiedDay[MAX_DAYS_PER_WEEK]; //should be MAX_DAYS_PER_WEEK/2, but doesn't matter
17892 					//bool canEmptyDay[MAX_DAYS_PER_WEEK];
17893 
17894 					//int _minWrong[MAX_DAYS_PER_WEEK];
17895 					//int _nWrong[MAX_DAYS_PER_WEEK];
17896 					//int _nConflActivities[MAX_DAYS_PER_WEEK];
17897 					//int _minIndexAct[MAX_DAYS_PER_WEEK];
17898 
17899 					//QList<int> _activitiesForDay[MAX_DAYS_PER_WEEK];
17900 
17901 					for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
17902 						if(d2==d/2)
17903 							continue;
17904 
17905 						occupiedDay[d2]=false;
17906 						canEmptyDay[d2]=true;
17907 
17908 						_minWrong[d2]=INF;
17909 						_nWrong[d2]=0;
17910 						_nConflActivities[d2]=0;
17911 						_minIndexAct[d2]=gt.rules.nInternalActivities;
17912 						_activitiesForDay[d2].clear();
17913 
17914 						for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,2*d2))){
17915 							if(ai2>=0){
17916 								if(!conflActivities[newtime].contains(ai2)){
17917 									occupiedDay[d2]=true;
17918 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
17919 										canEmptyDay[d2]=false;
17920 									else if(!_activitiesForDay[d2].contains(ai2)){
17921 										_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
17922 										_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
17923 										_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
17924 										_nConflActivities[d2]++;
17925 										_activitiesForDay[d2].append(ai2);
17926 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
17927 									}
17928 								}
17929 							}
17930 						}
17931 						for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,2*d2+1))){
17932 							if(ai2>=0){
17933 								if(!conflActivities[newtime].contains(ai2)){
17934 									occupiedDay[d2]=true;
17935 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
17936 										canEmptyDay[d2]=false;
17937 									else if(!_activitiesForDay[d2].contains(ai2)){
17938 										_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
17939 										_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
17940 										_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
17941 										_nConflActivities[d2]++;
17942 										_activitiesForDay[d2].append(ai2);
17943 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
17944 									}
17945 								}
17946 							}
17947 						}
17948 
17949 						if(!occupiedDay[d2])
17950 							canEmptyDay[d2]=false;
17951 					}
17952 					occupiedDay[d/2]=true;
17953 					canEmptyDay[d/2]=false;
17954 
17955 					int nOc=0;
17956 					bool canChooseDay=false;
17957 
17958 					for(int j=0; j<gt.rules.nDaysPerWeek/2; j++)
17959 						if(occupiedDay[j]){
17960 							nOc++;
17961 							if(canEmptyDay[j]){
17962 								canChooseDay=true;
17963 							}
17964 						}
17965 
17966 					if(nOc>maxDays){
17967 						assert(nOc==maxDays+1);
17968 
17969 						if(!canChooseDay){
17970 							if(level==0){
17971 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
17972 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
17973 							}
17974 							okteachermaxrealdaysperweek=false;
17975 							goto impossibleteachermaxrealdaysperweek;
17976 						}
17977 
17978 						int d2=-1;
17979 
17980 						if(level!=0){
17981 							//choose random day from those with minimum number of conflicting activities
17982 							QList<int> candidateDays;
17983 
17984 							int m=gt.rules.nInternalActivities;
17985 
17986 							for(int kk=0; kk<gt.rules.nDaysPerWeek/2; kk++)
17987 								if(canEmptyDay[kk])
17988 									if(m>_nConflActivities[kk])
17989 										m=_nConflActivities[kk];
17990 
17991 							candidateDays.clear();
17992 							for(int kk=0; kk<gt.rules.nDaysPerWeek/2; kk++)
17993 								if(canEmptyDay[kk] && m==_nConflActivities[kk])
17994 									candidateDays.append(kk);
17995 
17996 							assert(candidateDays.count()>0);
17997 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
17998 						}
17999 						else{ //level==0
18000 							QList<int> candidateDays;
18001 
18002 							int _mW=INF;
18003 							int _nW=INF;
18004 							int _mCA=gt.rules.nInternalActivities;
18005 							int _mIA=gt.rules.nInternalActivities;
18006 
18007 							for(int kk=0; kk<gt.rules.nDaysPerWeek/2; kk++)
18008 								if(canEmptyDay[kk]){
18009 									if(_mW>_minWrong[kk] ||
18010 									 (_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
18011 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
18012 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
18013 										_mW=_minWrong[kk];
18014 										_nW=_nWrong[kk];
18015 										_mCA=_nConflActivities[kk];
18016 										_mIA=_minIndexAct[kk];
18017 									}
18018 								}
18019 
18020 							assert(_mW<INF);
18021 
18022 							candidateDays.clear();
18023 							for(int kk=0; kk<gt.rules.nDaysPerWeek/2; kk++)
18024 								if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
18025 									candidateDays.append(kk);
18026 
18027 							assert(candidateDays.count()>0);
18028 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
18029 						}
18030 
18031 						assert(d2>=0);
18032 
18033 						assert(_activitiesForDay[d2].count()>0);
18034 
18035 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
18036 							assert(ai2!=ai);
18037 							assert(!swappedActivities[ai2]);
18038 							assert(!fixedTimeActivity[ai2]);
18039 							assert(!conflActivities[newtime].contains(ai2));
18040 							conflActivities[newtime].append(ai2);
18041 							nConflActivities[newtime]++;
18042 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
18043 							//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
18044 						}
18045 					}
18046 				}
18047 			}
18048 		}
18049 impossibleteachermaxrealdaysperweek:
18050 		if(!okteachermaxrealdaysperweek){
18051 			if(updateSubgroups || updateTeachers)
18052 				removeAiFromNewTimetable(ai, act, d, h);
18053 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
18054 
18055 			nConflActivities[newtime]=MAX_ACTIVITIES;
18056 			continue;
18057 		}
18058 
18059 		////////////////////////////END teachers max real days per week
18060 
18061 /////////////////////////////////////////////////////////////////////////////////////////////
18062 
18063 		//!!!If both teachers mornings early and afternoons early are present, the code is not perfectly optimized.
18064 		//We should integrate these with the other constraints, like for students.
18065 		okteachersafternoonsearlymaxbeginningsatsecondhour=true;
18066 
18067 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
18068 			//for Said213 - 2019-08-18
18069 			if(haveTeachersAfternoonsEarly){
18070 				for(int tch : qAsConst(act->iTeachersList))
18071 					if(!skipRandom(teachersAfternoonsEarlyMaxBeginningsAtSecondHourPercentage[tch])){
18072 						//preliminary check
18073 						int mhd=1; //min hours per day
18074 						if(teachersMinHoursDailyMinHours[tch][1]>=0)
18075 							mhd=teachersMinHoursDailyMinHours[tch][1];
18076 						assert(mhd>=1);
18077 						int mhm=mhd; //min hours per morning
18078 						if(teachersMinHoursDailyMinHours[tch][0]>=0)
18079 							mhm=teachersMinHoursDailyMinHours[tch][0];
18080 						assert(mhm>=mhd);
18081 						bool maxGapsZero=false;
18082 						if(teachersMaxGapsPerDayMaxGaps[tch]==0 ||
18083 						 teachersMaxGapsPerWeekMaxGaps[tch]==0 ||
18084 						 (teachersMaxGapsPerRealDayMaxGaps[tch]==0 && teachersMaxGapsPerRealDayAllowException[tch]==false) ||
18085 						 teachersMaxGapsPerWeekForRealDaysMaxGaps[tch]==0)
18086 							maxGapsZero=true;
18087 
18088 						int _nUsedMornings=0;
18089 						int _nUsedAfternoons=0;
18090 
18091 						int _nHours=0;
18092 						int _nfg=0;
18093 
18094 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
18095 							if(d2%2==0){ //morning
18096 								if(!maxGapsZero){
18097 									if(newTeachersDayNHours(tch,d2)>0){
18098 										_nHours+=max(newTeachersDayNHours(tch,d2), mhm);
18099 										_nUsedMornings++;
18100 									}
18101 								}
18102 								else{
18103 									if(newTeachersDayNHours(tch,d2)>0){
18104 										_nHours+=max(newTeachersDayNHours(tch,d2)+newTeachersDayNGaps(tch,d2), mhm);
18105 										_nUsedMornings++;
18106 									}
18107 								}
18108 							}
18109 							else{ //afternoon
18110 								if(!teacherNoGapsPerAfternoon(tch) && !maxGapsZero){
18111 									if(newTeachersDayNHours(tch,d2)>0){
18112 										int _nh=newTeachersDayNHours(tch,d2);
18113 										if(newTeachersDayNFirstGaps(tch,d2)==1){
18114 											if(_nh<mhd){
18115 												_nh=mhd;
18116 											}
18117 											else{
18118 												_nfg++;
18119 											}
18120 										}
18121 										else if(newTeachersDayNFirstGaps(tch,d2)>=2){
18122 											_nh++;
18123 										}
18124 
18125 										_nHours+=max(_nh, mhd);
18126 										_nUsedAfternoons++;
18127 									}
18128 								}
18129 								else{
18130 									if(newTeachersDayNHours(tch,d2)>0){
18131 										int _nh=newTeachersDayNHours(tch,d2)+newTeachersDayNGaps(tch,d2);
18132 										if(newTeachersDayNFirstGaps(tch,d2)==1){
18133 											if(_nh<mhd){
18134 												_nh=mhd;
18135 											}
18136 											else{
18137 												_nfg++;
18138 											}
18139 										}
18140 										else if(newTeachersDayNFirstGaps(tch,d2)>=2){
18141 											_nh+=newTeachersDayNFirstGaps(tch,d2)-1;
18142 											if(_nh<mhd){
18143 												_nh=mhd;
18144 											}
18145 											else{
18146 												_nfg++;
18147 											}
18148 										}
18149 
18150 										_nHours+=max(_nh, mhd);
18151 										_nUsedAfternoons++;
18152 									}
18153 								}
18154 							}
18155 						}
18156 
18157 						if(teachersAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch]<_nfg)
18158 							_nHours+=_nfg-teachersAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch];
18159 
18160 						if(teachersMinMorningsPerWeekMinMornings[tch]>=0)
18161 							if(teachersMinMorningsPerWeekMinMornings[tch]>_nUsedMornings)
18162 								_nHours+=(teachersMinMorningsPerWeekMinMornings[tch]-_nUsedMornings)*mhm;
18163 
18164 						if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>=0)
18165 							if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>_nUsedAfternoons)
18166 								_nHours+=(teachersMinAfternoonsPerWeekMinAfternoons[tch]-_nUsedAfternoons)*mhd;
18167 
18168 						if(_nHours > nHoursPerTeacher[tch]){
18169 							if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
18170 								okteachersafternoonsearlymaxbeginningsatsecondhour=false;
18171 								goto impossibleteachersafternoonsearlymaxbeginningsatsecondhour;
18172 							}
18173 
18174 							getTchTimetable(tch, conflActivities[newtime]);
18175 							tchGetNHoursGaps(tch);
18176 
18177 							for(;;){
18178 								int nUsedMornings=0;
18179 								int nUsedAfternoons=0;
18180 
18181 								int nHours=0;
18182 								int nfg=0;
18183 
18184 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
18185 									if(d2%2==0){ //morning
18186 										if(!maxGapsZero){
18187 											if(tchDayNHours[d2]>0){
18188 												nHours+=max(tchDayNHours[d2], mhm);
18189 												nUsedMornings++;
18190 											}
18191 										}
18192 										else{
18193 											if(tchDayNHours[d2]>0){
18194 												nHours+=max(tchDayNHours[d2]+tchDayNGaps[d2], mhm);
18195 												nUsedMornings++;
18196 											}
18197 										}
18198 									}
18199 									else{ //afternoon
18200 										if(!teacherNoGapsPerAfternoon(tch) && !maxGapsZero){
18201 											if(tchDayNHours[d2]>0){
18202 												int nh=tchDayNHours[d2];
18203 												if(tchDayNFirstGaps[d2]==1){
18204 													if(nh<mhd){
18205 														nh=mhd;
18206 													}
18207 													else{
18208 														nfg++;
18209 													}
18210 												}
18211 												else if(tchDayNFirstGaps[d2]>=2){
18212 													nh++;
18213 												}
18214 
18215 												nHours+=max(nh, mhd);
18216 												nUsedAfternoons++;
18217 											}
18218 										}
18219 										else{
18220 											if(tchDayNHours[d2]>0){
18221 												int nh=tchDayNHours[d2]+tchDayNGaps[d2];
18222 												if(tchDayNFirstGaps[d2]==1){
18223 													if(nh<mhd){
18224 														nh=mhd;
18225 													}
18226 													else{
18227 														nfg++;
18228 													}
18229 												}
18230 												else if(tchDayNFirstGaps[d2]>=2){
18231 													nh+=tchDayNFirstGaps[d2]-1;
18232 													if(nh<mhd){
18233 														nh=mhd;
18234 													}
18235 													else{
18236 														nfg++;
18237 													}
18238 												}
18239 
18240 												nHours+=max(nh, mhd);
18241 												nUsedAfternoons++;
18242 											}
18243 										}
18244 									}
18245 								}
18246 
18247 								if(teachersAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch]<nfg)
18248 									nHours+=nfg-teachersAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch];
18249 
18250 								if(teachersMinMorningsPerWeekMinMornings[tch]>=0)
18251 									if(teachersMinMorningsPerWeekMinMornings[tch]>nUsedMornings)
18252 										nHours+=(teachersMinMorningsPerWeekMinMornings[tch]-nUsedMornings)*mhm;
18253 
18254 								if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>=0)
18255 									if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>nUsedAfternoons)
18256 										nHours+=(teachersMinAfternoonsPerWeekMinAfternoons[tch]-nUsedAfternoons)*mhd;
18257 
18258 								int ai2=-1;
18259 
18260 								if(nHours > nHoursPerTeacher[tch]){
18261 									//remove an activity
18262 									bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
18263 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
18264 									if(!k){
18265 										if(maxGapsZero){
18266 											okteachersafternoonsearlymaxbeginningsatsecondhour=false;
18267 											goto impossibleteachersafternoonsearlymaxbeginningsatsecondhour;
18268 										}
18269 										else{
18270 											bool ka=teacherRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
18271 											assert(conflActivities[newtime].count()==nConflActivities[newtime]);
18272 
18273 											if(!ka){
18274 												okteachersafternoonsearlymaxbeginningsatsecondhour=false;
18275 												goto impossibleteachersafternoonsearlymaxbeginningsatsecondhour;
18276 											}
18277 										}
18278 									}
18279 								}
18280 								else{ //OK
18281 									break;
18282 								}
18283 
18284 								assert(ai2>=0);
18285 
18286 								removeAi2FromTchTimetable(ai2);
18287 								updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
18288 							}
18289 						}
18290 					}
18291 			}
18292 		}
18293 
18294 impossibleteachersafternoonsearlymaxbeginningsatsecondhour:
18295 		if(!okteachersafternoonsearlymaxbeginningsatsecondhour){
18296 			if(updateSubgroups || updateTeachers)
18297 				removeAiFromNewTimetable(ai, act, d, h);
18298 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
18299 
18300 			nConflActivities[newtime]=MAX_ACTIVITIES;
18301 			continue;
18302 		}
18303 
18304 		////////////////////////////END teachers afternoons early
18305 
18306 /////////////////////////////////////////////////////////////////////////////////////////////
18307 
18308 		//!!!If both teachers mornings early and afternoons early are present, the code is not perfectly optimized.
18309 		//We should integrate these with the other constraints, like for students.
18310 		okteachersmorningsearlymaxbeginningsatsecondhour=true;
18311 
18312 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
18313 			if(haveTeachersMorningsEarly){
18314 				for(int tch : qAsConst(act->iTeachersList))
18315 					if(!skipRandom(teachersMorningsEarlyMaxBeginningsAtSecondHourPercentage[tch])){
18316 						//preliminary check
18317 						int mhd=1; //min hours per day
18318 						if(teachersMinHoursDailyMinHours[tch][1]>=0)
18319 							mhd=teachersMinHoursDailyMinHours[tch][1];
18320 						assert(mhd>=1);
18321 						int mhm=mhd; //min hours per morning
18322 						if(teachersMinHoursDailyMinHours[tch][0]>=0)
18323 							mhm=teachersMinHoursDailyMinHours[tch][0];
18324 						assert(mhm>=mhd);
18325 						bool maxGapsZero=false;
18326 						if(teachersMaxGapsPerDayMaxGaps[tch]==0 ||
18327 						 teachersMaxGapsPerWeekMaxGaps[tch]==0 ||
18328 						 (teachersMaxGapsPerRealDayMaxGaps[tch]==0 && teachersMaxGapsPerRealDayAllowException[tch]==false) ||
18329 						 teachersMaxGapsPerWeekForRealDaysMaxGaps[tch]==0)
18330 							maxGapsZero=true;
18331 
18332 						int _nUsedMornings=0;
18333 						int _nUsedAfternoons=0;
18334 
18335 						int _nHours=0;
18336 						int _nfg=0;
18337 
18338 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
18339 							if(d2%2==0){ //morning
18340 								if(!maxGapsZero){
18341 									if(newTeachersDayNHours(tch,d2)>0){
18342 										int _nh=newTeachersDayNHours(tch,d2);
18343 										if(newTeachersDayNFirstGaps(tch,d2)==1){
18344 											if(_nh<mhm){
18345 												_nh=mhm;
18346 											}
18347 											else{
18348 												_nfg++;
18349 											}
18350 										}
18351 										else if(newTeachersDayNFirstGaps(tch,d2)>=2){
18352 											_nh++;
18353 										}
18354 
18355 										_nHours+=max(_nh, mhm);
18356 										_nUsedMornings++;
18357 									}
18358 								}
18359 								else{
18360 									if(newTeachersDayNHours(tch,d2)>0){
18361 										int _nh=newTeachersDayNHours(tch,d2)+newTeachersDayNGaps(tch,d2);
18362 										if(newTeachersDayNFirstGaps(tch,d2)==1){
18363 											if(_nh<mhm){
18364 												_nh=mhm;
18365 											}
18366 											else{
18367 												_nfg++;
18368 											}
18369 										}
18370 										else if(newTeachersDayNFirstGaps(tch,d2)>=2){
18371 											_nh+=newTeachersDayNFirstGaps(tch,d2)-1;
18372 											if(_nh<mhm){
18373 												_nh=mhm;
18374 											}
18375 											else{
18376 												_nfg++;
18377 											}
18378 										}
18379 
18380 										_nHours+=max(_nh, mhm);
18381 										_nUsedMornings++;
18382 									}
18383 								}
18384 							}
18385 							else{ //afternoon
18386 								if(!teacherNoGapsPerAfternoon(tch) && !maxGapsZero){
18387 									if(newTeachersDayNHours(tch,d2)>0){
18388 										_nHours+=max(newTeachersDayNHours(tch,d2), mhd);
18389 										_nUsedAfternoons++;
18390 									}
18391 								}
18392 								else{
18393 									if(newTeachersDayNHours(tch,d2)>0){
18394 										_nHours+=max(newTeachersDayNHours(tch,d2)+newTeachersDayNGaps(tch,d2), mhd);
18395 										_nUsedAfternoons++;
18396 									}
18397 								}
18398 							}
18399 						}
18400 
18401 						if(teachersMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch]<_nfg)
18402 							_nHours+=_nfg-teachersMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch];
18403 
18404 						if(teachersMinMorningsPerWeekMinMornings[tch]>=0)
18405 							if(teachersMinMorningsPerWeekMinMornings[tch]>_nUsedMornings)
18406 								_nHours+=(teachersMinMorningsPerWeekMinMornings[tch]-_nUsedMornings)*mhm;
18407 
18408 						if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>=0)
18409 							if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>_nUsedAfternoons)
18410 								_nHours+=(teachersMinAfternoonsPerWeekMinAfternoons[tch]-_nUsedAfternoons)*mhd;
18411 
18412 						if(_nHours > nHoursPerTeacher[tch]){
18413 							if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
18414 								okteachersmorningsearlymaxbeginningsatsecondhour=false;
18415 								goto impossibleteachersmorningsearlymaxbeginningsatsecondhour;
18416 							}
18417 
18418 							getTchTimetable(tch, conflActivities[newtime]);
18419 							tchGetNHoursGaps(tch);
18420 
18421 							for(;;){
18422 								int nUsedMornings=0;
18423 								int nUsedAfternoons=0;
18424 
18425 								int nHours=0;
18426 								int nfg=0;
18427 
18428 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
18429 									if(d2%2==0){ //morning
18430 										if(!maxGapsZero){
18431 											if(tchDayNHours[d2]>0){
18432 												int nh=tchDayNHours[d2];
18433 												if(tchDayNFirstGaps[d2]==1){
18434 													if(nh<mhm){
18435 														nh=mhm;
18436 													}
18437 													else{
18438 														nfg++;
18439 													}
18440 												}
18441 												else if(tchDayNFirstGaps[d2]>=2){
18442 													nh++;
18443 												}
18444 
18445 												nHours+=max(nh, mhm);
18446 												nUsedMornings++;
18447 											}
18448 										}
18449 										else{
18450 											if(tchDayNHours[d2]>0){
18451 												int nh=tchDayNHours[d2]+tchDayNGaps[d2];
18452 												if(tchDayNFirstGaps[d2]==1){
18453 													if(nh<mhm){
18454 														nh=mhm;
18455 													}
18456 													else{
18457 														nfg++;
18458 													}
18459 												}
18460 												else if(tchDayNFirstGaps[d2]>=2){
18461 													nh+=tchDayNFirstGaps[d2]-1;
18462 													if(nh<mhm){
18463 														nh=mhm;
18464 													}
18465 													else{
18466 														nfg++;
18467 													}
18468 												}
18469 
18470 												nHours+=max(nh, mhm);
18471 												nUsedMornings++;
18472 											}
18473 										}
18474 									}
18475 									else{ //afternoon
18476 										if(!teacherNoGapsPerAfternoon(tch) && !maxGapsZero){
18477 											if(tchDayNHours[d2]>0){
18478 												nHours+=max(tchDayNHours[d2], mhd);
18479 												nUsedAfternoons++;
18480 											}
18481 										}
18482 										else{
18483 											if(tchDayNHours[d2]>0){
18484 												nHours+=max(tchDayNHours[d2]+tchDayNGaps[d2], mhd);
18485 												nUsedAfternoons++;
18486 											}
18487 										}
18488 									}
18489 								}
18490 
18491 								if(teachersMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch]<nfg)
18492 									nHours+=nfg-teachersMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch];
18493 
18494 								if(teachersMinMorningsPerWeekMinMornings[tch]>=0)
18495 									if(teachersMinMorningsPerWeekMinMornings[tch]>nUsedMornings)
18496 										nHours+=(teachersMinMorningsPerWeekMinMornings[tch]-nUsedMornings)*mhm;
18497 
18498 								if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>=0)
18499 									if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>nUsedAfternoons)
18500 										nHours+=(teachersMinAfternoonsPerWeekMinAfternoons[tch]-nUsedAfternoons)*mhd;
18501 
18502 								int ai2=-1;
18503 
18504 								if(nHours > nHoursPerTeacher[tch]){
18505 									//remove an activity
18506 									bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
18507 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
18508 									if(!k){
18509 										if(maxGapsZero){
18510 											okteachersmorningsearlymaxbeginningsatsecondhour=false;
18511 											goto impossibleteachersmorningsearlymaxbeginningsatsecondhour;
18512 										}
18513 										else{
18514 											bool ka=teacherRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
18515 											assert(conflActivities[newtime].count()==nConflActivities[newtime]);
18516 
18517 											if(!ka){
18518 												okteachersmorningsearlymaxbeginningsatsecondhour=false;
18519 												goto impossibleteachersmorningsearlymaxbeginningsatsecondhour;
18520 											}
18521 										}
18522 									}
18523 								}
18524 								else{ //OK
18525 									break;
18526 								}
18527 
18528 								assert(ai2>=0);
18529 
18530 								removeAi2FromTchTimetable(ai2);
18531 								updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
18532 							}
18533 						}
18534 					}
18535 			}
18536 		}
18537 
18538 impossibleteachersmorningsearlymaxbeginningsatsecondhour:
18539 		if(!okteachersmorningsearlymaxbeginningsatsecondhour){
18540 			if(updateSubgroups || updateTeachers)
18541 				removeAiFromNewTimetable(ai, act, d, h);
18542 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
18543 
18544 			nConflActivities[newtime]=MAX_ACTIVITIES;
18545 			continue;
18546 		}
18547 
18548 		////////////////////////////END teachers mornings early
18549 
18550 /////////////////////////////////////////////////////////////////////////////////////////////
18551 
18552 		//If both teachers mornings early and afternoons early are present, we can do a combined better check.
18553 		okteachersmorningsafternoonsearlymaxbeginningsatsecondhour=true;
18554 
18555 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
18556 			if(haveTeachersAfternoonsEarly && haveTeachersMorningsEarly){
18557 				for(int tch : qAsConst(act->iTeachersList))
18558 					if(!skipRandom(teachersAfternoonsEarlyMaxBeginningsAtSecondHourPercentage[tch]) && !skipRandom(teachersMorningsEarlyMaxBeginningsAtSecondHourPercentage[tch])){
18559 						//preliminary check
18560 						int mhd=1; //min hours per day
18561 						if(teachersMinHoursDailyMinHours[tch][1]>=0)
18562 							mhd=teachersMinHoursDailyMinHours[tch][1];
18563 						assert(mhd>=1);
18564 						int mhm=mhd; //min hours per morning
18565 						if(teachersMinHoursDailyMinHours[tch][0]>=0)
18566 							mhm=teachersMinHoursDailyMinHours[tch][0];
18567 						assert(mhm>=mhd);
18568 						bool maxGapsZero=false;
18569 						if(teachersMaxGapsPerDayMaxGaps[tch]==0 ||
18570 						 teachersMaxGapsPerWeekMaxGaps[tch]==0 ||
18571 						 (teachersMaxGapsPerRealDayMaxGaps[tch]==0 && teachersMaxGapsPerRealDayAllowException[tch]==false) ||
18572 						 teachersMaxGapsPerWeekForRealDaysMaxGaps[tch]==0)
18573 							maxGapsZero=true;
18574 
18575 						int _nUsedMornings=0;
18576 						int _nUsedAfternoons=0;
18577 
18578 						int _nHours=0;
18579 						int _nfg=0;
18580 
18581 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
18582 							if(d2%2==0){ //morning
18583 								if(!maxGapsZero){
18584 									if(newTeachersDayNHours(tch,d2)>0){
18585 										int _nh=newTeachersDayNHours(tch,d2);
18586 										if(newTeachersDayNFirstGaps(tch,d2)==1){
18587 											if(_nh<mhm){
18588 												_nh=mhm;
18589 											}
18590 											else{
18591 												_nfg++;
18592 											}
18593 										}
18594 										else if(newTeachersDayNFirstGaps(tch,d2)>=2){
18595 											_nh++;
18596 										}
18597 
18598 										_nHours+=max(_nh, mhm);
18599 										_nUsedMornings++;
18600 									}
18601 								}
18602 								else{
18603 									if(newTeachersDayNHours(tch,d2)>0){
18604 										int _nh=newTeachersDayNHours(tch,d2)+newTeachersDayNGaps(tch,d2);
18605 										if(newTeachersDayNFirstGaps(tch,d2)==1){
18606 											if(_nh<mhm){
18607 												_nh=mhm;
18608 											}
18609 											else{
18610 												_nfg++;
18611 											}
18612 										}
18613 										else if(newTeachersDayNFirstGaps(tch,d2)>=2){
18614 											_nh+=newTeachersDayNFirstGaps(tch,d2)-1;
18615 											if(_nh<mhm){
18616 												_nh=mhm;
18617 											}
18618 											else{
18619 												_nfg++;
18620 											}
18621 										}
18622 
18623 										_nHours+=max(_nh, mhm);
18624 										_nUsedMornings++;
18625 									}
18626 								}
18627 							}
18628 							else{ //afternoon
18629 								if(!teacherNoGapsPerAfternoon(tch) && !maxGapsZero){
18630 									if(newTeachersDayNHours(tch,d2)>0){
18631 										int _nh=newTeachersDayNHours(tch,d2);
18632 										if(newTeachersDayNFirstGaps(tch,d2)==1){
18633 											if(_nh<mhd){
18634 												_nh=mhd;
18635 											}
18636 											else{
18637 												_nfg++;
18638 											}
18639 										}
18640 										else if(newTeachersDayNFirstGaps(tch,d2)>=2){
18641 											_nh++;
18642 										}
18643 
18644 										_nHours+=max(_nh, mhd);
18645 										_nUsedAfternoons++;
18646 									}
18647 								}
18648 								else{
18649 									if(newTeachersDayNHours(tch,d2)>0){
18650 										int _nh=newTeachersDayNHours(tch,d2)+newTeachersDayNGaps(tch,d2);
18651 										if(newTeachersDayNFirstGaps(tch,d2)==1){
18652 											if(_nh<mhd){
18653 												_nh=mhd;
18654 											}
18655 											else{
18656 												_nfg++;
18657 											}
18658 										}
18659 										else if(newTeachersDayNFirstGaps(tch,d2)>=2){
18660 											_nh+=newTeachersDayNFirstGaps(tch,d2)-1;
18661 											if(_nh<mhd){
18662 												_nh=mhd;
18663 											}
18664 											else{
18665 												_nfg++;
18666 											}
18667 										}
18668 
18669 										_nHours+=max(_nh, mhd);
18670 										_nUsedAfternoons++;
18671 									}
18672 								}
18673 							}
18674 						}
18675 
18676 						if(teachersAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch]+
18677 						 teachersMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch] < _nfg)
18678 							_nHours+=_nfg-teachersAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch]-teachersMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch];
18679 
18680 						if(teachersMinMorningsPerWeekMinMornings[tch]>=0)
18681 							if(teachersMinMorningsPerWeekMinMornings[tch]>_nUsedMornings)
18682 								_nHours+=(teachersMinMorningsPerWeekMinMornings[tch]-_nUsedMornings)*mhm;
18683 
18684 						if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>=0)
18685 							if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>_nUsedAfternoons)
18686 								_nHours+=(teachersMinAfternoonsPerWeekMinAfternoons[tch]-_nUsedAfternoons)*mhd;
18687 
18688 						if(_nHours > nHoursPerTeacher[tch]){
18689 							if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
18690 								okteachersmorningsafternoonsearlymaxbeginningsatsecondhour=false;
18691 								goto impossibleteachersmorningsafternoonsearlymaxbeginningsatsecondhour;
18692 							}
18693 
18694 							getTchTimetable(tch, conflActivities[newtime]);
18695 							tchGetNHoursGaps(tch);
18696 
18697 							for(;;){
18698 								int nUsedMornings=0;
18699 								int nUsedAfternoons=0;
18700 
18701 								int nHours=0;
18702 								int nfg=0;
18703 
18704 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
18705 									if(d2%2==0){ //morning
18706 										if(!maxGapsZero){
18707 											if(tchDayNHours[d2]>0){
18708 												int nh=tchDayNHours[d2];
18709 												if(tchDayNFirstGaps[d2]==1){
18710 													if(nh<mhm){
18711 														nh=mhm;
18712 													}
18713 													else{
18714 														nfg++;
18715 													}
18716 												}
18717 												else if(tchDayNFirstGaps[d2]>=2){
18718 													nh++;
18719 												}
18720 
18721 												nHours+=max(nh, mhm);
18722 												nUsedMornings++;
18723 											}
18724 										}
18725 										else{
18726 											if(tchDayNHours[d2]>0){
18727 												int nh=tchDayNHours[d2]+tchDayNGaps[d2];
18728 												if(tchDayNFirstGaps[d2]==1){
18729 													if(nh<mhm){
18730 														nh=mhm;
18731 													}
18732 													else{
18733 														nfg++;
18734 													}
18735 												}
18736 												else if(tchDayNFirstGaps[d2]>=2){
18737 													nh+=tchDayNFirstGaps[d2]-1;
18738 													if(nh<mhm){
18739 														nh=mhm;
18740 													}
18741 													else{
18742 														nfg++;
18743 													}
18744 												}
18745 
18746 												nHours+=max(nh, mhm);
18747 												nUsedMornings++;
18748 											}
18749 										}
18750 									}
18751 									else{ //afternoon
18752 										if(!teacherNoGapsPerAfternoon(tch) && !maxGapsZero){
18753 											if(tchDayNHours[d2]>0){
18754 												int nh=tchDayNHours[d2];
18755 												if(tchDayNFirstGaps[d2]==1){
18756 													if(nh<mhd){
18757 														nh=mhd;
18758 													}
18759 													else{
18760 														nfg++;
18761 													}
18762 												}
18763 												else if(tchDayNFirstGaps[d2]>=2){
18764 													nh++;
18765 												}
18766 
18767 												nHours+=max(nh, mhd);
18768 												nUsedAfternoons++;
18769 											}
18770 										}
18771 										else{
18772 											if(tchDayNHours[d2]>0){
18773 												int nh=tchDayNHours[d2]+tchDayNGaps[d2];
18774 												if(tchDayNFirstGaps[d2]==1){
18775 													if(nh<mhd){
18776 														nh=mhd;
18777 													}
18778 													else{
18779 														nfg++;
18780 													}
18781 												}
18782 												else if(tchDayNFirstGaps[d2]>=2){
18783 													nh+=tchDayNFirstGaps[d2]-1;
18784 													if(nh<mhd){
18785 														nh=mhd;
18786 													}
18787 													else{
18788 														nfg++;
18789 													}
18790 												}
18791 
18792 												nHours+=max(nh, mhd);
18793 												nUsedAfternoons++;
18794 											}
18795 										}
18796 									}
18797 								}
18798 
18799 								if(teachersAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch]+
18800 								 teachersMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch]<nfg)
18801 									nHours+=nfg-teachersAfternoonsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch]-teachersMorningsEarlyMaxBeginningsAtSecondHourMaxBeginnings[tch];
18802 
18803 								if(teachersMinMorningsPerWeekMinMornings[tch]>=0)
18804 									if(teachersMinMorningsPerWeekMinMornings[tch]>nUsedMornings)
18805 										nHours+=(teachersMinMorningsPerWeekMinMornings[tch]-nUsedMornings)*mhm;
18806 
18807 								if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>=0)
18808 									if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>nUsedAfternoons)
18809 										nHours+=(teachersMinAfternoonsPerWeekMinAfternoons[tch]-nUsedAfternoons)*mhd;
18810 
18811 								int ai2=-1;
18812 
18813 								if(nHours > nHoursPerTeacher[tch]){
18814 									//remove an activity
18815 									bool k=teacherRemoveAnActivityFromEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
18816 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
18817 									if(!k){
18818 										if(maxGapsZero){
18819 											okteachersmorningsafternoonsearlymaxbeginningsatsecondhour=false;
18820 											goto impossibleteachersmorningsafternoonsearlymaxbeginningsatsecondhour;
18821 										}
18822 										else{
18823 											bool ka=teacherRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
18824 											assert(conflActivities[newtime].count()==nConflActivities[newtime]);
18825 
18826 											if(!ka){
18827 												okteachersmorningsafternoonsearlymaxbeginningsatsecondhour=false;
18828 												goto impossibleteachersmorningsafternoonsearlymaxbeginningsatsecondhour;
18829 											}
18830 										}
18831 									}
18832 								}
18833 								else{ //OK
18834 									break;
18835 								}
18836 
18837 								assert(ai2>=0);
18838 
18839 								removeAi2FromTchTimetable(ai2);
18840 								updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
18841 							}
18842 						}
18843 					}
18844 			}
18845 		}
18846 
18847 impossibleteachersmorningsafternoonsearlymaxbeginningsatsecondhour:
18848 		if(!okteachersmorningsafternoonsearlymaxbeginningsatsecondhour){
18849 			if(updateSubgroups || updateTeachers)
18850 				removeAiFromNewTimetable(ai, act, d, h);
18851 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
18852 
18853 			nConflActivities[newtime]=MAX_ACTIVITIES;
18854 			continue;
18855 		}
18856 
18857 		////////////////////////////END teachers mornings+afternoons early
18858 
18859 /////////////////////////////////////////////////////////////////////////////////////////////
18860 
18861 		//not breaking the teacher max afternoons per week constraints
18862 		////////////////////////////BEGIN max afternoons per week for teachers
18863 		okteachermaxafternoonsperweek=true;
18864 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
18865 			//for(int tch : qAsConst(act->iTeachersList)){
18866 			for(int tch : qAsConst(teachersWithMaxAfternoonsPerWeekForActivities[ai])){
18867 				if(skipRandom(teachersMaxAfternoonsPerWeekWeightPercentages[tch]))
18868 					continue;
18869 
18870 				int maxAfternoons=teachersMaxAfternoonsPerWeekMaxAfternoons[tch];
18871 				assert(maxAfternoons>=0); //the list contains real information
18872 
18873 				//preliminary test
18874 				int _nOc=0;
18875 				for(int d2=1; d2<gt.rules.nDaysPerWeek; d2+=2)
18876 					//if(newTeachersDayNHours(tch,d2)>0)
18877 
18878 					//IT IS VITAL TO USE teacherActivitiesOfTheDay as a QList<int> (tch,d2)!!!!!!!
18879 					//The order of evaluation of activities is changed,
18880 					//with activities which were moved forward and back again
18881 					//being put at the end.
18882 					//If you do not follow this, you'll get impossible timetables
18883 					//for the Italian example Italy/2007/difficult/highschool-Ancona.fet or the examples from
18884 					//South Africa: South-Africa/difficult/Collegiate_Junior_School2.fet or
18885 					//South-Africa/difficult/Insight_Learning_Centre2.fet, I am not sure which of these 3
18886 
18887 					if(teacherActivitiesOfTheDay(tch,d2).count()>0 || d2==d)
18888 						_nOc++;
18889 				if(_nOc<=maxAfternoons)
18890 					continue; //OK, preliminary
18891 
18892 				if(maxAfternoons>=0){
18893 					assert(maxAfternoons>0);
18894 
18895 					//getTchTimetable(tch, conflActivities[newtime]);
18896 					//tchGetNHoursGaps(tch);
18897 
18898 					//bool occupiedDay[MAX_DAYS_PER_WEEK];
18899 					//bool canEmptyDay[MAX_DAYS_PER_WEEK];
18900 
18901 					//int _minWrong[MAX_DAYS_PER_WEEK];
18902 					//int _nWrong[MAX_DAYS_PER_WEEK];
18903 					//int _nConflActivities[MAX_DAYS_PER_WEEK];
18904 					//int _minIndexAct[MAX_DAYS_PER_WEEK];
18905 
18906 					//QList<int> _activitiesForDay[MAX_DAYS_PER_WEEK];
18907 
18908 					for(int d2=1; d2<gt.rules.nDaysPerWeek; d2+=2){
18909 						if(d2==d)
18910 							continue;
18911 
18912 						occupiedDay[d2]=false;
18913 						canEmptyDay[d2]=true;
18914 
18915 						_minWrong[d2]=INF;
18916 						_nWrong[d2]=0;
18917 						_nConflActivities[d2]=0;
18918 						_minIndexAct[d2]=gt.rules.nInternalActivities;
18919 						_activitiesForDay[d2].clear();
18920 
18921 						for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
18922 							if(ai2>=0){
18923 								if(!conflActivities[newtime].contains(ai2)){
18924 									occupiedDay[d2]=true;
18925 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
18926 										canEmptyDay[d2]=false;
18927 									else if(!_activitiesForDay[d2].contains(ai2)){
18928 										_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
18929 										_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
18930 										_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
18931 										_nConflActivities[d2]++;
18932 										_activitiesForDay[d2].append(ai2);
18933 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
18934 									}
18935 								}
18936 							}
18937 						}
18938 
18939 						if(!occupiedDay[d2])
18940 							canEmptyDay[d2]=false;
18941 					}
18942 					occupiedDay[d]=true;
18943 					canEmptyDay[d]=false;
18944 
18945 					int nOc=0;
18946 					bool canChooseDay=false;
18947 
18948 					for(int j=1; j<gt.rules.nDaysPerWeek; j+=2)
18949 						if(occupiedDay[j]){
18950 							nOc++;
18951 							if(canEmptyDay[j]){
18952 								canChooseDay=true;
18953 							}
18954 						}
18955 
18956 					if(nOc>maxAfternoons){
18957 						assert(nOc==maxAfternoons+1);
18958 
18959 						if(!canChooseDay){
18960 							if(level==0){
18961 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
18962 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
18963 							}
18964 							okteachermaxafternoonsperweek=false;
18965 							goto impossibleteachermaxafternoonsperweek;
18966 						}
18967 
18968 						int d2=-1;
18969 
18970 						if(level!=0){
18971 							//choose random day from those with minimum number of conflicting activities
18972 							QList<int> candidateDays;
18973 
18974 							int m=gt.rules.nInternalActivities;
18975 
18976 							for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2)
18977 								if(canEmptyDay[kk])
18978 									if(m>_nConflActivities[kk])
18979 										m=_nConflActivities[kk];
18980 
18981 							candidateDays.clear();
18982 							for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2)
18983 								if(canEmptyDay[kk] && m==_nConflActivities[kk])
18984 									candidateDays.append(kk);
18985 
18986 							assert(candidateDays.count()>0);
18987 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
18988 						}
18989 						else{ //level==0
18990 							QList<int> candidateDays;
18991 
18992 							int _mW=INF;
18993 							int _nW=INF;
18994 							int _mCA=gt.rules.nInternalActivities;
18995 							int _mIA=gt.rules.nInternalActivities;
18996 
18997 							for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2)
18998 								if(canEmptyDay[kk]){
18999 									if(_mW>_minWrong[kk] ||
19000 									 (_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
19001 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
19002 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
19003 										_mW=_minWrong[kk];
19004 										_nW=_nWrong[kk];
19005 										_mCA=_nConflActivities[kk];
19006 										_mIA=_minIndexAct[kk];
19007 									}
19008 								}
19009 
19010 							assert(_mW<INF);
19011 
19012 							candidateDays.clear();
19013 							for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2)
19014 								if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
19015 									candidateDays.append(kk);
19016 
19017 							assert(candidateDays.count()>0);
19018 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
19019 						}
19020 
19021 						assert(d2>=0);
19022 
19023 						assert(_activitiesForDay[d2].count()>0);
19024 
19025 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
19026 							assert(ai2!=ai);
19027 							assert(!swappedActivities[ai2]);
19028 							assert(!fixedTimeActivity[ai2]);
19029 							assert(!conflActivities[newtime].contains(ai2));
19030 							conflActivities[newtime].append(ai2);
19031 							nConflActivities[newtime]++;
19032 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
19033 							//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
19034 						}
19035 					}
19036 				}
19037 			}
19038 		}
19039 impossibleteachermaxafternoonsperweek:
19040 		if(!okteachermaxafternoonsperweek){
19041 			if(updateSubgroups || updateTeachers)
19042 				removeAiFromNewTimetable(ai, act, d, h);
19043 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
19044 
19045 			nConflActivities[newtime]=MAX_ACTIVITIES;
19046 			continue;
19047 		}
19048 
19049 		////////////////////////////END teachers max afternoons per week
19050 
19051 /////////////////////////////////////////////////////////////////////////////////////////////
19052 
19053 		//not breaking the teacher max mornings per week constraints
19054 		////////////////////////////BEGIN max mornings per week for teachers
19055 		okteachermaxmorningsperweek=true;
19056 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
19057 			//for(int tch : qAsConst(act->iTeachersList)){
19058 			for(int tch : qAsConst(teachersWithMaxMorningsPerWeekForActivities[ai])){
19059 				if(skipRandom(teachersMaxMorningsPerWeekWeightPercentages[tch]))
19060 					continue;
19061 
19062 				int maxMornings=teachersMaxMorningsPerWeekMaxMornings[tch];
19063 				assert(maxMornings>=0); //the list contains real information
19064 
19065 				//preliminary test
19066 				int _nOc=0;
19067 				for(int d2=0; d2<gt.rules.nDaysPerWeek; d2+=2)
19068 					//if(newTeachersDayNHours(tch,d2)>0)
19069 
19070 					//IT IS VITAL TO USE teacherActivitiesOfTheDay as a QList<int> (tch,d2)!!!!!!!
19071 					//The order of evaluation of activities is changed,
19072 					//with activities which were moved forward and back again
19073 					//being put at the end.
19074 					//If you do not follow this, you'll get impossible timetables
19075 					//for the Italian example Italy/2007/difficult/highschool-Ancona.fet or the examples from
19076 					//South Africa: South-Africa/difficult/Collegiate_Junior_School2.fet or
19077 					//South-Africa/difficult/Insight_Learning_Centre2.fet, I am not sure which of these 3
19078 
19079 					if(teacherActivitiesOfTheDay(tch,d2).count()>0 || d2==d)
19080 						_nOc++;
19081 				if(_nOc<=maxMornings)
19082 					continue; //OK, preliminary
19083 
19084 				if(maxMornings>=0){
19085 					assert(maxMornings>0);
19086 
19087 					//getTchTimetable(tch, conflActivities[newtime]);
19088 					//tchGetNHoursGaps(tch);
19089 
19090 					//bool occupiedDay[MAX_DAYS_PER_WEEK];
19091 					//bool canEmptyDay[MAX_DAYS_PER_WEEK];
19092 
19093 					//int _minWrong[MAX_DAYS_PER_WEEK];
19094 					//int _nWrong[MAX_DAYS_PER_WEEK];
19095 					//int _nConflActivities[MAX_DAYS_PER_WEEK];
19096 					//int _minIndexAct[MAX_DAYS_PER_WEEK];
19097 
19098 					//QList<int> _activitiesForDay[MAX_DAYS_PER_WEEK];
19099 
19100 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2+=2){
19101 						if(d2==d)
19102 							continue;
19103 
19104 						occupiedDay[d2]=false;
19105 						canEmptyDay[d2]=true;
19106 
19107 						_minWrong[d2]=INF;
19108 						_nWrong[d2]=0;
19109 						_nConflActivities[d2]=0;
19110 						_minIndexAct[d2]=gt.rules.nInternalActivities;
19111 						_activitiesForDay[d2].clear();
19112 
19113 						for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
19114 							if(ai2>=0){
19115 								if(!conflActivities[newtime].contains(ai2)){
19116 									occupiedDay[d2]=true;
19117 									if(fixedTimeActivity[ai2] || swappedActivities[ai2])
19118 										canEmptyDay[d2]=false;
19119 									else if(!_activitiesForDay[d2].contains(ai2)){
19120 										_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
19121 										_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
19122 										_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
19123 										_nConflActivities[d2]++;
19124 										_activitiesForDay[d2].append(ai2);
19125 										assert(_nConflActivities[d2]==_activitiesForDay[d2].count());
19126 									}
19127 								}
19128 							}
19129 						}
19130 
19131 						if(!occupiedDay[d2])
19132 							canEmptyDay[d2]=false;
19133 					}
19134 					occupiedDay[d]=true;
19135 					canEmptyDay[d]=false;
19136 
19137 					int nOc=0;
19138 					bool canChooseDay=false;
19139 
19140 					for(int j=0; j<gt.rules.nDaysPerWeek; j+=2)
19141 						if(occupiedDay[j]){
19142 							nOc++;
19143 							if(canEmptyDay[j]){
19144 								canChooseDay=true;
19145 							}
19146 						}
19147 
19148 					if(nOc>maxMornings){
19149 						assert(nOc==maxMornings+1);
19150 
19151 						if(!canChooseDay){
19152 							if(level==0){
19153 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
19154 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
19155 							}
19156 							okteachermaxmorningsperweek=false;
19157 							goto impossibleteachermaxmorningsperweek;
19158 						}
19159 
19160 						int d2=-1;
19161 
19162 						if(level!=0){
19163 							//choose random day from those with minimum number of conflicting activities
19164 							QList<int> candidateDays;
19165 
19166 							int m=gt.rules.nInternalActivities;
19167 
19168 							for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2)
19169 								if(canEmptyDay[kk])
19170 									if(m>_nConflActivities[kk])
19171 										m=_nConflActivities[kk];
19172 
19173 							candidateDays.clear();
19174 							for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2)
19175 								if(canEmptyDay[kk] && m==_nConflActivities[kk])
19176 									candidateDays.append(kk);
19177 
19178 							assert(candidateDays.count()>0);
19179 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
19180 						}
19181 						else{ //level==0
19182 							QList<int> candidateDays;
19183 
19184 							int _mW=INF;
19185 							int _nW=INF;
19186 							int _mCA=gt.rules.nInternalActivities;
19187 							int _mIA=gt.rules.nInternalActivities;
19188 
19189 							for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2)
19190 								if(canEmptyDay[kk]){
19191 									if(_mW>_minWrong[kk] ||
19192 									 (_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
19193 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
19194 									 (_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
19195 										_mW=_minWrong[kk];
19196 										_nW=_nWrong[kk];
19197 										_mCA=_nConflActivities[kk];
19198 										_mIA=_minIndexAct[kk];
19199 									}
19200 								}
19201 
19202 							assert(_mW<INF);
19203 
19204 							candidateDays.clear();
19205 							for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2)
19206 								if(canEmptyDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
19207 									candidateDays.append(kk);
19208 
19209 							assert(candidateDays.count()>0);
19210 							d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
19211 						}
19212 
19213 						assert(d2>=0);
19214 
19215 						assert(_activitiesForDay[d2].count()>0);
19216 
19217 						for(int ai2 : qAsConst(_activitiesForDay[d2])){
19218 							assert(ai2!=ai);
19219 							assert(!swappedActivities[ai2]);
19220 							assert(!fixedTimeActivity[ai2]);
19221 							assert(!conflActivities[newtime].contains(ai2));
19222 							conflActivities[newtime].append(ai2);
19223 							nConflActivities[newtime]++;
19224 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
19225 							//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
19226 						}
19227 					}
19228 				}
19229 			}
19230 		}
19231 impossibleteachermaxmorningsperweek:
19232 		if(!okteachermaxmorningsperweek){
19233 			if(updateSubgroups || updateTeachers)
19234 				removeAiFromNewTimetable(ai, act, d, h);
19235 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
19236 
19237 			nConflActivities[newtime]=MAX_ACTIVITIES;
19238 			continue;
19239 		}
19240 		////////////////////////////END teachers max mornings per week
19241 
19242 /////////////////////////////////////////////////////////////////////////////////////////////
19243 		//BEGIN teachers interval max days per week
19244 
19245 		okteachersintervalmaxdaysperweek=true;
19246 		for(int tch : qAsConst(act->iTeachersList)){
19247 			for(int cnt=0; cnt<teachersIntervalMaxDaysPerWeekPercentages[tch].count(); cnt++){
19248 				double perc=teachersIntervalMaxDaysPerWeekPercentages[tch].at(cnt);
19249 				int maxDays=teachersIntervalMaxDaysPerWeekMaxDays[tch].at(cnt);
19250 				int sth=teachersIntervalMaxDaysPerWeekIntervalStart[tch].at(cnt);
19251 				int endh=teachersIntervalMaxDaysPerWeekIntervalEnd[tch].at(cnt);
19252 
19253 				assert(perc>=0);
19254 				assert(sth>=0 && sth<gt.rules.nHoursPerDay);
19255 				assert(endh>sth && endh<=gt.rules.nHoursPerDay);
19256 				assert(maxDays>=0 && maxDays<=gt.rules.nDaysPerWeek);
19257 
19258 				if(skipRandom(perc))
19259 					continue;
19260 
19261 				assert(perc==100.0);
19262 
19263 				bool foundothers=false;
19264 				bool foundai=false;
19265 				for(int hh=sth; hh<endh; hh++){
19266 					if(newTeachersTimetable(tch,d,hh)==ai){
19267 						foundai=true;
19268 					}
19269 					else{
19270 						assert(newTeachersTimetable(tch,d,hh)==teachersTimetable(tch,d,hh));
19271 						if(newTeachersTimetable(tch,d,hh)>=0){
19272 							if(!conflActivities[newtime].contains(newTeachersTimetable(tch,d,hh))){
19273 								foundothers=true;
19274 							}
19275 						}
19276 					}
19277 				}
19278 				int nrotherdays=0;
19279 				for(int dd=0; dd<gt.rules.nDaysPerWeek; dd++){
19280 					if(dd!=d){
19281 						for(int hh=sth; hh<endh; hh++){
19282 							assert(newTeachersTimetable(tch,dd,hh)==teachersTimetable(tch,dd,hh));
19283 							if(newTeachersTimetable(tch,dd,hh)>=0 && !conflActivities[newtime].contains(newTeachersTimetable(tch,dd,hh))){
19284 								nrotherdays++;
19285 								break;
19286 							}
19287 						}
19288 					}
19289 				}
19290 				assert(nrotherdays<=maxDays); //if percentage==100%, then it is impossible to break this constraint
19291 				if((foundai && !foundothers) && nrotherdays==maxDays){ //increased above limit
19292 					if(level>0){
19293 						//bool occupiedIntervalDay[MAX_DAYS_PER_WEEK];
19294 						//bool canEmptyIntervalDay[MAX_DAYS_PER_WEEK];
19295 
19296 						///int _minWrong[MAX_DAYS_PER_WEEK];
19297 						///int _nWrong[MAX_DAYS_PER_WEEK];
19298 						//int _nConflActivities[MAX_DAYS_PER_WEEK];
19299 						///int _minIndexAct[MAX_DAYS_PER_WEEK];
19300 
19301 						//QList<int> _activitiesForIntervalDay[MAX_DAYS_PER_WEEK];
19302 
19303 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
19304 							if(d2==d)
19305 								continue;
19306 
19307 							occupiedIntervalDay[d2]=false;
19308 							canEmptyIntervalDay[d2]=true;
19309 
19310 							//_minWrong[d2]=INF;
19311 							//_nWrong[d2]=0;
19312 							_nConflActivities[d2]=0;
19313 							//_minIndexAct[d2]=gt.rules.nInternalActivities;
19314 							_activitiesForIntervalDay[d2].clear();
19315 
19316 							for(int h2=sth; h2<endh; h2++){
19317 								int ai2=teachersTimetable(tch,d2,h2);
19318 								if(ai2>=0){
19319 									if(!conflActivities[newtime].contains(ai2)){
19320 										occupiedIntervalDay[d2]=true;
19321 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
19322 											canEmptyIntervalDay[d2]=false;
19323 										else if(!_activitiesForIntervalDay[d2].contains(ai2)){
19324 											//_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
19325 											//_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
19326 											//_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
19327 											_nConflActivities[d2]++;
19328 											_activitiesForIntervalDay[d2].append(ai2);
19329 											assert(_nConflActivities[d2]==_activitiesForIntervalDay[d2].count());
19330 										}
19331 									}
19332 								}
19333 							}
19334 
19335 							if(!occupiedIntervalDay[d2])
19336 								canEmptyIntervalDay[d2]=false;
19337 						}
19338 						occupiedIntervalDay[d]=true;
19339 						canEmptyIntervalDay[d]=false;
19340 
19341 						int nOc=0;
19342 						bool canChooseDay=false;
19343 
19344 						for(int j=0; j<gt.rules.nDaysPerWeek; j++)
19345 							if(occupiedIntervalDay[j]){
19346 								nOc++;
19347 								if(canEmptyIntervalDay[j]){
19348 									canChooseDay=true;
19349 								}
19350 							}
19351 
19352 						assert(nOc==maxDays+1);
19353 
19354 						if(!canChooseDay){
19355 							if(level==0){
19356 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
19357 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
19358 							}
19359 							okteachersintervalmaxdaysperweek=false;
19360 							goto impossibleteachersintervalmaxdaysperweek;
19361 						}
19362 
19363 						int d2=-1;
19364 
19365 						///////////////
19366 						//choose a random day from those with minimum number of conflicting activities
19367 						QList<int> candidateDays;
19368 
19369 						int m=gt.rules.nInternalActivities;
19370 
19371 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
19372 							if(canEmptyIntervalDay[kk])
19373 								if(m>_nConflActivities[kk])
19374 									m=_nConflActivities[kk];
19375 
19376 						candidateDays.clear();
19377 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
19378 							if(canEmptyIntervalDay[kk])
19379 								if(m==_nConflActivities[kk])
19380 									candidateDays.append(kk);
19381 
19382 						assert(candidateDays.count()>0);
19383 						d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
19384 						///////////////
19385 
19386 						assert(d2>=0);
19387 
19388 						assert(_activitiesForIntervalDay[d2].count()>0);
19389 
19390 						for(int ai2 : qAsConst(_activitiesForIntervalDay[d2])){
19391 							assert(ai2!=ai);
19392 							assert(!swappedActivities[ai2]);
19393 							assert(!fixedTimeActivity[ai2]);
19394 							assert(!conflActivities[newtime].contains(ai2));
19395 							conflActivities[newtime].append(ai2);
19396 							nConflActivities[newtime]++;
19397 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
19398 							//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
19399 						}
19400 					}
19401 					else{
19402 						assert(level==0);
19403 
19404 						//bool occupiedIntervalDay[MAX_DAYS_PER_WEEK];
19405 						//bool canEmptyIntervalDay[MAX_DAYS_PER_WEEK];
19406 
19407 						//int _minWrong[MAX_DAYS_PER_WEEK];
19408 						//int _nWrong[MAX_DAYS_PER_WEEK];
19409 						//int _nConflActivities[MAX_DAYS_PER_WEEK];
19410 						//int _minIndexAct[MAX_DAYS_PER_WEEK];
19411 
19412 						//QList<int> _activitiesForIntervalDay[MAX_DAYS_PER_WEEK];
19413 
19414 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
19415 							if(d2==d)
19416 								continue;
19417 
19418 							occupiedIntervalDay[d2]=false;
19419 							canEmptyIntervalDay[d2]=true;
19420 
19421 							_minWrong[d2]=INF;
19422 							_nWrong[d2]=0;
19423 							_nConflActivities[d2]=0;
19424 							_minIndexAct[d2]=gt.rules.nInternalActivities;
19425 							_activitiesForIntervalDay[d2].clear();
19426 
19427 							for(int h2=sth; h2<endh; h2++){
19428 								int ai2=teachersTimetable(tch,d2,h2);
19429 								if(ai2>=0){
19430 									if(!conflActivities[newtime].contains(ai2)){
19431 										occupiedIntervalDay[d2]=true;
19432 										if(fixedTimeActivity[ai2] || swappedActivities[ai2])
19433 											canEmptyIntervalDay[d2]=false;
19434 										else if(!_activitiesForIntervalDay[d2].contains(ai2)){
19435 											_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
19436 											_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
19437 											_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
19438 											_nConflActivities[d2]++;
19439 											_activitiesForIntervalDay[d2].append(ai2);
19440 											assert(_nConflActivities[d2]==_activitiesForIntervalDay[d2].count());
19441 										}
19442 									}
19443 								}
19444 							}
19445 
19446 							if(!occupiedIntervalDay[d2])
19447 								canEmptyIntervalDay[d2]=false;
19448 						}
19449 						occupiedIntervalDay[d]=true;
19450 						canEmptyIntervalDay[d]=false;
19451 
19452 						int nOc=0;
19453 						bool canChooseDay=false;
19454 
19455 						for(int j=0; j<gt.rules.nDaysPerWeek; j++)
19456 							if(occupiedIntervalDay[j]){
19457 								nOc++;
19458 								if(canEmptyIntervalDay[j]){
19459 									canChooseDay=true;
19460 								}
19461 							}
19462 
19463 						assert(nOc==maxDays+1);
19464 
19465 						if(!canChooseDay){
19466 							if(level==0){
19467 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
19468 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
19469 							}
19470 							okteachersintervalmaxdaysperweek=false;
19471 							goto impossibleteachersintervalmaxdaysperweek;
19472 						}
19473 
19474 						int d2=-1;
19475 
19476 						////////////////////
19477 						QList<int> candidateDays;
19478 
19479 						int _mW=INF;
19480 						int _nW=INF;
19481 						int _mCA=gt.rules.nInternalActivities;
19482 						int _mIA=gt.rules.nInternalActivities;
19483 
19484 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
19485 							if(canEmptyIntervalDay[kk]){
19486 								if(_mW>_minWrong[kk] ||
19487 								(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
19488 								(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
19489 								(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
19490 									_mW=_minWrong[kk];
19491 									_nW=_nWrong[kk];
19492 									_mCA=_nConflActivities[kk];
19493 									_mIA=_minIndexAct[kk];
19494 								}
19495 							}
19496 
19497 						assert(_mW<INF);
19498 
19499 						candidateDays.clear();
19500 						for(int kk=0; kk<gt.rules.nDaysPerWeek; kk++)
19501 							if(canEmptyIntervalDay[kk])
19502 								if(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
19503 									candidateDays.append(kk);
19504 
19505 						assert(candidateDays.count()>0);
19506 						d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
19507 						///////////////////
19508 
19509 						assert(d2>=0);
19510 
19511 						assert(_activitiesForIntervalDay[d2].count()>0);
19512 
19513 						for(int ai2 : qAsConst(_activitiesForIntervalDay[d2])){
19514 							assert(ai2!=ai);
19515 							assert(!swappedActivities[ai2]);
19516 							assert(!fixedTimeActivity[ai2]);
19517 							assert(!conflActivities[newtime].contains(ai2));
19518 							conflActivities[newtime].append(ai2);
19519 							nConflActivities[newtime]++;
19520 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
19521 							//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
19522 						}
19523 					}
19524 				}
19525 			}
19526 		}
19527 		//respecting teachers interval max days per week
19528 impossibleteachersintervalmaxdaysperweek:
19529 		if(!okteachersintervalmaxdaysperweek){
19530 			if(updateSubgroups || updateTeachers)
19531 				removeAiFromNewTimetable(ai, act, d, h);
19532 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
19533 
19534 			nConflActivities[newtime]=MAX_ACTIVITIES;
19535 			continue;
19536 		}
19537 
19538 		////////////////////////////END teachers interval max days per week
19539 
19540 /////////////////////////////////////////////////////////////////////////////////////////////
19541 
19542 		//BEGIN teachers morning interval max days per week
19543 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
19544 			if(d%2==0){ //d is a morning
19545 				okteachersmorningintervalmaxdaysperweek=true;
19546 				for(int tch : qAsConst(act->iTeachersList)){
19547 					for(int cnt=0; cnt<teachersMorningIntervalMaxDaysPerWeekPercentages[tch].count(); cnt++){
19548 						double perc=teachersMorningIntervalMaxDaysPerWeekPercentages[tch].at(cnt);
19549 						int maxDays=teachersMorningIntervalMaxDaysPerWeekMaxDays[tch].at(cnt);
19550 						int sth=teachersMorningIntervalMaxDaysPerWeekIntervalStart[tch].at(cnt);
19551 						int endh=teachersMorningIntervalMaxDaysPerWeekIntervalEnd[tch].at(cnt);
19552 
19553 						assert(perc>=0);
19554 						assert(sth>=0 && sth<gt.rules.nHoursPerDay);
19555 						assert(endh>sth && endh<=gt.rules.nHoursPerDay);
19556 						assert(maxDays>=0 && maxDays<=gt.rules.nDaysPerWeek/2);
19557 
19558 						if(skipRandom(perc))
19559 							continue;
19560 
19561 						assert(perc==100.0);
19562 
19563 						bool foundothers=false;
19564 						bool foundai=false;
19565 						for(int hh=sth; hh<endh; hh++){
19566 							if(newTeachersTimetable(tch,d,hh)==ai){
19567 								foundai=true;
19568 							}
19569 							else{
19570 								assert(newTeachersTimetable(tch,d,hh)==teachersTimetable(tch,d,hh));
19571 								if(newTeachersTimetable(tch,d,hh)>=0){
19572 									if(!conflActivities[newtime].contains(newTeachersTimetable(tch,d,hh))){
19573 										foundothers=true;
19574 									}
19575 								}
19576 							}
19577 						}
19578 						int nrotherdays=0;
19579 						for(int dd=0; dd<gt.rules.nDaysPerWeek; dd+=2){ //morning
19580 							if(dd!=d){
19581 								for(int hh=sth; hh<endh; hh++){
19582 									assert(newTeachersTimetable(tch,dd,hh)==teachersTimetable(tch,dd,hh));
19583 									if(newTeachersTimetable(tch,dd,hh)>=0 && !conflActivities[newtime].contains(newTeachersTimetable(tch,dd,hh))){
19584 										nrotherdays++;
19585 										break;
19586 									}
19587 								}
19588 							}
19589 						}
19590 						assert(nrotherdays<=maxDays); //if percentage==100%, then it is impossible to break this constraint
19591 						if((foundai && !foundothers) && nrotherdays==maxDays){
19592 							//increased above limit
19593 							//bool occupiedIntervalDay[MAX_DAYS_PER_WEEK];
19594 							//bool canEmptyIntervalDay[MAX_DAYS_PER_WEEK];
19595 
19596 							//int _minWrong[MAX_DAYS_PER_WEEK];
19597 							//int _nWrong[MAX_DAYS_PER_WEEK];
19598 							//int _nConflActivities[MAX_DAYS_PER_WEEK];
19599 							//int _minIndexAct[MAX_DAYS_PER_WEEK];
19600 
19601 							//QList<int> _activitiesForIntervalDay[MAX_DAYS_PER_WEEK];
19602 
19603 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2+=2){ //morning
19604 								if(d2==d)
19605 									continue;
19606 
19607 								occupiedIntervalDay[d2]=false;
19608 								canEmptyIntervalDay[d2]=true;
19609 
19610 								_minWrong[d2]=INF;
19611 								_nWrong[d2]=0;
19612 								_nConflActivities[d2]=0;
19613 								_minIndexAct[d2]=gt.rules.nInternalActivities;
19614 								_activitiesForIntervalDay[d2].clear();
19615 
19616 								for(int h2=sth; h2<endh; h2++){
19617 									int ai2=teachersTimetable(tch,d2,h2);
19618 								//for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
19619 									if(ai2>=0){
19620 										if(!conflActivities[newtime].contains(ai2)){
19621 											occupiedIntervalDay[d2]=true;
19622 											if(fixedTimeActivity[ai2] || swappedActivities[ai2])
19623 												canEmptyIntervalDay[d2]=false;
19624 											else if(!_activitiesForIntervalDay[d2].contains(ai2)){
19625 												_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
19626 												_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
19627 												_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
19628 												_nConflActivities[d2]++;
19629 												_activitiesForIntervalDay[d2].append(ai2);
19630 												assert(_nConflActivities[d2]==_activitiesForIntervalDay[d2].count());
19631 											}
19632 										}
19633 									}
19634 								}
19635 
19636 								if(!occupiedIntervalDay[d2])
19637 									canEmptyIntervalDay[d2]=false;
19638 							}
19639 							occupiedIntervalDay[d]=true;
19640 							canEmptyIntervalDay[d]=false;
19641 
19642 							int nOc=0;
19643 							bool canChooseDay=false;
19644 
19645 							for(int j=0; j<gt.rules.nDaysPerWeek; j+=2) //morning
19646 								if(occupiedIntervalDay[j]){
19647 									nOc++;
19648 									if(canEmptyIntervalDay[j]){
19649 										canChooseDay=true;
19650 									}
19651 								}
19652 
19653 							//if(nOc>maxDays){
19654 							assert(nOc==maxDays+1);
19655 
19656 							if(!canChooseDay){
19657 								if(level==0){
19658 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
19659 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
19660 								}
19661 								okteachersmorningintervalmaxdaysperweek=false;
19662 								goto impossibleteachersmorningintervalmaxdaysperweek;
19663 							}
19664 
19665 							int d2=-1;
19666 
19667 							if(level!=0){
19668 								//choose random day from those with minimum number of conflicting activities
19669 								QList<int> candidateDays;
19670 
19671 								int m=gt.rules.nInternalActivities;
19672 
19673 								for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2) //morning
19674 									if(canEmptyIntervalDay[kk])
19675 										if(m>_nConflActivities[kk])
19676 											m=_nConflActivities[kk];
19677 
19678 								candidateDays.clear();
19679 								for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2) //morning
19680 									if(canEmptyIntervalDay[kk] && m==_nConflActivities[kk])
19681 										candidateDays.append(kk);
19682 
19683 								assert(candidateDays.count()>0);
19684 								d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
19685 							}
19686 							else{ //level==0
19687 								QList<int> candidateDays;
19688 
19689 								int _mW=INF;
19690 								int _nW=INF;
19691 								int _mCA=gt.rules.nInternalActivities;
19692 								int _mIA=gt.rules.nInternalActivities;
19693 
19694 								for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2) //morning
19695 									if(canEmptyIntervalDay[kk]){
19696 										if(_mW>_minWrong[kk] ||
19697 										(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
19698 										(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
19699 										(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
19700 											_mW=_minWrong[kk];
19701 											_nW=_nWrong[kk];
19702 											_mCA=_nConflActivities[kk];
19703 											_mIA=_minIndexAct[kk];
19704 										}
19705 									}
19706 
19707 								assert(_mW<INF);
19708 
19709 								candidateDays.clear();
19710 								for(int kk=0; kk<gt.rules.nDaysPerWeek; kk+=2) //morning
19711 									if(canEmptyIntervalDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
19712 										candidateDays.append(kk);
19713 
19714 								assert(candidateDays.count()>0);
19715 								d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
19716 							}
19717 
19718 							assert(d2>=0);
19719 
19720 							assert(_activitiesForIntervalDay[d2].count()>0);
19721 
19722 							for(int ai2 : qAsConst(_activitiesForIntervalDay[d2])){
19723 								assert(ai2!=ai);
19724 								assert(!swappedActivities[ai2]);
19725 								assert(!fixedTimeActivity[ai2]);
19726 								assert(!conflActivities[newtime].contains(ai2));
19727 								conflActivities[newtime].append(ai2);
19728 								nConflActivities[newtime]++;
19729 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
19730 								//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
19731 							}
19732 						}
19733 					}
19734 				}
19735 				//respecting teachers interval max days per week
19736 impossibleteachersmorningintervalmaxdaysperweek:
19737 				if(!okteachersmorningintervalmaxdaysperweek){
19738 					if(updateSubgroups || updateTeachers)
19739 						removeAiFromNewTimetable(ai, act, d, h);
19740 					//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
19741 
19742 					nConflActivities[newtime]=MAX_ACTIVITIES;
19743 					continue;
19744 				}
19745 			}
19746 		}
19747 
19748 		////////////////////////////END teachers morning interval max days per week
19749 
19750 /////////////////////////////////////////////////////////////////////////////////////////////
19751 
19752 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
19753 			//BEGIN teachers afternoon interval max days per week
19754 			if(d%2==1){ //d is an afternoon
19755 				okteachersafternoonintervalmaxdaysperweek=true;
19756 				for(int tch : qAsConst(act->iTeachersList)){
19757 					for(int cnt=0; cnt<teachersAfternoonIntervalMaxDaysPerWeekPercentages[tch].count(); cnt++){
19758 						double perc=teachersAfternoonIntervalMaxDaysPerWeekPercentages[tch].at(cnt);
19759 						int maxDays=teachersAfternoonIntervalMaxDaysPerWeekMaxDays[tch].at(cnt);
19760 						int sth=teachersAfternoonIntervalMaxDaysPerWeekIntervalStart[tch].at(cnt);
19761 						int endh=teachersAfternoonIntervalMaxDaysPerWeekIntervalEnd[tch].at(cnt);
19762 
19763 						assert(perc>=0);
19764 						assert(sth>=0 && sth<gt.rules.nHoursPerDay);
19765 						assert(endh>sth && endh<=gt.rules.nHoursPerDay);
19766 						assert(maxDays>=0 && maxDays<=gt.rules.nDaysPerWeek/2);
19767 
19768 						if(skipRandom(perc))
19769 							continue;
19770 
19771 						assert(perc==100.0);
19772 
19773 						bool foundothers=false;
19774 						bool foundai=false;
19775 						for(int hh=sth; hh<endh; hh++){
19776 							if(newTeachersTimetable(tch,d,hh)==ai){
19777 								foundai=true;
19778 							}
19779 							else{
19780 								assert(newTeachersTimetable(tch,d,hh)==teachersTimetable(tch,d,hh));
19781 								if(newTeachersTimetable(tch,d,hh)>=0){
19782 									if(!conflActivities[newtime].contains(newTeachersTimetable(tch,d,hh))){
19783 										foundothers=true;
19784 									}
19785 								}
19786 							}
19787 						}
19788 						int nrotherdays=0;
19789 						for(int dd=1; dd<gt.rules.nDaysPerWeek; dd+=2){ //afternoon
19790 							if(dd!=d){
19791 								for(int hh=sth; hh<endh; hh++){
19792 									assert(newTeachersTimetable(tch,dd,hh)==teachersTimetable(tch,dd,hh));
19793 									if(newTeachersTimetable(tch,dd,hh)>=0 && !conflActivities[newtime].contains(newTeachersTimetable(tch,dd,hh))){
19794 										nrotherdays++;
19795 										break;
19796 									}
19797 								}
19798 							}
19799 						}
19800 						assert(nrotherdays<=maxDays); //if percentage==100%, then it is impossible to break this constraint
19801 						if((foundai && !foundothers) && nrotherdays==maxDays){
19802 							//increased above limit
19803 							//bool occupiedIntervalDay[MAX_DAYS_PER_WEEK];
19804 							//bool canEmptyIntervalDay[MAX_DAYS_PER_WEEK];
19805 
19806 							//int _minWrong[MAX_DAYS_PER_WEEK];
19807 							//int _nWrong[MAX_DAYS_PER_WEEK];
19808 							//int _nConflActivities[MAX_DAYS_PER_WEEK];
19809 							//int _minIndexAct[MAX_DAYS_PER_WEEK];
19810 
19811 							//QList<int> _activitiesForIntervalDay[MAX_DAYS_PER_WEEK];
19812 
19813 							for(int d2=1; d2<gt.rules.nDaysPerWeek; d2+=2){ //afternoon
19814 								if(d2==d)
19815 									continue;
19816 
19817 								occupiedIntervalDay[d2]=false;
19818 								canEmptyIntervalDay[d2]=true;
19819 
19820 								_minWrong[d2]=INF;
19821 								_nWrong[d2]=0;
19822 								_nConflActivities[d2]=0;
19823 								_minIndexAct[d2]=gt.rules.nInternalActivities;
19824 								_activitiesForIntervalDay[d2].clear();
19825 
19826 								for(int h2=sth; h2<endh; h2++){
19827 									int ai2=teachersTimetable(tch,d2,h2);
19828 								//for(int ai2 : qAsConst(teacherActivitiesOfTheDay(tch,d2))){
19829 									if(ai2>=0){
19830 										if(!conflActivities[newtime].contains(ai2)){
19831 											occupiedIntervalDay[d2]=true;
19832 											if(fixedTimeActivity[ai2] || swappedActivities[ai2])
19833 												canEmptyIntervalDay[d2]=false;
19834 											else if(!_activitiesForIntervalDay[d2].contains(ai2)){
19835 												_minWrong[d2] = min (_minWrong[d2], triedRemovals(ai2,c.times[ai2]));
19836 												_minIndexAct[d2]=min(_minIndexAct[d2], invPermutation[ai2]);
19837 												_nWrong[d2]+=triedRemovals(ai2,c.times[ai2]);
19838 												_nConflActivities[d2]++;
19839 												_activitiesForIntervalDay[d2].append(ai2);
19840 												assert(_nConflActivities[d2]==_activitiesForIntervalDay[d2].count());
19841 											}
19842 										}
19843 									}
19844 								}
19845 
19846 								if(!occupiedIntervalDay[d2])
19847 									canEmptyIntervalDay[d2]=false;
19848 							}
19849 							occupiedIntervalDay[d]=true;
19850 							canEmptyIntervalDay[d]=false;
19851 
19852 							int nOc=0;
19853 							bool canChooseDay=false;
19854 
19855 							for(int j=1; j<gt.rules.nDaysPerWeek; j+=2) //afternoon
19856 								if(occupiedIntervalDay[j]){
19857 									nOc++;
19858 									if(canEmptyIntervalDay[j]){
19859 										canChooseDay=true;
19860 									}
19861 								}
19862 
19863 							//if(nOc>maxDays){
19864 							assert(nOc==maxDays+1);
19865 
19866 							if(!canChooseDay){
19867 								if(level==0){
19868 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
19869 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
19870 								}
19871 								okteachersafternoonintervalmaxdaysperweek=false;
19872 								goto impossibleteachersafternoonintervalmaxdaysperweek;
19873 							}
19874 
19875 							int d2=-1;
19876 
19877 							if(level!=0){
19878 								//choose random day from those with minimum number of conflicting activities
19879 								QList<int> candidateDays;
19880 
19881 								int m=gt.rules.nInternalActivities;
19882 
19883 								for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2) //afternoon
19884 									if(canEmptyIntervalDay[kk])
19885 										if(m>_nConflActivities[kk])
19886 											m=_nConflActivities[kk];
19887 
19888 								candidateDays.clear();
19889 								for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2) //afternoon
19890 									if(canEmptyIntervalDay[kk] && m==_nConflActivities[kk])
19891 										candidateDays.append(kk);
19892 
19893 								assert(candidateDays.count()>0);
19894 								d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
19895 							}
19896 							else{ //level==0
19897 								QList<int> candidateDays;
19898 
19899 								int _mW=INF;
19900 								int _nW=INF;
19901 								int _mCA=gt.rules.nInternalActivities;
19902 								int _mIA=gt.rules.nInternalActivities;
19903 
19904 								for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2) //afternoon
19905 									if(canEmptyIntervalDay[kk]){
19906 										if(_mW>_minWrong[kk] ||
19907 										(_mW==_minWrong[kk] && _nW>_nWrong[kk]) ||
19908 										(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA>_nConflActivities[kk]) ||
19909 										(_mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA>_minIndexAct[kk])){
19910 											_mW=_minWrong[kk];
19911 											_nW=_nWrong[kk];
19912 											_mCA=_nConflActivities[kk];
19913 											_mIA=_minIndexAct[kk];
19914 										}
19915 									}
19916 
19917 								assert(_mW<INF);
19918 
19919 								candidateDays.clear();
19920 								for(int kk=1; kk<gt.rules.nDaysPerWeek; kk+=2) //afternoon
19921 									if(canEmptyIntervalDay[kk] && _mW==_minWrong[kk] && _nW==_nWrong[kk] && _mCA==_nConflActivities[kk] && _mIA==_minIndexAct[kk])
19922 										candidateDays.append(kk);
19923 
19924 								assert(candidateDays.count()>0);
19925 								d2=candidateDays.at(rng.intMRG32k3a(candidateDays.count()));
19926 							}
19927 
19928 							assert(d2>=0);
19929 
19930 							assert(_activitiesForIntervalDay[d2].count()>0);
19931 
19932 							for(int ai2 : qAsConst(_activitiesForIntervalDay[d2])){
19933 								assert(ai2!=ai);
19934 								assert(!swappedActivities[ai2]);
19935 								assert(!fixedTimeActivity[ai2]);
19936 								assert(!conflActivities[newtime].contains(ai2));
19937 								conflActivities[newtime].append(ai2);
19938 								nConflActivities[newtime]++;
19939 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
19940 								//addConflActivity(conflActivities[newtime], nConflActivities[newtime], ai2, &gt.rules.internalActivitiesList[ai2]);
19941 							}
19942 						}
19943 					}
19944 				}
19945 				//respecting teachers interval max days per week
19946 impossibleteachersafternoonintervalmaxdaysperweek:
19947 				if(!okteachersafternoonintervalmaxdaysperweek){
19948 					if(updateSubgroups || updateTeachers)
19949 						removeAiFromNewTimetable(ai, act, d, h);
19950 					//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
19951 
19952 					nConflActivities[newtime]=MAX_ACTIVITIES;
19953 					continue;
19954 				}
19955 			}
19956 		}
19957 
19958 		////////////////////////////END teachers afternoon interval max days per week
19959 
19960 /////////////////////////////////////////////////////////////////////////////////////////////
19961 
19962 		////////////////////////////BEGIN teachers max span per day
19963 
19964 		//Rodolfo Ribeiro Gomes's code (https://bitbucket.org/rodolforg/fet/src/dev/) was a source of inspiration for the following constraint
19965 
19966 		okteachersmaxspanperday=true;
19967 		for(int tch : qAsConst(act->iTeachersList))
19968 			if(teachersMaxSpanPerDayPercentages[tch]>=0){
19969 				//percentage is 100%
19970 				int maxSpanPerDay=teachersMaxSpanPerDayMaxSpan[tch];
19971 				bool allowException=teachersMaxSpanPerDayAllowOneDayExceptionPlusOne[tch];
19972 
19973 				//preliminary test
19974 				int _cnt=0;
19975 				int _start=-1;
19976 				int _end=-1;
19977 				for(int h2=0; h2<gt.rules.nHoursPerDay; h2++)
19978 					if(newTeachersTimetable(tch, d, h2)>=0){
19979 						_start=h2;
19980 						break;
19981 					}
19982 				for(int h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
19983 					if(newTeachersTimetable(tch, d, h2)>=0){
19984 						_end=h2;
19985 						break;
19986 					}
19987 
19988 				if(_start>=0 && _end>=0 && _end>=_start)
19989 					_cnt=_end-_start+1;
19990 
19991 				if(_cnt<=maxSpanPerDay)
19992 					continue;
19993 
19994 				int dayOldException=-2;
19995 				if(/*_cnt==maxSpanPerDay+1 &&*/ allowException){
19996 					if(_cnt==maxSpanPerDay+1)
19997 						dayOldException=-1;
19998 					else
19999 						dayOldException=-3;
20000 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
20001 						if(d2==d)
20002 							continue;
20003 
20004 						int _cnt3=0;
20005 						int _start3=-1;
20006 						int _end3=-1;
20007 						for(int h2=0; h2<gt.rules.nHoursPerDay; h2++)
20008 							if(newTeachersTimetable(tch, d2, h2)>=0){
20009 								_start3=h2;
20010 								break;
20011 							}
20012 						for(int h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
20013 							if(newTeachersTimetable(tch, d2, h2)>=0){
20014 								_end3=h2;
20015 								break;
20016 							}
20017 
20018 						if(_start3>=0 && _end3>=0 && _end3>=_start3)
20019 							_cnt3=_end3-_start3+1;
20020 
20021 						assert(_cnt3<=maxSpanPerDay+1);
20022 						if(_cnt3==maxSpanPerDay+1){
20023 							assert(dayOldException==-1 || dayOldException==-3);
20024 							dayOldException=d2;
20025 						}
20026 					}
20027 				}
20028 				if(dayOldException==-1) //OK
20029 					continue;
20030 
20031 				if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
20032 					okteachersmaxspanperday=false;
20033 					goto impossibleteachersmaxspanperday;
20034 				}
20035 
20036 				getTchTimetable(tch, conflActivities[newtime]);
20037 				updateTchNHoursGaps(tch, d); //needed for teacherRemoveAnActivityFromBeginOrEndCertainDay below
20038 				if(dayOldException>=0)
20039 					updateTchNHoursGaps(tch, dayOldException); //needed for teacherRemoveAnActivityFromBeginOrEndCertainDay below
20040 
20041 				for(;;){
20042 					int cnt=0;
20043 					int start=-1;
20044 					int end=-1;
20045 					for(int h2=0; h2<gt.rules.nHoursPerDay; h2++)
20046 						if(tchTimetable(d, h2)>=0){
20047 							start=h2;
20048 							break;
20049 						}
20050 					for(int h2=gt.rules.nHoursPerDay-1; h2>=0; h2--)
20051 						if(tchTimetable(d, h2)>=0){
20052 							end=h2;
20053 							break;
20054 						}
20055 
20056 					if(start>=0 && end>=0 && end>=start)
20057 						cnt=end-start+1;
20058 
20059 					if(cnt<=maxSpanPerDay || (cnt==maxSpanPerDay+1 && dayOldException==-1))
20060 						break;
20061 
20062 					int ai2=-1;
20063 
20064 					if(cnt>=maxSpanPerDay+2 || !allowException || dayOldException==-3){
20065 						bool k=teacherRemoveAnActivityFromBeginOrEndCertainDay(d, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20066 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20067 						if(!k){
20068 							if(level==0){
20069 								//old comment below
20070 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
20071 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
20072 							}
20073 							okteachersmaxspanperday=false;
20074 							goto impossibleteachersmaxspanperday;
20075 						}
20076 
20077 						assert(ai2>=0);
20078 
20079 						removeAi2FromTchTimetable(ai2);
20080 						tchDayNHours[d]-=gt.rules.internalActivitiesList[ai2].duration; //needed for teacherRemoveAnActivityFromBeginOrEndCertainDay above
20081 						assert(tchDayNHours[d]>=0);
20082 					}
20083 					else{
20084 						assert(allowException);
20085 						assert(cnt==maxSpanPerDay+1);
20086 
20087 						assert(dayOldException>=0);
20088 
20089 						//int removedDayOldException=-1;
20090 
20091 						if(rng.intMRG32k3a(2)==0){
20092 							bool k=teacherRemoveAnActivityFromBeginOrEndCertainDay(d, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20093 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20094 							if(!k){
20095 								bool k2=teacherRemoveAnActivityFromBeginOrEndCertainDay(dayOldException, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20096 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20097 								if(!k2){
20098 									if(level==0){
20099 										//old comment below
20100 										//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
20101 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
20102 									}
20103 									okteachersmaxspanperday=false;
20104 									goto impossibleteachersmaxspanperday;
20105 								}
20106 								else{
20107 									/*removedDayOldException=dayOldException;
20108 									dayOldException=-1;*/
20109 								}
20110 							}
20111 
20112 							assert(ai2>=0);
20113 
20114 							break; //OK
20115 
20116 							/*removeAi2FromTchTimetable(ai2);
20117 							if(removedDayOldException==-1){
20118 								assert(c.times[ai2]%gt.rules.nDaysPerWeek==d);
20119 								tchDayNHours[d]-=gt.rules.internalActivitiesList[ai2].duration; //needed for teacherRemoveAnActivityFromBeginOrEndCertainDay above
20120 								assert(tchDayNHours[d]>=0);
20121 							}
20122 							else{
20123 								assert(c.times[ai2]%gt.rules.nDaysPerWeek==removedDayOldException);
20124 								tchDayNHours[removedDayOldException]-=gt.rules.internalActivitiesList[ai2].duration; //needed for teacherRemoveAnActivityFromBeginOrEndCertainDay above
20125 								assert(tchDayNHours[removedDayOldException]>=0);
20126 							}*/
20127 						}
20128 						else{
20129 							bool k=teacherRemoveAnActivityFromBeginOrEndCertainDay(dayOldException, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20130 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20131 							if(!k){
20132 								bool k2=teacherRemoveAnActivityFromBeginOrEndCertainDay(d, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20133 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20134 								if(!k2){
20135 									if(level==0){
20136 										//old comment below
20137 										//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
20138 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
20139 									}
20140 									okteachersmaxspanperday=false;
20141 									goto impossibleteachersmaxspanperday;
20142 								}
20143 							}
20144 							else{
20145 								/*removedDayOldException=dayOldException;
20146 								dayOldException=-1;*/
20147 							}
20148 
20149 							assert(ai2>=0);
20150 
20151 							break; //OK
20152 
20153 							/*removeAi2FromTchTimetable(ai2);
20154 							if(removedDayOldException==-1){
20155 								assert(c.times[ai2]%gt.rules.nDaysPerWeek==d);
20156 								tchDayNHours[d]-=gt.rules.internalActivitiesList[ai2].duration; //needed for teacherRemoveAnActivityFromBeginOrEndCertainDay above
20157 								assert(tchDayNHours[d]>=0);
20158 							}
20159 							else{
20160 								assert(c.times[ai2]%gt.rules.nDaysPerWeek==removedDayOldException);
20161 								tchDayNHours[removedDayOldException]-=gt.rules.internalActivitiesList[ai2].duration; //needed for teacherRemoveAnActivityFromBeginOrEndCertainDay above
20162 								assert(tchDayNHours[removedDayOldException]>=0);
20163 							}*/
20164 						}
20165 					}
20166 				}
20167 			}
20168 
20169 impossibleteachersmaxspanperday:
20170 		if(!okteachersmaxspanperday){
20171 			if(updateSubgroups || updateTeachers)
20172 				removeAiFromNewTimetable(ai, act, d, h);
20173 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
20174 
20175 			nConflActivities[newtime]=MAX_ACTIVITIES;
20176 			continue;
20177 		}
20178 
20179 		////////////////////////////END teachers max span per day
20180 
20181 /////////////////////////////////////////////////////////////////////////////////////////////
20182 
20183 		////////////////////////////BEGIN teachers max span per real day
20184 
20185 		//Rodolfo Ribeiro Gomes's code (https://bitbucket.org/rodolforg/fet/src/dev/) was a source of inspiration for the following constraint
20186 
20187 		okteachersmaxspanperrealday=true;
20188 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
20189 			for(int tch : qAsConst(act->iTeachersList))
20190 				if(teachersMaxSpanPerRealDayPercentages[tch]>=0){
20191 					//percentage is 100%
20192 					int maxSpanPerDay=teachersMaxSpanPerRealDayMaxSpan[tch];
20193 					bool allowException=teachersMaxSpanPerRealDayAllowOneDayExceptionPlusOne[tch];
20194 
20195 					//preliminary test
20196 					int _cnt=0;
20197 					int _start=-1;
20198 					int _end=-1;
20199 					for(int h2=0; h2<2*gt.rules.nHoursPerDay; h2++){
20200 						int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
20201 						int h3=h2%gt.rules.nHoursPerDay;
20202 						if(newTeachersTimetable(tch, d3, h3)>=0){
20203 							_start=h2;
20204 							break;
20205 						}
20206 					}
20207 					for(int h2=2*gt.rules.nHoursPerDay-1; h2>=0; h2--){
20208 						int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
20209 						int h3=h2%gt.rules.nHoursPerDay;
20210 						if(newTeachersTimetable(tch, d3, h3)>=0){
20211 							_end=h2;
20212 							break;
20213 						}
20214 					}
20215 
20216 					if(_start>=0 && _end>=0 && _end>=_start)
20217 						_cnt=_end-_start+1;
20218 
20219 					if(_cnt<=maxSpanPerDay)
20220 						continue;
20221 
20222 					int dayOldException=-2;
20223 					//cout<<"_cnt=="<<_cnt<<endl;
20224 					//cout<<"maxSpanPerDay=="<<maxSpanPerDay<<endl;
20225 					//cout<<"allowException=="<<allowException<<endl;
20226 					if(/*_cnt==maxSpanPerDay+1 &&*/ allowException){
20227 						if(_cnt==maxSpanPerDay+1)
20228 							dayOldException=-1;
20229 						else
20230 							dayOldException=-3;
20231 						for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
20232 							if(d2==d/2)
20233 								continue;
20234 
20235 							int _cnt3=0;
20236 							int _start3=-1;
20237 							int _end3=-1;
20238 							for(int h2=0; h2<2*gt.rules.nHoursPerDay; h2++){
20239 								int d3=d2*2+(h2<gt.rules.nHoursPerDay?0:1);
20240 								int h3=h2%gt.rules.nHoursPerDay;
20241 								if(newTeachersTimetable(tch, d3, h3)>=0){
20242 									_start3=h2;
20243 									break;
20244 								}
20245 							}
20246 							for(int h2=2*gt.rules.nHoursPerDay-1; h2>=0; h2--){
20247 								int d3=d2*2+(h2<gt.rules.nHoursPerDay?0:1);
20248 								int h3=h2%gt.rules.nHoursPerDay;
20249 								if(newTeachersTimetable(tch, d3, h3)>=0){
20250 									_end3=h2;
20251 									break;
20252 								}
20253 							}
20254 
20255 							if(_start3>=0 && _end3>=0 && _end3>=_start3)
20256 								_cnt3=_end3-_start3+1;
20257 
20258 							assert(_cnt3<=maxSpanPerDay+1);
20259 							if(_cnt3==maxSpanPerDay+1){
20260 								assert(dayOldException==-1 || dayOldException==-3);
20261 								dayOldException=d2;
20262 							}
20263 						}
20264 					}
20265 					if(dayOldException==-1) //OK
20266 						continue;
20267 
20268 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
20269 						okteachersmaxspanperrealday=false;
20270 						goto impossibleteachersmaxspanperrealday;
20271 					}
20272 
20273 					getTchTimetable(tch, conflActivities[newtime]);
20274 					updateTchNHoursGaps(tch, d); //needed for teacherRemoveAnActivityFromBeginOrEndCertainRealDay below
20275 					updateTchNHoursGaps(tch, dpair); //needed for teacherRemoveAnActivityFromBeginOrEndCertainRealDay below
20276 					if(dayOldException>=0){
20277 						updateTchNHoursGaps(tch, dayOldException*2); //needed for teacherRemoveAnActivityFromBeginOrEndCertainRealDay below
20278 						updateTchNHoursGaps(tch, dayOldException*2+1); //needed for teacherRemoveAnActivityFromBeginOrEndCertainRealDay below
20279 					}
20280 
20281 					for(;;){
20282 						int cnt=0;
20283 						int start=-1;
20284 						int end=-1;
20285 
20286 						for(int h2=0; h2<2*gt.rules.nHoursPerDay; h2++){
20287 							int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
20288 							int h3=h2%gt.rules.nHoursPerDay;
20289 							if(tchTimetable(d3, h3)>=0){
20290 								start=h2;
20291 								break;
20292 							}
20293 						}
20294 						for(int h2=2*gt.rules.nHoursPerDay-1; h2>=0; h2--){
20295 							int d3=(d/2)*2+(h2<gt.rules.nHoursPerDay?0:1);
20296 							int h3=h2%gt.rules.nHoursPerDay;
20297 							if(tchTimetable(d3, h3)>=0){
20298 								end=h2;
20299 								break;
20300 							}
20301 						}
20302 
20303 						if(start>=0 && end>=0 && end>=start)
20304 							cnt=end-start+1;
20305 
20306 						if(cnt<=maxSpanPerDay || (cnt==maxSpanPerDay+1 && dayOldException==-1))
20307 							break;
20308 
20309 						int ai2=-1;
20310 
20311 						if(cnt>=maxSpanPerDay+2 || !allowException || dayOldException==-3){
20312 							bool k=teacherRemoveAnActivityFromBeginOrEndCertainRealDay(d/2, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20313 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20314 							if(!k){
20315 								if(level==0){
20316 									//old comment below
20317 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
20318 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
20319 								}
20320 								okteachersmaxspanperrealday=false;
20321 								goto impossibleteachersmaxspanperrealday;
20322 							}
20323 
20324 							assert(ai2>=0);
20325 
20326 							/*cout<<endl;
20327 							cout<<"1"<<endl;
20328 							cout<<"d2=="<<c.times[ai2]%gt.rules.nDaysPerWeek<<endl;
20329 							cout<<"tchDayNHours[d2]=="<<tchDayNHours[c.times[ai2]%gt.rules.nDaysPerWeek]<<endl;
20330 							cout<<"ai2=="<<ai2<<", id of ai2 == "<<gt.rules.internalActivitiesList[ai2].id
20331 							 <<", duration of ai2 == "<<gt.rules.internalActivitiesList[ai2].duration<<endl;*/
20332 
20333 							int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
20334 							removeAi2FromTchTimetable(ai2);
20335 							tchDayNHours[d2]-=gt.rules.internalActivitiesList[ai2].duration; //needed for teacherRemoveAnActivityFromBeginOrEndCertainRealDay above
20336 							assert(tchDayNHours[d2]>=0);
20337 						}
20338 						else{
20339 							assert(allowException);
20340 							assert(cnt==maxSpanPerDay+1);
20341 
20342 							//cout<<"dayOldException=="<<dayOldException<<endl;
20343 							//cout<<"cnt=="<<cnt<<endl;
20344 							//cout<<"maxSpanPerDay=="<<maxSpanPerDay<<endl;
20345 							//cout<<"allowException=="<<allowException<<endl;
20346 
20347 							assert(dayOldException>=0);
20348 
20349 							//int removedDayOldException=-1;
20350 
20351 							if(rng.intMRG32k3a(2)==0){
20352 								bool k=teacherRemoveAnActivityFromBeginOrEndCertainRealDay(d/2, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20353 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20354 								if(!k){
20355 									bool k2=teacherRemoveAnActivityFromBeginOrEndCertainRealDay(dayOldException, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20356 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20357 									if(!k2){
20358 										if(level==0){
20359 											//old comment below
20360 											//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
20361 											//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
20362 										}
20363 										okteachersmaxspanperrealday=false;
20364 										goto impossibleteachersmaxspanperrealday;
20365 									}
20366 									else{
20367 										/*removedDayOldException=dayOldException;
20368 										dayOldException=-1;*/
20369 									}
20370 								}
20371 
20372 								//Q_UNUSED(removedDayOldException);
20373 
20374 								assert(ai2>=0);
20375 
20376 								break; //OK
20377 
20378 								/*cout<<endl;
20379 								cout<<"2"<<endl;
20380 								cout<<"d2=="<<c.times[ai2]%gt.rules.nDaysPerWeek<<endl;
20381 								cout<<"tchDayNHours[d2]=="<<tchDayNHours[c.times[ai2]%gt.rules.nDaysPerWeek]<<endl;
20382 								cout<<"ai2=="<<ai2<<", id of ai2 == "<<gt.rules.internalActivitiesList[ai2].id
20383 								 <<", duration of ai2 == "<<gt.rules.internalActivitiesList[ai2].duration<<endl;*/
20384 
20385 								/*int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
20386 								removeAi2FromTchTimetable(ai2);
20387 								tchDayNHours[d2]-=gt.rules.internalActivitiesList[ai2].duration; //needed for teacherRemoveAnActivityFromBeginOrEndCertainRealDay above
20388 								assert(tchDayNHours[d2]>=0);*/
20389 							}
20390 							else{
20391 								bool k=teacherRemoveAnActivityFromBeginOrEndCertainRealDay(dayOldException, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20392 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20393 								if(!k){
20394 									bool k2=teacherRemoveAnActivityFromBeginOrEndCertainRealDay(d/2, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20395 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20396 									if(!k2){
20397 										if(level==0){
20398 											//old comment below
20399 											//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
20400 											//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
20401 										}
20402 										okteachersmaxspanperrealday=false;
20403 										goto impossibleteachersmaxspanperrealday;
20404 									}
20405 								}
20406 								else{
20407 									/*removedDayOldException=dayOldException;
20408 									dayOldException=-1;*/
20409 								}
20410 
20411 								//Q_UNUSED(removedDayOldException);
20412 
20413 								assert(ai2>=0);
20414 
20415 								break; //OK
20416 
20417 								/*cout<<endl;
20418 								cout<<"3"<<endl;
20419 								cout<<"d2=="<<c.times[ai2]%gt.rules.nDaysPerWeek<<endl;
20420 								cout<<"tchDayNHours[d2]=="<<tchDayNHours[c.times[ai2]%gt.rules.nDaysPerWeek]<<endl;
20421 								cout<<"ai2=="<<ai2<<", id of ai2 == "<<gt.rules.internalActivitiesList[ai2].id
20422 								 <<", duration of ai2 == "<<gt.rules.internalActivitiesList[ai2].duration<<endl;*/
20423 
20424 								/*int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
20425 								removeAi2FromTchTimetable(ai2);
20426 								tchDayNHours[d2]-=gt.rules.internalActivitiesList[ai2].duration; //needed for teacherRemoveAnActivityFromBeginOrEndCertainRealDay above
20427 								assert(tchDayNHours[d2]>=0);*/
20428 							}
20429 						}
20430 					}
20431 				}
20432 			//cout<<"exit"<<endl;
20433 		}
20434 
20435 impossibleteachersmaxspanperrealday:
20436 		if(!okteachersmaxspanperrealday){
20437 			if(updateSubgroups || updateTeachers)
20438 				removeAiFromNewTimetable(ai, act, d, h);
20439 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
20440 
20441 			nConflActivities[newtime]=MAX_ACTIVITIES;
20442 			continue;
20443 		}
20444 
20445 		////////////////////////////END teachers max span per real day
20446 
20447 /////////////////////////////////////////////////////////////////////////////////////////////
20448 
20449 		////////////////////////////BEGIN teachers min resting hours
20450 
20451 		//Rodolfo Ribeiro Gomes's code (https://bitbucket.org/rodolforg/fet/src/dev/) was a source of inspiration for the following constraint
20452 
20453 		okteachersminrestinghours=true;
20454 
20455 		for(int tch : qAsConst(act->iTeachersList)){
20456 			for(int qq=0; qq<2; qq++){
20457 				double percentage;
20458 				int minRestingHours;
20459 				bool circular;
20460 				if(qq==0){
20461 					percentage=teachersMinRestingHoursCircularPercentages[tch];
20462 					minRestingHours=teachersMinRestingHoursCircularMinHours[tch];
20463 					circular=true;
20464 				}
20465 				else{
20466 					assert(qq==1);
20467 					percentage=teachersMinRestingHoursNotCircularPercentages[tch];
20468 					minRestingHours=teachersMinRestingHoursNotCircularMinHours[tch];
20469 					circular=false;
20470 				}
20471 				if(percentage>=0){
20472 					//percentage is 100%
20473 					assert(minRestingHours<=gt.rules.nHoursPerDay);
20474 
20475 					//phase 1 - activity is at the end of the day
20476 					int _cnt1=0;
20477 					int _cnt1d=0;
20478 					if(d <= gt.rules.nDaysPerWeek-2+(circular?1:0) && h+act->duration-1 >= gt.rules.nHoursPerDay-minRestingHours){
20479 						for(int h2=gt.rules.nHoursPerDay-1; h2>h+act->duration-1; h2--){
20480 							int ai2=newTeachersTimetable(tch, d, h2);
20481 							if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20482 								break;
20483 							else
20484 								_cnt1++;
20485 						}
20486 						_cnt1d=_cnt1;
20487 						for(int h2=0; h2<minRestingHours-_cnt1d; h2++){
20488 							int ai2=newTeachersTimetable(tch, (d+1<=gt.rules.nDaysPerWeek-1?d+1:0), h2);
20489 							if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20490 								break;
20491 							else
20492 								_cnt1++;
20493 						}
20494 					}
20495 					else
20496 						_cnt1=minRestingHours;
20497 
20498 					//phase 2 - activity is at the beginning of the day
20499 					int _cnt2=0;
20500 					int _cnt2d=0;
20501 					if(d>=1-(circular?1:0) && h<=minRestingHours-1){
20502 						for(int h2=0; h2<h; h2++){
20503 							int ai2=newTeachersTimetable(tch, d, h2);
20504 							if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20505 								break;
20506 							else
20507 								_cnt2++;
20508 						}
20509 						_cnt2d=_cnt2;
20510 						for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d; h2--){
20511 							int ai2=newTeachersTimetable(tch, (d-1>=0?d-1:gt.rules.nDaysPerWeek-1), h2);
20512 							if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20513 								break;
20514 							else
20515 								_cnt2++;
20516 						}
20517 					}
20518 					else
20519 						_cnt2=minRestingHours;
20520 
20521 					if(_cnt1<minRestingHours){
20522 						QList<int> removableActs;
20523 						/*for(int h2=gt.rules.nHoursPerDay-minRestingHours; h2<gt.rules.nHoursPerDay; h2++){
20524 							int ai2=newTeachersTimetable(tch, d, h2);
20525 							if(ai2>=0 && ai2!=ai && !removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2] && !conflActivities[newtime].contains(ai2))
20526 								removableActs.append(ai2);
20527 						}*/
20528 						for(int h2=0; h2<minRestingHours-_cnt1d; h2++){
20529 							int ai2=newTeachersTimetable(tch, (d+1<=gt.rules.nDaysPerWeek-1?d+1:0), h2);
20530 							if(ai2>=0 && !removableActs.contains(ai2) && !conflActivities[newtime].contains(ai2)){
20531 								if(!fixedTimeActivity[ai2] && !swappedActivities[ai2])
20532 									removableActs.append(ai2);
20533 								else
20534 									break;
20535 							}
20536 						}
20537 
20538 						for(;;){
20539 							if(removableActs.count()==0){
20540 								okteachersminrestinghours=false;
20541 								goto impossibleteachersminrestinghours;
20542 							}
20543 
20544 							int ai2=removableActs.at(0);
20545 
20546 							int t=removableActs.removeAll(ai2);
20547 							assert(t==1);
20548 
20549 							assert(!conflActivities[newtime].contains(ai2));
20550 							conflActivities[newtime].append(ai2);
20551 							nConflActivities[newtime]++;
20552 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20553 
20554 							int cnt1=0;
20555 							if(d <= gt.rules.nDaysPerWeek-2+(circular?1:0) && h+act->duration-1 >= gt.rules.nHoursPerDay-minRestingHours){
20556 								/*for(int h2=gt.rules.nHoursPerDay-1; h2>h+act->duration-1; h2--){
20557 									int ai2=newTeachersTimetable(tch, d, h2);
20558 									if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20559 										break;
20560 									else
20561 										cnt1++;
20562 								}*/
20563 								cnt1+=_cnt1d;
20564 								for(int h2=0; h2<minRestingHours-_cnt1d; h2++){
20565 									int ai2=newTeachersTimetable(tch, (d+1<=gt.rules.nDaysPerWeek-1?d+1:0), h2);
20566 									if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20567 										break;
20568 									else
20569 										cnt1++;
20570 								}
20571 							}
20572 							else{
20573 								assert(0);
20574 							}
20575 
20576 							assert(cnt1>_cnt1);
20577 							_cnt1=cnt1;
20578 
20579 							if(cnt1>=minRestingHours)
20580 								break;
20581 						}
20582 					}
20583 					if(_cnt2<minRestingHours){
20584 						QList<int> removableActs;
20585 						for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d; h2--){
20586 							int ai2=newTeachersTimetable(tch, (d-1>=0?d-1:gt.rules.nDaysPerWeek-1), h2);
20587 							if(ai2>=0 && !removableActs.contains(ai2) && !conflActivities[newtime].contains(ai2)){
20588 								if(!fixedTimeActivity[ai2] && !swappedActivities[ai2])
20589 									removableActs.append(ai2);
20590 								else
20591 									break;
20592 							}
20593 						}
20594 						/*for(int h2=0; h2<minRestingHours; h2++){
20595 							int ai2=newTeachersTimetable(tch, d, h2);
20596 							if(ai2>=0 && ai2!=ai && !removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2] && !conflActivities[newtime].contains(ai2))
20597 								removableActs.append(ai2);
20598 						}*/
20599 
20600 						for(;;){
20601 							if(removableActs.count()==0){
20602 								okteachersminrestinghours=false;
20603 								goto impossibleteachersminrestinghours;
20604 							}
20605 
20606 							int ai2=removableActs.at(0);
20607 
20608 							int t=removableActs.removeAll(ai2);
20609 							assert(t==1);
20610 
20611 							assert(!conflActivities[newtime].contains(ai2));
20612 							conflActivities[newtime].append(ai2);
20613 							nConflActivities[newtime]++;
20614 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20615 
20616 							int cnt2=0;
20617 							if(d>=1-(circular?1:0) && h<minRestingHours){
20618 								/*for(int h2=0; h2<h; h2++){
20619 									int ai2=newTeachersTimetable(tch, d, h2);
20620 									if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20621 										break;
20622 									else
20623 										cnt2++;
20624 								}*/
20625 								cnt2+=_cnt2d;
20626 								for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d; h2--){
20627 									int ai2=newTeachersTimetable(tch, (d-1>=0?d-1:gt.rules.nDaysPerWeek-1), h2);
20628 									if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20629 										break;
20630 									else
20631 										cnt2++;
20632 								}
20633 							}
20634 							else{
20635 								assert(0);
20636 							}
20637 
20638 							assert(cnt2>_cnt2);
20639 							_cnt2=cnt2;
20640 
20641 							if(cnt2>=minRestingHours)
20642 								break;
20643 						}
20644 					}
20645 				}
20646 			}
20647 		}
20648 
20649 impossibleteachersminrestinghours:
20650 		if(!okteachersminrestinghours){
20651 			if(updateSubgroups || updateTeachers)
20652 				removeAiFromNewTimetable(ai, act, d, h);
20653 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
20654 
20655 			nConflActivities[newtime]=MAX_ACTIVITIES;
20656 			continue;
20657 		}
20658 
20659 		////////////////////////////END teachers min resting hours
20660 
20661 /////////////////////////////////////////////////////////////////////////////////////////////
20662 
20663 		////////////////////////////BEGIN teachers min resting hours between morning and afternoon
20664 
20665 		//Rodolfo Ribeiro Gomes's code (https://bitbucket.org/rodolforg/fet/src/dev/) was a source of inspiration for the following constraint
20666 
20667 		okteachersminrestinghoursbetweenmorningandafternoon=true;
20668 
20669 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
20670 			for(int tch : qAsConst(act->iTeachersList)){
20671 				double percentage=teachersMinRestingHoursBetweenMorningAndAfternoonPercentages[tch];
20672 				int minRestingHours=teachersMinRestingHoursBetweenMorningAndAfternoonMinHours[tch];
20673 
20674 				if(percentage>=0){
20675 					assert(percentage==100.0);
20676 					assert(minRestingHours<=2*gt.rules.nHoursPerDay);
20677 
20678 					if(d%2==0){ //morning
20679 						int _cnt1=0;
20680 						int _cnt1d=0;
20681 						if(h+act->duration-1 >= gt.rules.nHoursPerDay-minRestingHours){
20682 							for(int h2=gt.rules.nHoursPerDay-1; h2>h+act->duration-1; h2--){
20683 								int ai2=newTeachersTimetable(tch, d, h2);
20684 								if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20685 									break;
20686 								else
20687 									_cnt1++;
20688 							}
20689 							_cnt1d=_cnt1;
20690 							for(int h2=0; h2<minRestingHours-_cnt1d && h2<gt.rules.nHoursPerDay; h2++){
20691 								int ai2=newTeachersTimetable(tch, d+1, h2);
20692 								if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20693 									break;
20694 								else
20695 									_cnt1++;
20696 							}
20697 						}
20698 						else
20699 							_cnt1=minRestingHours;
20700 
20701 						if(_cnt1<minRestingHours){
20702 							QList<int> removableActs;
20703 							/*for(int h2=gt.rules.nHoursPerDay-minRestingHours; h2<gt.rules.nHoursPerDay; h2++){
20704 								int ai2=newTeachersTimetable(tch, d, h2);
20705 								if(ai2>=0 && ai2!=ai && !removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2] && !conflActivities[newtime].contains(ai2))
20706 									removableActs.append(ai2);
20707 							}*/
20708 							for(int h2=0; h2<minRestingHours-_cnt1d && h2<gt.rules.nHoursPerDay; h2++){
20709 								int ai2=newTeachersTimetable(tch, d+1, h2);
20710 								if(ai2>=0 && !removableActs.contains(ai2) && !conflActivities[newtime].contains(ai2)){
20711 									if(!fixedTimeActivity[ai2] && !swappedActivities[ai2])
20712 										removableActs.append(ai2);
20713 									else
20714 										break;
20715 								}
20716 							}
20717 
20718 							for(;;){
20719 								if(removableActs.count()==0){
20720 									okteachersminrestinghoursbetweenmorningandafternoon=false;
20721 									goto impossibleteachersminrestinghoursbetweenmorningandafternoon;
20722 								}
20723 
20724 								int ai2=removableActs.at(0);
20725 
20726 								int t=removableActs.removeAll(ai2);
20727 								assert(t==1);
20728 
20729 								assert(!conflActivities[newtime].contains(ai2));
20730 								conflActivities[newtime].append(ai2);
20731 								nConflActivities[newtime]++;
20732 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20733 
20734 								int cnt1=0;
20735 								if(h+act->duration-1 >= gt.rules.nHoursPerDay-minRestingHours){
20736 									/*for(int h2=gt.rules.nHoursPerDay-1; h2>h+act->duration-1; h2--){
20737 										int ai2=newTeachersTimetable(tch, d, h2);
20738 										if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20739 											break;
20740 										else
20741 											cnt1++;
20742 									}*/
20743 									cnt1+=_cnt1d;
20744 									for(int h2=0; h2<minRestingHours-_cnt1d && h2<gt.rules.nHoursPerDay; h2++){
20745 										int ai2=newTeachersTimetable(tch, d+1, h2);
20746 										if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20747 											break;
20748 										else
20749 											cnt1++;
20750 									}
20751 								}
20752 								else{
20753 									assert(0);
20754 								}
20755 
20756 								assert(cnt1>_cnt1);
20757 								_cnt1=cnt1;
20758 
20759 								if(cnt1>=minRestingHours)
20760 									break;
20761 							}
20762 						}
20763 					}
20764 					else{ //afternoon
20765 						assert(d%2==1);
20766 						int _cnt2=0;
20767 						int _cnt2d=0;
20768 						if(h<=minRestingHours-1){
20769 							for(int h2=0; h2<h; h2++){
20770 								int ai2=newTeachersTimetable(tch, d, h2);
20771 								if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20772 									break;
20773 								else
20774 									_cnt2++;
20775 							}
20776 							_cnt2d=_cnt2;
20777 							for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d && h2>=0; h2--){
20778 								int ai2=newTeachersTimetable(tch, d-1, h2);
20779 								if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20780 									break;
20781 								else
20782 									_cnt2++;
20783 							}
20784 						}
20785 						else
20786 							_cnt2=minRestingHours;
20787 
20788 						if(_cnt2<minRestingHours){
20789 							QList<int> removableActs;
20790 							for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d && h2>=0; h2--){
20791 								int ai2=newTeachersTimetable(tch, d-1, h2);
20792 								if(ai2>=0 && !removableActs.contains(ai2) && !conflActivities[newtime].contains(ai2)){
20793 									if(!fixedTimeActivity[ai2] && !swappedActivities[ai2])
20794 										removableActs.append(ai2);
20795 									else
20796 										break;
20797 								}
20798 							}
20799 							/*for(int h2=0; h2<minRestingHours; h2++){
20800 								int ai2=newTeachersTimetable(tch, d, h2);
20801 								if(ai2>=0 && ai2!=ai && !removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2] && !conflActivities[newtime].contains(ai2))
20802 									removableActs.append(ai2);
20803 							}*/
20804 
20805 							for(;;){
20806 								if(removableActs.count()==0){
20807 									okteachersminrestinghoursbetweenmorningandafternoon=false;
20808 									goto impossibleteachersminrestinghoursbetweenmorningandafternoon;
20809 								}
20810 
20811 								int ai2=removableActs.at(0);
20812 
20813 								int t=removableActs.removeAll(ai2);
20814 								assert(t==1);
20815 
20816 								assert(!conflActivities[newtime].contains(ai2));
20817 								conflActivities[newtime].append(ai2);
20818 								nConflActivities[newtime]++;
20819 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20820 
20821 								int cnt2=0;
20822 								if(h<minRestingHours){
20823 									/*for(int h2=0; h2<h; h2++){
20824 										int ai2=newTeachersTimetable(tch, d, h2);
20825 										if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20826 											break;
20827 										else
20828 											cnt2++;
20829 									}*/
20830 									cnt2+=_cnt2d;
20831 									for(int h2=gt.rules.nHoursPerDay-1; h2>=gt.rules.nHoursPerDay-minRestingHours+_cnt2d && h2>=0; h2--){
20832 										int ai2=newTeachersTimetable(tch, d-1, h2);
20833 										if(ai2>=0 && !conflActivities[newtime].contains(ai2))
20834 											break;
20835 										else
20836 											cnt2++;
20837 									}
20838 								}
20839 								else{
20840 									assert(0);
20841 								}
20842 
20843 								assert(cnt2>_cnt2);
20844 								_cnt2=cnt2;
20845 
20846 								if(cnt2>=minRestingHours)
20847 									break;
20848 							}
20849 						}
20850 					}
20851 				}
20852 			}
20853 		}
20854 
20855 impossibleteachersminrestinghoursbetweenmorningandafternoon:
20856 		if(!okteachersminrestinghoursbetweenmorningandafternoon){
20857 			if(updateSubgroups || updateTeachers)
20858 				removeAiFromNewTimetable(ai, act, d, h);
20859 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
20860 
20861 			nConflActivities[newtime]=MAX_ACTIVITIES;
20862 			continue;
20863 		}
20864 
20865 		////////////////////////////END teachers min resting hours between morning and afternoon
20866 
20867 /////////////////////////////////////////////////////////////////////////////////////////////
20868 
20869 		//not causing more than teachersMaxGapsPerWeek teachers gaps
20870 		okteachersmaxgapsperweek=true;
20871 		for(int tch : qAsConst(act->iTeachersList))
20872 			if(!skipRandom(teachersMaxGapsPerWeekPercentage[tch])){
20873 				assert(teachersMaxGapsPerWeekPercentage[tch]==100);
20874 
20875 				//preliminary test
20876 				int _nHours=0;
20877 				int _nGaps=0;
20878 				for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
20879 					_nHours+=newTeachersDayNHours(tch,d2);
20880 					_nGaps+=newTeachersDayNGaps(tch,d2);
20881 				}
20882 
20883 				if(_nGaps+_nHours > teachersMaxGapsPerWeekMaxGaps[tch]+nHoursPerTeacher[tch]){
20884 
20885 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
20886 						okteachersmaxgapsperweek=false;
20887 						goto impossibleteachersmaxgapsperweek;
20888 					}
20889 
20890 					getTchTimetable(tch, conflActivities[newtime]);
20891 					tchGetNHoursGaps(tch);
20892 
20893 					for(;;){
20894 						int nHours=0;
20895 						int nGaps=0;
20896 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
20897 							nHours+=tchDayNHours[d2];
20898 							nGaps+=tchDayNGaps[d2];
20899 						}
20900 
20901 						int ai2=-1;
20902 
20903 						if(nGaps+nHours > teachersMaxGapsPerWeekMaxGaps[tch]+nHoursPerTeacher[tch]){
20904 							//remove an activity
20905 							bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20906 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20907 							if(!k){
20908 								if(level==0){
20909 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
20910 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
20911 								}
20912 								okteachersmaxgapsperweek=false;
20913 								goto impossibleteachersmaxgapsperweek;
20914 							}
20915 						}
20916 						else{ //OK
20917 							break;
20918 						}
20919 
20920 						assert(ai2>=0);
20921 
20922 						removeAi2FromTchTimetable(ai2);
20923 						updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
20924 					}
20925 				}
20926 			}
20927 
20928 impossibleteachersmaxgapsperweek:
20929 		if(!okteachersmaxgapsperweek){
20930 			if(updateSubgroups || updateTeachers)
20931 				removeAiFromNewTimetable(ai, act, d, h);
20932 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
20933 
20934 			nConflActivities[newtime]=MAX_ACTIVITIES;
20935 			continue;
20936 		}
20937 
20938 		////////////////////////////END max gaps per week
20939 
20940 /////////////////////////////////////////////////////////////////////////////////////////////
20941 
20942 		//not causing more than teachersMaxGapsPerDay teachers gaps
20943 		okteachersmaxgapsperday=true;
20944 		if(gt.rules.mode!=MORNINGS_AFTERNOONS){
20945 			for(int tch : qAsConst(act->iTeachersList))
20946 				if(!skipRandom(teachersMaxGapsPerDayPercentage[tch])){
20947 					assert(teachersMaxGapsPerDayPercentage[tch]==100);
20948 
20949 					//preliminary test
20950 					int _total=0;
20951 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
20952 						_total+=newTeachersDayNHours(tch,d2);
20953 						if(teachersMaxGapsPerDayMaxGaps[tch]<newTeachersDayNGaps(tch,d2))
20954 							_total+=newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch];
20955 					}
20956 					if(_total<=nHoursPerTeacher[tch]) //OK
20957 						continue;
20958 
20959 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
20960 						okteachersmaxgapsperday=false;
20961 						goto impossibleteachersmaxgapsperday;
20962 					}
20963 
20964 					getTchTimetable(tch, conflActivities[newtime]);
20965 					tchGetNHoursGaps(tch);
20966 
20967 					for(;;){
20968 						int total=0;
20969 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
20970 							total+=tchDayNHours[d2];
20971 							if(teachersMaxGapsPerDayMaxGaps[tch]<tchDayNGaps[d2])
20972 								total+=tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch];
20973 						}
20974 						if(total<=nHoursPerTeacher[tch]) //OK
20975 							break;
20976 
20977 						//remove an activity from the beginning or from the end of a day
20978 						//following code is identical to maxgapsperweek
20979 						//remove an activity
20980 						int ai2=-1;
20981 
20982 						//it should also be allowed to take from anywhere, but it is risky to change now
20983 						bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
20984 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
20985 						if(!k){
20986 							if(level==0){
20987 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
20988 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
20989 							}
20990 							okteachersmaxgapsperday=false;
20991 							goto impossibleteachersmaxgapsperday;
20992 						}
20993 
20994 						assert(ai2>=0);
20995 
20996 						removeAi2FromTchTimetable(ai2);
20997 						updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
20998 					}
20999 				}
21000 		}
21001 		else{
21002 			for(int tch : qAsConst(act->iTeachersList))
21003 				if(!skipRandom(teachersMaxGapsPerDayPercentage[tch])){
21004 					assert(teachersMaxGapsPerDayPercentage[tch]==100);
21005 
21006 					//preliminary test
21007 					int _total=0;
21008 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
21009 						_total+=newTeachersDayNHours(tch,d2);
21010 						if(teacherNoGapsPerAfternoon(tch)){
21011 							//2019-09-13 - max gaps per afternoon = 0
21012 							if(d2%2==0){ //morning
21013 								if(teachersMaxGapsPerDayMaxGaps[tch]<newTeachersDayNGaps(tch,d2))
21014 									_total+=newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch];
21015 							}
21016 							else{ //afternoon
21017 								_total+=newTeachersDayNGaps(tch,d2);
21018 							}
21019 						}
21020 						else{
21021 							if(teachersMaxGapsPerDayMaxGaps[tch]<newTeachersDayNGaps(tch,d2))
21022 								_total+=newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch];
21023 						}
21024 					}
21025 					if(_total<=nHoursPerTeacher[tch]) //OK
21026 						continue;
21027 
21028 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
21029 						okteachersmaxgapsperday=false;
21030 						goto impossibleteachersmaxgapsperday;
21031 					}
21032 
21033 					getTchTimetable(tch, conflActivities[newtime]);
21034 					tchGetNHoursGaps(tch);
21035 
21036 					for(;;){
21037 						int total=0;
21038 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
21039 							total+=tchDayNHours[d2];
21040 							if(teacherNoGapsPerAfternoon(tch)){
21041 								//2019-09-13 - max gaps per afternoon = 0
21042 								if(d2%2==0){ //morning
21043 									if(teachersMaxGapsPerDayMaxGaps[tch]<tchDayNGaps[d2])
21044 										total+=tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch];
21045 								}
21046 								else{ //afternoon
21047 									total+=tchDayNGaps[d2];
21048 								}
21049 							}
21050 							else{
21051 								if(teachersMaxGapsPerDayMaxGaps[tch]<tchDayNGaps[d2])
21052 									total+=tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch];
21053 							}
21054 						}
21055 						if(total<=nHoursPerTeacher[tch]) //OK
21056 							break;
21057 
21058 						//remove an activity from the beginning or from the end of a day
21059 						//following code is identical to maxgapsperweek
21060 						//remove an activity
21061 						int ai2=-1;
21062 
21063 						//it should also be allowed to take from anywhere, but it is risky to change now
21064 						bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
21065 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
21066 						if(!k){
21067 							if(level==0){
21068 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
21069 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
21070 							}
21071 							okteachersmaxgapsperday=false;
21072 							goto impossibleteachersmaxgapsperday;
21073 						}
21074 
21075 						assert(ai2>=0);
21076 
21077 						/*Activity* act2=&gt.rules.internalActivitiesList[ai2];
21078 						int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
21079 						int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
21080 
21081 						for(int dur2=0; dur2<act2->duration; dur2++){
21082 							assert(tchTimetable(d2,h2+dur2)==ai2);
21083 							tchTimetable(d2,h2+dur2)=-1;
21084 						}*/
21085 
21086 						removeAi2FromTchTimetable(ai2);
21087 						updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
21088 					}
21089 				}
21090 		}
21091 
21092 impossibleteachersmaxgapsperday:
21093 		if(!okteachersmaxgapsperday){
21094 			if(updateSubgroups || updateTeachers)
21095 				removeAiFromNewTimetable(ai, act, d, h);
21096 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
21097 
21098 			nConflActivities[newtime]=MAX_ACTIVITIES;
21099 			continue;
21100 		}
21101 
21102 		////////////////////////////END max gaps per day
21103 
21104 /////////////////////////////////////////////////////////////////////////////////////////////
21105 
21106 		//not causing more than teachersMaxGapsPerMorningAndAfternoon teachers gaps
21107 		okteachersmaxgapspermorningandafternoon=true;
21108 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
21109 			for(int tch : qAsConst(act->iTeachersList))
21110 				if(!skipRandom(teachersMaxGapsPerMorningAndAfternoonPercentage[tch])){
21111 					assert(teachersMaxGapsPerMorningAndAfternoonPercentage[tch]==100);
21112 
21113 					//preliminary test
21114 					int _total=0;
21115 					for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
21116 						int dm=d2*2, da=d2*2+1;
21117 						_total+=newTeachersDayNHours(tch,dm)+newTeachersDayNHours(tch,da);
21118 						if(teacherNoGapsPerAfternoon(tch)){
21119 							//2019-09-13 - max gaps per afternoon = 0
21120 							if(teachersMaxGapsPerMorningAndAfternoonMaxGaps[tch]<newTeachersDayNGaps(tch,dm))
21121 								_total+=newTeachersDayNGaps(tch,dm)-teachersMaxGapsPerMorningAndAfternoonMaxGaps[tch];
21122 							_total+=newTeachersDayNGaps(tch,da);
21123 						}
21124 						else{
21125 							if(teachersMaxGapsPerMorningAndAfternoonMaxGaps[tch]<newTeachersDayNGaps(tch,dm)+newTeachersDayNGaps(tch,da))
21126 								_total+=newTeachersDayNGaps(tch,dm)+newTeachersDayNGaps(tch,da)-teachersMaxGapsPerMorningAndAfternoonMaxGaps[tch];
21127 						}
21128 					}
21129 					if(_total<=nHoursPerTeacher[tch]) //OK
21130 						continue;
21131 
21132 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
21133 						okteachersmaxgapspermorningandafternoon=false;
21134 						goto impossibleteachersmaxgapspermorningandafternoon;
21135 					}
21136 
21137 					getTchTimetable(tch, conflActivities[newtime]);
21138 					tchGetNHoursGaps(tch);
21139 
21140 					for(;;){
21141 						int total=0;
21142 						for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
21143 							int dm=d2*2, da=d2*2+1;
21144 							total+=tchDayNHours[dm]+tchDayNHours[da];
21145 							if(teacherNoGapsPerAfternoon(tch)){
21146 								//2019-09-13 - max gaps per afternoon = 0
21147 								if(teachersMaxGapsPerMorningAndAfternoonMaxGaps[tch]<tchDayNGaps[dm])
21148 									total+=tchDayNGaps[dm]-teachersMaxGapsPerMorningAndAfternoonMaxGaps[tch];
21149 								total+=tchDayNGaps[da];
21150 							}
21151 							else{
21152 								if(teachersMaxGapsPerMorningAndAfternoonMaxGaps[tch]<tchDayNGaps[dm]+tchDayNGaps[da])
21153 									total+=tchDayNGaps[dm]+tchDayNGaps[da]-teachersMaxGapsPerMorningAndAfternoonMaxGaps[tch];
21154 							}
21155 						}
21156 						if(total<=nHoursPerTeacher[tch]) //OK
21157 							break;
21158 
21159 						//remove an activity from the beginning or from the end of a day
21160 						//following code is identical to maxgapsperweek
21161 						//remove an activity
21162 						int ai2=-1;
21163 
21164 						//it should also be allowed to take from anywhere, but it is risky to change now
21165 						bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
21166 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
21167 						if(!k){
21168 							if(level==0){
21169 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
21170 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
21171 							}
21172 							okteachersmaxgapspermorningandafternoon=false;
21173 							goto impossibleteachersmaxgapspermorningandafternoon;
21174 						}
21175 
21176 						assert(ai2>=0);
21177 
21178 						/*Activity* act2=&gt.rules.internalActivitiesList[ai2];
21179 						int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
21180 						int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
21181 
21182 						for(int dur2=0; dur2<act2->duration; dur2++){
21183 							assert(tchTimetable(d2,h2+dur2)==ai2);
21184 							tchTimetable(d2,h2+dur2)=-1;
21185 						}*/
21186 
21187 						removeAi2FromTchTimetable(ai2);
21188 						updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
21189 					}
21190 				}
21191 		}
21192 
21193 impossibleteachersmaxgapspermorningandafternoon:
21194 		if(!okteachersmaxgapspermorningandafternoon){
21195 			if(updateSubgroups || updateTeachers)
21196 				removeAiFromNewTimetable(ai, act, d, h);
21197 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
21198 
21199 			nConflActivities[newtime]=MAX_ACTIVITIES;
21200 			continue;
21201 		}
21202 
21203 		////////////////////////////END max gaps per morning and afternoon
21204 
21205 /////////////////////////////////////////////////////////////////////////////////////////////
21206 
21207 		//2019-09-13 - max 0 gaps per afternoon
21208 		okteachersmax0gapsperafternoon=true;
21209 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
21210 			for(int tch : qAsConst(act->iTeachersList))
21211 				if(teacherNoGapsPerAfternoon(tch)){
21212 					//assert(teachersMaxGapsPerDayPercentage[tch]==100);
21213 
21214 					//preliminary test
21215 					int _total=0;
21216 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
21217 						_total+=newTeachersDayNHours(tch,d2);
21218 						//2019-09-13 - max gaps per afternoon = 0
21219 						if(d2%2==0){ //morning
21220 							//Careful: the max gaps per day may be -1.
21221 							//And anyway I don't need this test, because I tested in the max gaps per day constraint above.
21222 							//if(teachersMaxGapsPerDayMaxGaps[tch]<newTeachersDayNGaps(tch,d2))
21223 							//	_total+=newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch];
21224 						}
21225 						else{ //afternoon
21226 							_total+=newTeachersDayNGaps(tch,d2);
21227 						}
21228 					}
21229 					if(_total<=nHoursPerTeacher[tch]) //OK
21230 						continue;
21231 
21232 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
21233 						okteachersmax0gapsperafternoon=false;
21234 						goto impossibleteachersmax0gapsperafternoon;
21235 					}
21236 
21237 					getTchTimetable(tch, conflActivities[newtime]);
21238 					tchGetNHoursGaps(tch);
21239 
21240 					for(;;){
21241 						int total=0;
21242 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
21243 							total+=tchDayNHours[d2];
21244 							//2019-09-13 - max gaps per afternoon = 0
21245 							if(d2%2==0){ //morning
21246 								//Careful: the max gaps per day may be -1.
21247 								//And anyway I don't need this test, because I tested in the max gaps per day constraint above.
21248 								//if(teachersMaxGapsPerDayMaxGaps[tch]<tchDayNGaps[d2])
21249 								//	total+=tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch];
21250 							}
21251 							else{ //afternoon
21252 								total+=tchDayNGaps[d2];
21253 							}
21254 						}
21255 						if(total<=nHoursPerTeacher[tch]) //OK
21256 							break;
21257 
21258 						//remove an activity from the beginning or from the end of a day
21259 						//following code is identical to maxgapsperweek
21260 						//remove an activity
21261 						int ai2=-1;
21262 
21263 						//it should also be allowed to take from anywhere, but it is risky to change now
21264 						bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
21265 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
21266 						if(!k){
21267 							if(level==0){
21268 								//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
21269 								//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
21270 							}
21271 							okteachersmax0gapsperafternoon=false;
21272 							goto impossibleteachersmax0gapsperafternoon;
21273 						}
21274 
21275 						assert(ai2>=0);
21276 
21277 						/*Activity* act2=&gt.rules.internalActivitiesList[ai2];
21278 						int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
21279 						int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
21280 
21281 						for(int dur2=0; dur2<act2->duration; dur2++){
21282 							assert(tchTimetable(d2,h2+dur2)==ai2);
21283 							tchTimetable(d2,h2+dur2)=-1;
21284 						}*/
21285 
21286 						removeAi2FromTchTimetable(ai2);
21287 						updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
21288 					}
21289 				}
21290 		}
21291 
21292 impossibleteachersmax0gapsperafternoon:
21293 		if(!okteachersmax0gapsperafternoon){
21294 			if(updateSubgroups || updateTeachers)
21295 				removeAiFromNewTimetable(ai, act, d, h);
21296 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
21297 
21298 			nConflActivities[newtime]=MAX_ACTIVITIES;
21299 			continue;
21300 		}
21301 
21302 		////////////////////////////END max 0 gaps per afternoon
21303 
21304 /////////////////////////////////////////////////////////////////////////////////////////////
21305 //2020-07-29
21306 
21307 		//not causing more than teachersMaxGapsPerWeekForRealDays teachers gaps
21308 		okteachersmaxgapsperweekforrealdays=true;
21309 
21310 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
21311 			if(haveTeachersMaxGapsPerRealDay){
21312 				for(int tch : qAsConst(act->iTeachersList))
21313 					if(!skipRandom(teachersMaxGapsPerWeekForRealDaysPercentage[tch])){
21314 						assert(teachersMaxGapsPerWeekForRealDaysPercentage[tch]==100);
21315 
21316 						//preliminary test
21317 						int _total=0;
21318 						for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
21319 							_total+=newTeachersRealDayNHours(tch,d2)+newTeachersRealDayNGaps(tch,d2);
21320 
21321 						if(_total<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekForRealDaysMaxGaps[tch]) //OK
21322 							continue;
21323 
21324 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
21325 							okteachersmaxgapsperweekforrealdays=false;
21326 							goto impossibleteachersmaxgapsperweekforrealdays;
21327 						}
21328 
21329 						getTchTimetable(tch, conflActivities[newtime]);
21330 						tchGetNHoursGapsRealDays(tch);
21331 						tchGetNHoursGaps(tch); //bug fix on 2020-02-29, because the remove an activity function below needs to know the number of hours per half-day.
21332 
21333 						for(;;){
21334 							int total=0;
21335 							for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
21336 								total+=tchRealDayNHours[d2]+tchRealDayNGaps[d2];
21337 
21338 							if(total<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekForRealDaysMaxGaps[tch]) //OK
21339 								break;
21340 
21341 							//remove an activity from the beginning or from the end of a real day
21342 							//following code is identical to maxgapsperweek
21343 							//remove an activity
21344 							int ai2=-1;
21345 
21346 							//it should also be allowed to take from anywhere, but it is risky to change now
21347 							bool k=teacherRemoveAnActivityFromBeginMorningOrEndAfternoon(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
21348 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
21349 							if(!k){
21350 								if(level==0){
21351 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
21352 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
21353 								}
21354 								okteachersmaxgapsperweekforrealdays=false;
21355 								goto impossibleteachersmaxgapsperweekforrealdays;
21356 							}
21357 
21358 							assert(ai2>=0);
21359 
21360 							removeAi2FromTchTimetable(ai2);
21361 							updateTchNHoursGapsRealDay(tch, (c.times[ai2]%gt.rules.nDaysPerWeek)/2);
21362 							updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek); //bug fix on 2020-02-29, because the remove an activity function above needs to know the number of hours per half-day.
21363 						}
21364 					}
21365 			}
21366 		}
21367 
21368 impossibleteachersmaxgapsperweekforrealdays:
21369 		if(!okteachersmaxgapsperweekforrealdays){
21370 			if(updateSubgroups || updateTeachers)
21371 				removeAiFromNewTimetable(ai, act, d, h);
21372 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
21373 
21374 			nConflActivities[newtime]=MAX_ACTIVITIES;
21375 			continue;
21376 		}
21377 
21378 		////////////////////////////END teachers max gaps per week for real days
21379 
21380 /////////////////////////////////////////////////////////////////////////////////////////////
21381 
21382 		//not causing more than teachersMaxGapsPerRealDay teachers gaps
21383 		okteachersmaxgapsperrealday=true;
21384 
21385 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
21386 			if(haveTeachersMaxGapsPerRealDay){
21387 				for(int tch : qAsConst(act->iTeachersList))
21388 					if(!skipRandom(teachersMaxGapsPerRealDayPercentage[tch])){
21389 						assert(teachersMaxGapsPerRealDayPercentage[tch]==100);
21390 
21391 						//preliminary test
21392 						int _total=0;
21393 						int _texcept;
21394 						if(teachersMaxGapsPerRealDayAllowException[tch]==true)
21395 							_texcept=1;
21396 						else
21397 							_texcept=0;
21398 						for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
21399 							_total+=newTeachersRealDayNHours(tch,d2);
21400 							if(teachersMaxGapsPerRealDayMaxGaps[tch]<newTeachersRealDayNGaps(tch,d2)){
21401 								_total+=newTeachersRealDayNGaps(tch,d2)-teachersMaxGapsPerRealDayMaxGaps[tch];
21402 								if(_texcept>0){
21403 									_texcept--;
21404 									_total--;
21405 								}
21406 							}
21407 						}
21408 
21409 						if(_total<=nHoursPerTeacher[tch]) //OK
21410 							continue;
21411 
21412 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
21413 							okteachersmaxgapsperrealday=false;
21414 							goto impossibleteachersmaxgapsperrealday;
21415 						}
21416 
21417 						getTchTimetable(tch, conflActivities[newtime]);
21418 						tchGetNHoursGapsRealDays(tch);
21419 						tchGetNHoursGaps(tch); //bug fix on 2020-02-29, because the remove an activity function below needs to know the number of hours per half-day.
21420 
21421 						for(;;){
21422 							int total=0;
21423 							int texcept;
21424 							if(teachersMaxGapsPerRealDayAllowException[tch]==true)
21425 								texcept=1;
21426 							else
21427 								texcept=0;
21428 							for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
21429 								total+=tchRealDayNHours[d2];
21430 								if(teachersMaxGapsPerRealDayMaxGaps[tch]<tchRealDayNGaps[d2]){
21431 									total+=tchRealDayNGaps[d2]-teachersMaxGapsPerRealDayMaxGaps[tch];
21432 									if(texcept>0){
21433 										texcept--;
21434 										total--;
21435 									}
21436 								}
21437 							}
21438 							if(total<=nHoursPerTeacher[tch]) //OK
21439 								break;
21440 
21441 							//remove an activity from the beginning or from the end of a real day
21442 							//following code is identical to maxgapsperweek
21443 							//remove an activity
21444 							int ai2=-1;
21445 
21446 							//it should also be allowed to take from anywhere, but it is risky to change now
21447 							bool k=teacherRemoveAnActivityFromBeginMorningOrEndAfternoon(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
21448 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
21449 							if(!k){
21450 								if(level==0){
21451 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
21452 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
21453 								}
21454 								okteachersmaxgapsperrealday=false;
21455 								goto impossibleteachersmaxgapsperrealday;
21456 							}
21457 
21458 							assert(ai2>=0);
21459 
21460 							/*Activity* act2=&gt.rules.internalActivitiesList[ai2];
21461 							int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
21462 							int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
21463 
21464 							for(int dur2=0; dur2<act2->duration; dur2++){
21465 								assert(tchTimetable(d2,h2+dur2)==ai2);
21466 								tchTimetable(d2,h2+dur2)=-1;
21467 							}*/
21468 
21469 							removeAi2FromTchTimetable(ai2);
21470 							updateTchNHoursGapsRealDay(tch, (c.times[ai2]%gt.rules.nDaysPerWeek)/2);
21471 							updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek); //bug fix on 2020-02-29, because the remove an activity function above needs to know the number of hours per half-day.
21472 						}
21473 					}
21474 			}
21475 		}
21476 
21477 impossibleteachersmaxgapsperrealday:
21478 		if(!okteachersmaxgapsperrealday){
21479 			if(updateSubgroups || updateTeachers)
21480 				removeAiFromNewTimetable(ai, act, d, h);
21481 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
21482 
21483 			nConflActivities[newtime]=MAX_ACTIVITIES;
21484 			continue;
21485 		}
21486 
21487 		////////////////////////////END teachers max gaps per real day
21488 
21489 /////////////////////////////////////////////////////////////////////////////////////////////
21490 
21491 		//allowed from teachers max hours daily per real day
21492 
21493 		//!!!after max gaps per week and max gaps per day
21494 
21495 		okteachersmaxhoursdailyrealdays=true;
21496 
21497 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
21498 			for(int tch : qAsConst(act->iTeachersList)){
21499 				for(int count=0; count<2; count++){
21500 					int limitHoursDaily;
21501 					double percentage;
21502 					if(count==0){
21503 						limitHoursDaily=teachersMaxHoursDailyRealDaysMaxHours1[tch];
21504 						percentage=teachersMaxHoursDailyRealDaysPercentages1[tch];
21505 					}
21506 					else{
21507 						limitHoursDaily=teachersMaxHoursDailyRealDaysMaxHours2[tch];
21508 						percentage=teachersMaxHoursDailyRealDaysPercentages2[tch];
21509 					}
21510 
21511 					if(limitHoursDaily<0)
21512 						continue;
21513 
21514 					//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
21515 					//	continue;
21516 
21517 					bool increased;
21518 					//2019-09-13 - max gaps per afternoon = 0
21519 					//2021-04-22: the tests below involve uninitialized variables, as reported by Valgrind.
21520 					/*if(teachersMaxGapsPerWeekPercentage[tch]>=0 || teachersMaxGapsPerDayPercentage[tch]>=0 || teacherNoGapsPerAfternoon(tch)){
21521 						if(newTeachersDayNHours(tch,d)+newTeachersDayNHours(tch,dpair) > oldTeachersDayNHours(tch,d)+oldTeachersDayNHours(tch,dpair)
21522 						  || newTeachersDayNHours(tch,d)+newTeachersDayNGaps(tch,d)+newTeachersDayNHours(tch,dpair)+newTeachersDayNGaps(tch,dpair)
21523 						  > oldTeachersDayNHours(tch,d)+oldTeachersDayNGaps(tch,d)+
21524 						  oldTeachersDayNHours(tch,dpair)+oldTeachersDayNGaps(tch,dpair))
21525 							increased=true;
21526 						else
21527 							increased=false;
21528 					}
21529 					else{
21530 						if(newTeachersDayNHours(tch,d)+newTeachersDayNHours(tch,dpair) > oldTeachersDayNHours(tch,d)+oldTeachersDayNHours(tch,dpair))
21531 							increased=true;
21532 						else
21533 							increased=false;
21534 					}*/
21535 					/*
21536 					if(newTeachersDayNHours(tch,d) > oldTeachersDayNHours(tch,d))
21537 						increased=true;
21538 					else
21539 						increased=false;*/
21540 
21541 					//Liviu Lalescu 2021-03-29: I think this needs to remain true, because of gaps per real day/real day per week.
21542 					///???????? TODO
21543 					increased=true; /////???????????
21544 
21545 					if(limitHoursDaily>=0 && !skipRandom(percentage) && increased){
21546 						if(limitHoursDaily<act->duration){
21547 							okteachersmaxhoursdailyrealdays=false;
21548 							goto impossibleteachersmaxhoursdailyrealdays;
21549 						}
21550 
21551 						//preliminary test
21552 
21553 						//basically, see that the gaps are enough
21554 						bool _ok;
21555 						if(newTeachersDayNHours(tch,d)+newTeachersDayNHours(tch,dpair)>limitHoursDaily){
21556 							_ok=false;
21557 						}
21558 						else{
21559 							if(teachersMaxGapsPerWeekPercentage[tch]>=0){
21560 								int rg=teachersMaxGapsPerWeekMaxGaps[tch];
21561 								for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
21562 									if(d2!=d/2){
21563 										int dfet1=d2*2, dfet2=d2*2+1;
21564 										int g=limitHoursDaily-newTeachersDayNHours(tch,dfet1)-newTeachersDayNHours(tch,dfet2);
21565 										//TODO: if g lower than 0 make g 0
21566 										//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
21567 										g=newTeachersDayNGaps(tch,dfet1)+newTeachersDayNGaps(tch,dfet2)-g;
21568 										if(g>0)
21569 											rg-=g;
21570 									}
21571 								}
21572 
21573 								if(rg<0)
21574 									rg=0;
21575 
21576 								if(teachersMaxGapsPerDayPercentage[tch]>=0){
21577 									if(teacherNoGapsPerAfternoon(tch)){
21578 										//if(rg>2*teachersMaxGapsPerDayMaxGaps[tch])
21579 										//	rg=2*teachersMaxGapsPerDayMaxGaps[tch];
21580 										//2019-09-13 - max gaps per afternoon = 0
21581 										if(rg>teachersMaxGapsPerDayMaxGaps[tch])
21582 											rg=teachersMaxGapsPerDayMaxGaps[tch];
21583 									}
21584 									else{
21585 										if(rg>2*teachersMaxGapsPerDayMaxGaps[tch])
21586 											rg=2*teachersMaxGapsPerDayMaxGaps[tch];
21587 									}
21588 								}
21589 
21590 								int hg=newTeachersDayNGaps(tch,d)+newTeachersDayNGaps(tch,dpair)-rg;
21591 								if(hg<0)
21592 									hg=0;
21593 
21594 								if(hg+newTeachersDayNHours(tch,d)+newTeachersDayNHours(tch,dpair) > limitHoursDaily){
21595 									_ok=false;
21596 								}
21597 								else
21598 									_ok=true;
21599 							}
21600 							else{
21601 								int rg=newTeachersDayNGaps(tch,d)+newTeachersDayNGaps(tch,dpair);
21602 								int hg=rg;
21603 								if(teachersMaxGapsPerDayPercentage[tch]>=0){
21604 									if(teacherNoGapsPerAfternoon(tch)){
21605 										//2019-09-13 - max gaps per afternoon = 0
21606 										//if(rg>2*teachersMaxGapsPerDayMaxGaps[tch])
21607 										//	rg=2*teachersMaxGapsPerDayMaxGaps[tch];
21608 										if(rg>teachersMaxGapsPerDayMaxGaps[tch])
21609 											rg=teachersMaxGapsPerDayMaxGaps[tch];
21610 									}
21611 									else{
21612 										if(rg>2*teachersMaxGapsPerDayMaxGaps[tch])
21613 											rg=2*teachersMaxGapsPerDayMaxGaps[tch];
21614 									}
21615 								}
21616 								hg-=rg;
21617 								if(hg+newTeachersDayNHours(tch,d)+newTeachersDayNHours(tch,dpair) > limitHoursDaily)
21618 									_ok=false;
21619 								else
21620 									_ok=true;
21621 							}
21622 						}
21623 
21624 						if(_ok){
21625 							continue;
21626 						}
21627 
21628 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
21629 							okteachersmaxhoursdailyrealdays=false;
21630 							goto impossibleteachersmaxhoursdailyrealdays;
21631 						}
21632 
21633 						getTchTimetable(tch, conflActivities[newtime]);
21634 						tchGetNHoursGaps(tch);
21635 
21636 						//2019-09-13 - max gaps per afternoon = 0
21637 						bool canTakeFromBeginOrEnd=true;
21638 						bool canTakeFromAnywhere=(teachersMaxGapsPerWeekMaxGaps[tch]!=0 && teachersMaxGapsPerDayMaxGaps[tch]!=0); //-1 or >0
21639 						bool canTakeFromBeginOrEndAnyDay=(teacherNoGapsPerAfternoon(tch) || teachersMaxGapsPerWeekMaxGaps[tch]>=0 || teachersMaxGapsPerDayMaxGaps[tch]>=0);
21640 
21641 						for(;;){
21642 							//basically, see that the gaps are enough
21643 							bool ok;
21644 							if(tchDayNHours[d]+tchDayNHours[dpair]>limitHoursDaily){
21645 								ok=false;
21646 							}
21647 							else{
21648 								if(teachersMaxGapsPerWeekPercentage[tch]>=0){
21649 									int rg=teachersMaxGapsPerWeekMaxGaps[tch];
21650 									for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++){
21651 										if(d2!=d/2){
21652 											int dfet1=d2*2, dfet2=d2*2+1;
21653 											int g=limitHoursDaily-tchDayNHours[dfet1]-tchDayNHours[dfet2];
21654 											//TODO: if g lower than 0 make g 0
21655 											//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
21656 											g=tchDayNGaps[dfet1]+tchDayNGaps[dfet2]-g;
21657 											if(g>0)
21658 												rg-=g;
21659 										}
21660 									}
21661 
21662 									if(rg<0)
21663 										rg=0;
21664 
21665 									if(teachersMaxGapsPerDayPercentage[tch]>=0){
21666 										if(teacherNoGapsPerAfternoon(tch)){
21667 											//2019-09-13 - max gaps per afternoon = 0
21668 											//if(rg>2*teachersMaxGapsPerDayMaxGaps[tch])
21669 											//	rg=2*teachersMaxGapsPerDayMaxGaps[tch];
21670 											if(rg>teachersMaxGapsPerDayMaxGaps[tch])
21671 												rg=teachersMaxGapsPerDayMaxGaps[tch];
21672 										}
21673 										else{
21674 											if(rg>2*teachersMaxGapsPerDayMaxGaps[tch])
21675 												rg=2*teachersMaxGapsPerDayMaxGaps[tch];
21676 										}
21677 									}
21678 
21679 									int hg=tchDayNGaps[d]+tchDayNGaps[dpair]-rg;
21680 									if(hg<0)
21681 										hg=0;
21682 
21683 									if(hg+tchDayNHours[d]+tchDayNHours[dpair] > limitHoursDaily){
21684 										ok=false;
21685 									}
21686 									else
21687 										ok=true;
21688 								}
21689 								else{
21690 									int rg=tchDayNGaps[d]+tchDayNGaps[dpair];
21691 									int hg=rg;
21692 									if(teachersMaxGapsPerDayPercentage[tch]>=0){
21693 										if(teacherNoGapsPerAfternoon(tch)){
21694 												//2019-09-13 - max gaps per afternoon = 0
21695 												//if(rg>2*teachersMaxGapsPerDayMaxGaps[tch])
21696 												//	rg=2*teachersMaxGapsPerDayMaxGaps[tch];
21697 												if(rg>teachersMaxGapsPerDayMaxGaps[tch])
21698 													rg=teachersMaxGapsPerDayMaxGaps[tch];
21699 										}
21700 										else{
21701 											if(rg>2*teachersMaxGapsPerDayMaxGaps[tch])
21702 												rg=2*teachersMaxGapsPerDayMaxGaps[tch];
21703 										}
21704 									}
21705 									hg-=rg;
21706 									if(hg+tchDayNHours[d]+tchDayNHours[dpair] > limitHoursDaily)
21707 										ok=false;
21708 									else
21709 										ok=true;
21710 								}
21711 							}
21712 
21713 							if(ok){
21714 								break;
21715 							}
21716 
21717 							int ai2=-1;
21718 
21719 							bool k=false;
21720 							if(canTakeFromBeginOrEnd)
21721 								k=teacherRemoveAnActivityFromBeginOrEndCertainTwoDays(d, dpair, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
21722 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
21723 							if(!k){
21724 								canTakeFromBeginOrEnd=false;
21725 								bool ka=false;
21726 								if(canTakeFromAnywhere)
21727 									ka=teacherRemoveAnActivityFromAnywhereCertainTwoDays(d, dpair, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
21728 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
21729 
21730 								if(!ka){
21731 									canTakeFromAnywhere=false;
21732 									bool kaa=false;
21733 									if(canTakeFromBeginOrEndAnyDay && tchDayNHours[d]+tchDayNHours[dpair]<=limitHoursDaily)
21734 										//Fix on 2017-08-26, to solve Volker Dirr's bug report
21735 										kaa=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
21736 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
21737 									if(!kaa){
21738 										canTakeFromBeginOrEndAnyDay=false;
21739 
21740 										if(level==0){
21741 											/*cout<<"d=="<<d<<", h=="<<h<<", teacher=="<<qPrintable(gt.rules.internalTeachersList[tch]->name);
21742 											cout<<", ai=="<<ai<<endl;
21743 											for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
21744 												for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
21745 													cout<<"\t"<<tchTimetable(d2,h2)<<"\t";
21746 												cout<<endl;
21747 											}
21748 
21749 											cout<<endl;
21750 											for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
21751 												for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
21752 													cout<<"\t"<<newTeachersTimetable(tch,d2,h2)<<"\t";
21753 												cout<<endl;
21754 											}*/
21755 
21756 											//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
21757 											//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
21758 										}
21759 										okteachersmaxhoursdailyrealdays=false;
21760 										goto impossibleteachersmaxhoursdailyrealdays;
21761 									}
21762 								}
21763 							}
21764 
21765 							assert(ai2>=0);
21766 
21767 							removeAi2FromTchTimetable(ai2);
21768 							updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
21769 						}
21770 					}
21771 				}
21772 			}
21773 		}
21774 
21775 impossibleteachersmaxhoursdailyrealdays:
21776 		if(!okteachersmaxhoursdailyrealdays){
21777 			if(updateSubgroups || updateTeachers)
21778 				removeAiFromNewTimetable(ai, act, d, h);
21779 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
21780 
21781 			nConflActivities[newtime]=MAX_ACTIVITIES;
21782 			continue;
21783 		}
21784 
21785 /////////////////////////////////////////////////////////////////////////////////////////////
21786 
21787 		//allowed from teachers max hours daily
21788 
21789 		//!!!after max gaps per week and max gaps per day
21790 
21791 		okteachersmaxhoursdaily=true;
21792 
21793 		if(gt.rules.mode!=MORNINGS_AFTERNOONS){
21794 			for(int tch : qAsConst(act->iTeachersList)){
21795 				for(int count=0; count<2; count++){
21796 					int limitHoursDaily;
21797 					double percentage;
21798 					if(count==0){
21799 						limitHoursDaily=teachersMaxHoursDailyMaxHours1[tch];
21800 						percentage=teachersMaxHoursDailyPercentages1[tch];
21801 					}
21802 					else{
21803 						limitHoursDaily=teachersMaxHoursDailyMaxHours2[tch];
21804 						percentage=teachersMaxHoursDailyPercentages2[tch];
21805 					}
21806 
21807 					if(limitHoursDaily<0)
21808 						continue;
21809 
21810 					//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
21811 					//	continue;
21812 
21813 					bool increased;
21814 					if(teachersMaxGapsPerWeekPercentage[tch]>=0 || teachersMaxGapsPerDayPercentage[tch]>=0){
21815 						if(newTeachersDayNHours(tch,d) > oldTeachersDayNHours(tch,d)
21816 						  || newTeachersDayNHours(tch,d)+newTeachersDayNGaps(tch,d) > oldTeachersDayNHours(tch,d)+oldTeachersDayNGaps(tch,d))
21817 							increased=true;
21818 						else
21819 							increased=false;
21820 					}
21821 					else{
21822 						if(newTeachersDayNHours(tch,d) > oldTeachersDayNHours(tch,d))
21823 							increased=true;
21824 						else
21825 							increased=false;
21826 					}
21827 					/*
21828 					if(newTeachersDayNHours(tch,d) > oldTeachersDayNHours(tch,d))
21829 						increased=true;
21830 					else
21831 						increased=false;*/
21832 
21833 					if(limitHoursDaily>=0 && !skipRandom(percentage) && increased){
21834 						if(limitHoursDaily<act->duration){
21835 							okteachersmaxhoursdaily=false;
21836 							goto impossibleteachersmaxhoursdaily;
21837 						}
21838 
21839 						//preliminary test
21840 						bool _ok;
21841 						if(newTeachersDayNHours(tch,d)>limitHoursDaily){
21842 							_ok=false; //trivially
21843 						}
21844 						else{
21845 							//basically, see that the gaps are enough
21846 							// Comment added on 2020-09-15: This code was written a long time ago. It cares that the gaps are enough, but it is more like a heuristic,
21847 							// because the weight might be any real number below 100.0%. So on other days the constraints should be allowed to be broken.
21848 							// However, it is very risky to change now. I think that the best would be to allow max hours daily only with 100.0% weight,
21849 							// but unfortunately I think that many users have files with weight <100.0%.
21850 							// Also, don't forget that we might have two constraints max hours daily for each subgroup.
21851 							if(teachersMaxGapsPerWeekPercentage[tch]>=0){
21852 								int rg=teachersMaxGapsPerWeekMaxGaps[tch];
21853 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
21854 									if(d2!=d){
21855 										int g=limitHoursDaily-newTeachersDayNHours(tch,d2);
21856 										//TODO: if g lower than 0 make g 0
21857 										//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
21858 										g=newTeachersDayNGaps(tch,d2)-g;
21859 										if(g>0)
21860 											rg-=g;
21861 									}
21862 								}
21863 
21864 								if(rg<0)
21865 									rg=0;
21866 
21867 								if(teachersMaxGapsPerDayPercentage[tch]>=0)
21868 									if(rg>teachersMaxGapsPerDayMaxGaps[tch])
21869 										rg=teachersMaxGapsPerDayMaxGaps[tch];
21870 
21871 								int hg=newTeachersDayNGaps(tch,d)-rg;
21872 								if(hg<0)
21873 									hg=0;
21874 
21875 								if(hg+newTeachersDayNHours(tch,d) > limitHoursDaily){
21876 									_ok=false;
21877 								}
21878 								else
21879 									_ok=true;
21880 							}
21881 							else{
21882 								int rg=newTeachersDayNGaps(tch,d);
21883 								int hg=rg;
21884 								if(teachersMaxGapsPerDayPercentage[tch]>=0)
21885 									if(rg>teachersMaxGapsPerDayMaxGaps[tch])
21886 										rg=teachersMaxGapsPerDayMaxGaps[tch];
21887 								hg-=rg;
21888 								if(hg+newTeachersDayNHours(tch,d) > limitHoursDaily)
21889 									_ok=false;
21890 								else
21891 									_ok=true;
21892 							}
21893 						}
21894 
21895 						if(_ok){
21896 							continue;
21897 						}
21898 
21899 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
21900 							okteachersmaxhoursdaily=false;
21901 							goto impossibleteachersmaxhoursdaily;
21902 						}
21903 
21904 						getTchTimetable(tch, conflActivities[newtime]);
21905 						tchGetNHoursGaps(tch);
21906 
21907 						bool canTakeFromBeginOrEnd=true;
21908 						bool canTakeFromAnywhere=(teachersMaxGapsPerWeekMaxGaps[tch]!=0 && teachersMaxGapsPerDayMaxGaps[tch]!=0); //-1 or >0
21909 						bool canTakeFromBeginOrEndAnyDay=(teachersMaxGapsPerWeekMaxGaps[tch]>=0 || teachersMaxGapsPerDayMaxGaps[tch]>=0);
21910 
21911 						for(;;){
21912 							bool ok;
21913 							if(tchDayNHours[d]>limitHoursDaily){
21914 								ok=false; //trivially
21915 							}
21916 							else{
21917 								//basically, see that the gaps are enough
21918 								// Comment added on 2020-09-15: This code was written a long time ago. It cares that the gaps are enough, but it is more like a heuristic,
21919 								// because the weight might be any real number below 100.0%. So on other days the constraints should be allowed to be broken.
21920 								// However, it is very risky to change now. I think that the best would be to allow max hours daily only with 100.0% weight,
21921 								// but unfortunately I think that many users have files with weight <100.0%.
21922 								// Also, don't forget that we might have two constraints max hours daily for each subgroup.
21923 								if(teachersMaxGapsPerWeekPercentage[tch]>=0){
21924 									int rg=teachersMaxGapsPerWeekMaxGaps[tch];
21925 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
21926 										if(d2!=d){
21927 											int g=limitHoursDaily-tchDayNHours[d2];
21928 											//TODO: if g lower than 0 make g 0
21929 											//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
21930 											g=tchDayNGaps[d2]-g;
21931 											if(g>0)
21932 												rg-=g;
21933 										}
21934 									}
21935 
21936 									if(rg<0)
21937 										rg=0;
21938 
21939 									if(teachersMaxGapsPerDayPercentage[tch]>=0)
21940 										if(rg>teachersMaxGapsPerDayMaxGaps[tch])
21941 											rg=teachersMaxGapsPerDayMaxGaps[tch];
21942 
21943 									int hg=tchDayNGaps[d]-rg;
21944 									if(hg<0)
21945 										hg=0;
21946 
21947 									if(hg+tchDayNHours[d] > limitHoursDaily){
21948 										ok=false;
21949 									}
21950 									else
21951 										ok=true;
21952 								}
21953 								else{
21954 									int rg=tchDayNGaps[d];
21955 									int hg=rg;
21956 									if(teachersMaxGapsPerDayPercentage[tch]>=0)
21957 										if(rg>teachersMaxGapsPerDayMaxGaps[tch])
21958 											rg=teachersMaxGapsPerDayMaxGaps[tch];
21959 									hg-=rg;
21960 									if(hg+tchDayNHours[d] > limitHoursDaily)
21961 										ok=false;
21962 									else
21963 										ok=true;
21964 								}
21965 							}
21966 
21967 							if(ok){
21968 								break;
21969 							}
21970 
21971 							int ai2=-1;
21972 
21973 							bool k=false;
21974 							if(canTakeFromBeginOrEnd)
21975 								k=teacherRemoveAnActivityFromBeginOrEndCertainDay(d, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
21976 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
21977 							if(!k){
21978 								canTakeFromBeginOrEnd=false;
21979 								bool ka=false;
21980 								if(canTakeFromAnywhere)
21981 									ka=teacherRemoveAnActivityFromAnywhereCertainDay(d, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
21982 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
21983 
21984 								if(!ka){
21985 									canTakeFromAnywhere=false;
21986 									bool kaa=false;
21987 									if(canTakeFromBeginOrEndAnyDay && tchDayNHours[d]<=limitHoursDaily)
21988 										//Fix on 2017-08-26, to solve Volker Dirr's bug report
21989 										kaa=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
21990 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
21991 									if(!kaa){
21992 										canTakeFromBeginOrEndAnyDay=false;
21993 
21994 										if(level==0){
21995 											//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
21996 											//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
21997 										}
21998 										okteachersmaxhoursdaily=false;
21999 										goto impossibleteachersmaxhoursdaily;
22000 									}
22001 								}
22002 							}
22003 
22004 							assert(ai2>=0);
22005 
22006 							removeAi2FromTchTimetable(ai2);
22007 							updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
22008 						}
22009 					}
22010 				}
22011 			}
22012 		}
22013 		else{ //max hours daily for half-days for the mornings-afternoons version
22014 			for(int tch : qAsConst(act->iTeachersList)){
22015 				for(int count=0; count<2; count++){
22016 					int limitHoursDaily;
22017 					double percentage;
22018 					if(count==0){
22019 						limitHoursDaily=teachersMaxHoursDailyMaxHours1[tch];
22020 						percentage=teachersMaxHoursDailyPercentages1[tch];
22021 					}
22022 					else{
22023 						limitHoursDaily=teachersMaxHoursDailyMaxHours2[tch];
22024 						percentage=teachersMaxHoursDailyPercentages2[tch];
22025 					}
22026 
22027 					if(limitHoursDaily<0)
22028 						continue;
22029 
22030 					//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
22031 					//	continue;
22032 
22033 					bool increased;
22034 					if(teachersMaxGapsPerWeekPercentage[tch]>=0 || teachersMaxGapsPerDayPercentage[tch]>=0){
22035 						if(newTeachersDayNHours(tch,d) > oldTeachersDayNHours(tch,d)
22036 						  || newTeachersDayNHours(tch,d)+newTeachersDayNGaps(tch,d) > oldTeachersDayNHours(tch,d)+oldTeachersDayNGaps(tch,d))
22037 							increased=true;
22038 						else
22039 							increased=false;
22040 					}
22041 					else{
22042 						if(newTeachersDayNHours(tch,d) > oldTeachersDayNHours(tch,d))
22043 							increased=true;
22044 						else
22045 							increased=false;
22046 					}
22047 					/*
22048 					if(newTeachersDayNHours(tch,d) > oldTeachersDayNHours(tch,d))
22049 						increased=true;
22050 					else
22051 						increased=false;*/
22052 
22053 					///???????? TODO
22054 					//commented out on 2019-07-02
22055 					//increased=true; /////???????????
22056 
22057 					if(limitHoursDaily>=0 && !skipRandom(percentage) && increased){
22058 						if(limitHoursDaily<act->duration){
22059 							okteachersmaxhoursdaily=false;
22060 							goto impossibleteachersmaxhoursdaily;
22061 						}
22062 
22063 						//preliminary test
22064 
22065 						//basically, see that the gaps are enough
22066 						bool _ok;
22067 						if(newTeachersDayNHours(tch,d)>limitHoursDaily){
22068 							_ok=false;
22069 						}
22070 						else{
22071 							if(teachersMaxGapsPerWeekPercentage[tch]>=0){
22072 								int rg=teachersMaxGapsPerWeekMaxGaps[tch];
22073 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
22074 									if(d2!=d){
22075 										int g=limitHoursDaily-newTeachersDayNHours(tch,d2);
22076 										//TODO: if g lower than 0 make g 0
22077 										//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
22078 										g=newTeachersDayNGaps(tch,d2)-g;
22079 										if(g>0)
22080 											rg-=g;
22081 									}
22082 								}
22083 
22084 								if(rg<0)
22085 									rg=0;
22086 
22087 								if(teachersMaxGapsPerDayPercentage[tch]>=0){
22088 									if(teacherNoGapsPerAfternoon(tch)){
22089 										//2019-09-13 - max gaps per afternoon = 0
22090 										if(d%2==0){ //morning
22091 											if(rg>teachersMaxGapsPerDayMaxGaps[tch]){
22092 												rg=teachersMaxGapsPerDayMaxGaps[tch];
22093 											}
22094 										}
22095 										else{ //afternoon
22096 											rg=0;
22097 										}
22098 									}
22099 									else{
22100 										if(rg>teachersMaxGapsPerDayMaxGaps[tch])
22101 											rg=teachersMaxGapsPerDayMaxGaps[tch];
22102 									}
22103 								}
22104 
22105 								int hg=newTeachersDayNGaps(tch,d)-rg;
22106 								if(hg<0)
22107 									hg=0;
22108 
22109 								if(hg+newTeachersDayNHours(tch,d) > limitHoursDaily){
22110 									_ok=false;
22111 								}
22112 								else
22113 									_ok=true;
22114 							}
22115 							else{
22116 								int rg=newTeachersDayNGaps(tch,d);
22117 								int hg=rg;
22118 								if(teachersMaxGapsPerDayPercentage[tch]>=0){
22119 									if(teacherNoGapsPerAfternoon(tch)){
22120 										//2019-09-13 - max gaps per afternoon = 0
22121 										if(d%2==0){ //morning
22122 											if(rg>teachersMaxGapsPerDayMaxGaps[tch]){
22123 												rg=teachersMaxGapsPerDayMaxGaps[tch];
22124 											}
22125 										}
22126 										else{ //afternoon
22127 											rg=0;
22128 										}
22129 									}
22130 									else{
22131 										if(rg>teachersMaxGapsPerDayMaxGaps[tch])
22132 											rg=teachersMaxGapsPerDayMaxGaps[tch];
22133 									}
22134 								}
22135 								hg-=rg;
22136 								if(hg+newTeachersDayNHours(tch,d) > limitHoursDaily)
22137 									_ok=false;
22138 								else
22139 									_ok=true;
22140 							}
22141 						}
22142 
22143 						if(_ok){
22144 							continue;
22145 						}
22146 
22147 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
22148 							okteachersmaxhoursdaily=false;
22149 							goto impossibleteachersmaxhoursdaily;
22150 						}
22151 
22152 						getTchTimetable(tch, conflActivities[newtime]);
22153 						tchGetNHoursGaps(tch);
22154 
22155 						//2019-09-13 - max gaps per afternoon = 0
22156 						bool canTakeFromBeginOrEnd=true;
22157 						bool canTakeFromAnywhere=(teachersMaxGapsPerWeekMaxGaps[tch]!=0 && teachersMaxGapsPerDayMaxGaps[tch]!=0); //-1 or >0
22158 						bool canTakeFromBeginOrEndAnyDay=(teacherNoGapsPerAfternoon(tch) || teachersMaxGapsPerWeekMaxGaps[tch]>=0 || teachersMaxGapsPerDayMaxGaps[tch]>=0);
22159 
22160 						for(;;){
22161 							//basically, see that the gaps are enough
22162 							bool ok;
22163 							if(tchDayNHours[d]>limitHoursDaily){
22164 								ok=false;
22165 							}
22166 							else{
22167 								if(teachersMaxGapsPerWeekPercentage[tch]>=0){
22168 									int rg=teachersMaxGapsPerWeekMaxGaps[tch];
22169 									for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
22170 										if(d2!=d){
22171 											int g=limitHoursDaily-tchDayNHours[d2];
22172 											//TODO: if g lower than 0 make g 0
22173 											//but with this change, speed decreases for file examples/anonymous/1/2008/anonymous-1-2008.fet
22174 											g=tchDayNGaps[d2]-g;
22175 											if(g>0)
22176 												rg-=g;
22177 										}
22178 									}
22179 
22180 									if(rg<0)
22181 										rg=0;
22182 
22183 									if(teachersMaxGapsPerDayPercentage[tch]>=0){
22184 										if(teacherNoGapsPerAfternoon(tch)){
22185 											//2019-09-13 - max gaps per afternoon = 0
22186 											if(d%2==0){ //morning
22187 												if(rg>teachersMaxGapsPerDayMaxGaps[tch]){
22188 													rg=teachersMaxGapsPerDayMaxGaps[tch];
22189 												}
22190 											}
22191 											else{ //afternoon
22192 												rg=0;
22193 											}
22194 										}
22195 										else{
22196 											if(rg>teachersMaxGapsPerDayMaxGaps[tch])
22197 												rg=teachersMaxGapsPerDayMaxGaps[tch];
22198 										}
22199 									}
22200 
22201 									int hg=tchDayNGaps[d]-rg;
22202 									if(hg<0)
22203 										hg=0;
22204 
22205 									if(hg+tchDayNHours[d] > limitHoursDaily){
22206 										ok=false;
22207 									}
22208 									else
22209 										ok=true;
22210 								}
22211 								else{
22212 									int rg=tchDayNGaps[d];
22213 									int hg=rg;
22214 									if(teachersMaxGapsPerDayPercentage[tch]>=0){
22215 										if(teacherNoGapsPerAfternoon(tch)){
22216 											//2019-09-13 - max gaps per afternoon = 0
22217 											if(d%2==0){ //morning
22218 												if(rg>teachersMaxGapsPerDayMaxGaps[tch]){
22219 													rg=teachersMaxGapsPerDayMaxGaps[tch];
22220 												}
22221 											}
22222 											else{ //afternoon
22223 												rg=0;
22224 											}
22225 										}
22226 										else{
22227 											if(rg>teachersMaxGapsPerDayMaxGaps[tch])
22228 												rg=teachersMaxGapsPerDayMaxGaps[tch];
22229 										}
22230 									}
22231 									hg-=rg;
22232 									if(hg+tchDayNHours[d] > limitHoursDaily)
22233 										ok=false;
22234 									else
22235 										ok=true;
22236 								}
22237 							}
22238 
22239 							if(ok){
22240 								break;
22241 							}
22242 
22243 							int ai2=-1;
22244 
22245 							bool k=false;
22246 							if(canTakeFromBeginOrEnd)
22247 								k=teacherRemoveAnActivityFromBeginOrEndCertainDay(d, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
22248 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
22249 							if(!k){
22250 								canTakeFromBeginOrEnd=false;
22251 								bool ka=false;
22252 								if(canTakeFromAnywhere)
22253 									ka=teacherRemoveAnActivityFromAnywhereCertainDay(d, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
22254 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
22255 
22256 								if(!ka){
22257 									canTakeFromAnywhere=false;
22258 									bool kaa=false;
22259 									if(canTakeFromBeginOrEndAnyDay && tchDayNHours[d]<=limitHoursDaily)
22260 										//Fix on 2017-08-26, to solve Volker Dirr's bug report
22261 										kaa=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
22262 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
22263 									if(!kaa){
22264 										canTakeFromBeginOrEndAnyDay=false;
22265 
22266 										if(level==0){
22267 											/*cout<<"d=="<<d<<", h=="<<h<<", teacher=="<<qPrintable(gt.rules.internalTeachersList[tch]->name);
22268 											cout<<", ai=="<<ai<<endl;
22269 											for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
22270 												for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
22271 													cout<<"\t"<<tchTimetable(d2,h2)<<"\t";
22272 												cout<<endl;
22273 											}
22274 
22275 											cout<<endl;
22276 											for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
22277 												for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
22278 													cout<<"\t"<<newTeachersTimetable(tch,d2,h2)<<"\t";
22279 												cout<<endl;
22280 											}*/
22281 
22282 											//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
22283 											//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
22284 										}
22285 										okteachersmaxhoursdaily=false;
22286 										goto impossibleteachersmaxhoursdaily;
22287 									}
22288 								}
22289 							}
22290 
22291 							assert(ai2>=0);
22292 
22293 							removeAi2FromTchTimetable(ai2);
22294 							updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
22295 						}
22296 					}
22297 				}
22298 			}
22299 		}
22300 
22301 impossibleteachersmaxhoursdaily:
22302 		if(!okteachersmaxhoursdaily){
22303 			if(updateSubgroups || updateTeachers)
22304 				removeAiFromNewTimetable(ai, act, d, h);
22305 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
22306 
22307 			nConflActivities[newtime]=MAX_ACTIVITIES;
22308 			continue;
22309 		}
22310 
22311 /////////////////////////////////////////////////////////////////////////////////////////////
22312 
22313 		//allowed from teachers max hours continuously
22314 
22315 		okteachersmaxhourscontinuously=true;
22316 
22317 		for(int tch : qAsConst(act->iTeachersList)){
22318 			for(int count=0; count<2; count++){
22319 				int limitHoursCont;
22320 				double percentage;
22321 				if(count==0){
22322 					limitHoursCont=teachersMaxHoursContinuouslyMaxHours1[tch];
22323 					percentage=teachersMaxHoursContinuouslyPercentages1[tch];
22324 				}
22325 				else{
22326 					limitHoursCont=teachersMaxHoursContinuouslyMaxHours2[tch];
22327 					percentage=teachersMaxHoursContinuouslyPercentages2[tch];
22328 				}
22329 
22330 				if(limitHoursCont<0) //no constraint
22331 					continue;
22332 
22333 				//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
22334 				//	continue;
22335 
22336 				bool increased;
22337 				int h2;
22338 				for(h2=h; h2<h+act->duration; h2++){
22339 					assert(h2<gt.rules.nHoursPerDay);
22340 					if(teachersTimetable(tch,d,h2)==-1)
22341 						break;
22342 				}
22343 				if(h2<h+act->duration)
22344 					increased=true;
22345 				else
22346 					increased=false;
22347 
22348 				QList<int> removableActs;
22349 
22350 				int nc=act->duration;
22351 				for(h2=h-1; h2>=0; h2--){
22352 					int ai2=teachersTimetable(tch,d,h2);
22353 					assert(ai2==newTeachersTimetable(tch,d,h2));
22354 					assert(ai2!=ai);
22355 					if(ai2>=0 && !conflActivities[newtime].contains(ai2)){
22356 						nc++;
22357 
22358 						if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
22359 							removableActs.append(ai2);
22360 					}
22361 					else
22362 						break;
22363 				}
22364 				for(h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
22365 					int ai2=teachersTimetable(tch,d,h2);
22366 					assert(ai2==newTeachersTimetable(tch,d,h2));
22367 					assert(ai2!=ai);
22368 					if(ai2>=0 && !conflActivities[newtime].contains(ai2)){
22369 						nc++;
22370 
22371 						if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
22372 							removableActs.append(ai2);
22373 					}
22374 					else
22375 						break;
22376 				}
22377 
22378 				if(!increased && percentage==100.0)
22379 					assert(nc<=limitHoursCont);
22380 
22381 				if(!increased || nc<=limitHoursCont) //OK
22382 					continue;
22383 
22384 				assert(limitHoursCont>=0);
22385 
22386 				if(!skipRandom(percentage) && increased){
22387 					if(act->duration>limitHoursCont){
22388 						okteachersmaxhourscontinuously=false;
22389 						goto impossibleteachersmaxhourscontinuously;
22390 					}
22391 
22392 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
22393 						okteachersmaxhourscontinuously=false;
22394 						goto impossibleteachersmaxhourscontinuously;
22395 					}
22396 
22397 					while(true){
22398 						if(removableActs.count()==0){
22399 							okteachersmaxhourscontinuously=false;
22400 							goto impossibleteachersmaxhourscontinuously;
22401 						}
22402 
22403 						int j=-1;
22404 
22405 						if(level==0){
22406 							int optMinWrong=INF;
22407 
22408 							QList<int> tl;
22409 
22410 							for(int q=0; q<removableActs.count(); q++){
22411 								int ai2=removableActs.at(q);
22412 								if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
22413 								 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
22414 								}
22415 							}
22416 
22417 							for(int q=0; q<removableActs.count(); q++){
22418 								int ai2=removableActs.at(q);
22419 								if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
22420 									tl.append(q);
22421 							}
22422 
22423 							assert(tl.count()>=1);
22424 							j=tl.at(rng.intMRG32k3a(tl.count()));
22425 
22426 							assert(j>=0 && j<removableActs.count());
22427 						}
22428 						else{
22429 							j=rng.intMRG32k3a(removableActs.count());
22430 						}
22431 
22432 						assert(j>=0);
22433 
22434 						int ai2=removableActs.at(j);
22435 
22436 						int t=removableActs.removeAll(ai2);
22437 						assert(t==1);
22438 
22439 						assert(!conflActivities[newtime].contains(ai2));
22440 						conflActivities[newtime].append(ai2);
22441 						nConflActivities[newtime]++;
22442 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
22443 
22444 						////////////
22445 						removableActs.clear();
22446 
22447 						int nc=act->duration;
22448 						int h2;
22449 						for(h2=h-1; h2>=0; h2--){
22450 							int ai2=teachersTimetable(tch,d,h2);
22451 							assert(ai2==newTeachersTimetable(tch,d,h2));
22452 							assert(ai2!=ai);
22453 							if(ai2>=0 && !conflActivities[newtime].contains(ai2)){
22454 								nc++;
22455 
22456 								if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
22457 									removableActs.append(ai2);
22458 							}
22459 							else
22460 								break;
22461 						}
22462 						for(h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
22463 							int ai2=teachersTimetable(tch,d,h2);
22464 							assert(ai2==newTeachersTimetable(tch,d,h2));
22465 							assert(ai2!=ai);
22466 							if(ai2>=0 && !conflActivities[newtime].contains(ai2)){
22467 								nc++;
22468 
22469 								if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
22470 									removableActs.append(ai2);
22471 							}
22472 							else
22473 								break;
22474 						}
22475 
22476 						if(nc<=limitHoursCont) //OK
22477 							break;
22478 						////////////
22479 					}
22480 				}
22481 			}
22482 		}
22483 
22484 impossibleteachersmaxhourscontinuously:
22485 		if(!okteachersmaxhourscontinuously){
22486 			if(updateSubgroups || updateTeachers)
22487 				removeAiFromNewTimetable(ai, act, d, h);
22488 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
22489 
22490 			nConflActivities[newtime]=MAX_ACTIVITIES;
22491 			continue;
22492 		}
22493 
22494 /////////////////////////////////////////////////////////////////////////////////////////////
22495 
22496 		//allowed from teachers activity tag max hours daily
22497 
22498 		//!!!NOT PERFECT, there is room for improvement
22499 
22500 		okteachersactivitytagmaxhoursdaily=true;
22501 
22502 		if(haveTeachersActivityTagMaxHoursDaily){
22503 
22504 			for(int tch : qAsConst(act->iTeachersList)){
22505 				for(int cnt=0; cnt<teachersActivityTagMaxHoursDailyMaxHours[tch].count(); cnt++){
22506 					int activityTag=teachersActivityTagMaxHoursDailyActivityTag[tch].at(cnt);
22507 
22508 					if(!gt.rules.internalActivitiesList[ai].iActivityTagsSet.contains(activityTag))
22509 						continue;
22510 
22511 					int limitHoursDaily=teachersActivityTagMaxHoursDailyMaxHours[tch].at(cnt);
22512 					double percentage=teachersActivityTagMaxHoursDailyPercentage[tch].at(cnt);
22513 
22514 					assert(limitHoursDaily>=0);
22515 					assert(percentage>=0);
22516 					assert(activityTag>=0 /*&& activityTag<gt.rules.nInternalActivityTags*/);
22517 
22518 					//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
22519 					//	continue;
22520 
22521 					bool increased;
22522 
22523 					int nold=0, nnew=0;
22524 					///////////
22525 					for(int h2=0; h2<h; h2++){
22526 						if(newTeachersTimetable(tch,d,h2)>=0){
22527 							int ai2=newTeachersTimetable(tch,d,h2);
22528 							assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22529 							Activity* act=&gt.rules.internalActivitiesList[ai2];
22530 							if(act->iActivityTagsSet.contains(activityTag)){
22531 								nold++;
22532 								nnew++;
22533 							}
22534 						}
22535 					}
22536 					for(int h2=h; h2<h+act->duration; h2++){
22537 						if(oldTeachersTimetable(tch,d,h2)>=0){
22538 							int ai2=oldTeachersTimetable(tch,d,h2);
22539 							assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22540 							Activity* act=&gt.rules.internalActivitiesList[ai2];
22541 							if(act->iActivityTagsSet.contains(activityTag))
22542 								nold++;
22543 						}
22544 					}
22545 					for(int h2=h; h2<h+act->duration; h2++){
22546 						if(newTeachersTimetable(tch,d,h2)>=0){
22547 							int ai2=newTeachersTimetable(tch,d,h2);
22548 							assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22549 							Activity* act=&gt.rules.internalActivitiesList[ai2];
22550 							if(act->iActivityTagsSet.contains(activityTag))
22551 								nnew++;
22552 						}
22553 					}
22554 					for(int h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
22555 						if(newTeachersTimetable(tch,d,h2)>=0){
22556 							int ai2=newTeachersTimetable(tch,d,h2);
22557 							assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22558 							Activity* act=&gt.rules.internalActivitiesList[ai2];
22559 							if(act->iActivityTagsSet.contains(activityTag)){
22560 								nold++;
22561 								nnew++;
22562 							}
22563 						}
22564 					}
22565 					/////////
22566 					if(nold<nnew)
22567 						increased=true;
22568 					else
22569 						increased=false;
22570 
22571 					if(percentage==100.0)
22572 						assert(nold<=limitHoursDaily);
22573 					if(!increased && percentage==100.0)
22574 						assert(nnew<=limitHoursDaily);
22575 
22576 					if(!increased || nnew<=limitHoursDaily) //OK
22577 						continue;
22578 
22579 					assert(limitHoursDaily>=0);
22580 
22581 					assert(increased);
22582 					assert(nnew>limitHoursDaily);
22583 					if(!skipRandom(percentage)){
22584 						if(act->duration>limitHoursDaily){
22585 							okteachersactivitytagmaxhoursdaily=false;
22586 							goto impossibleteachersactivitytagmaxhoursdaily;
22587 						}
22588 
22589 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
22590 							okteachersactivitytagmaxhoursdaily=false;
22591 							goto impossibleteachersactivitytagmaxhoursdaily;
22592 						}
22593 
22594 						getTchTimetable(tch, conflActivities[newtime]);
22595 						tchGetNHoursGaps(tch);
22596 
22597 						while(true){
22598 							int ncrt=0;
22599 							for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
22600 								if(tchTimetable(d,h2)>=0){
22601 									int ai2=tchTimetable(d,h2);
22602 									assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22603 									Activity* act=&gt.rules.internalActivitiesList[ai2];
22604 									if(act->iActivityTagsSet.contains(activityTag))
22605 										ncrt++;
22606 								}
22607 							}
22608 
22609 							if(ncrt<=limitHoursDaily)
22610 								break;
22611 
22612 							int ai2=-1;
22613 
22614 							bool ke=teacherRemoveAnActivityFromAnywhereCertainDayCertainActivityTag(d, activityTag, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
22615 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
22616 
22617 							if(!ke){
22618 								if(level==0){
22619 									//...this is not too good, but hopefully there is no problem
22620 								}
22621 								okteachersactivitytagmaxhoursdaily=false;
22622 								goto impossibleteachersactivitytagmaxhoursdaily;
22623 							}
22624 
22625 							assert(ai2>=0);
22626 
22627 							assert(gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag));
22628 
22629 							removeAi2FromTchTimetable(ai2);
22630 							updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
22631 						}
22632 					}
22633 				}
22634 			}
22635 
22636 		}
22637 
22638 impossibleteachersactivitytagmaxhoursdaily:
22639 		if(!okteachersactivitytagmaxhoursdaily){
22640 			if(updateSubgroups || updateTeachers)
22641 				removeAiFromNewTimetable(ai, act, d, h);
22642 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
22643 
22644 			nConflActivities[newtime]=MAX_ACTIVITIES;
22645 			continue;
22646 		}
22647 
22648 /////////////////////////////////////////////////////////////////////////////////////////////
22649 
22650 		//allowed from teachers activity tag max hours daily for real days
22651 
22652 		//!!!NOT PERFECT, there is room for improvement
22653 
22654 		okteachersactivitytagmaxhoursdailyrealdays=true;
22655 
22656 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
22657 			if(haveTeachersActivityTagMaxHoursDailyRealDays){
22658 				for(int tch : qAsConst(act->iTeachersList)){
22659 					for(int cnt=0; cnt<teachersActivityTagMaxHoursDailyRealDaysMaxHours[tch].count(); cnt++){
22660 						int activityTag=teachersActivityTagMaxHoursDailyRealDaysActivityTag[tch].at(cnt);
22661 
22662 						if(!gt.rules.internalActivitiesList[ai].iActivityTagsSet.contains(activityTag))
22663 							continue;
22664 
22665 						int limitHoursDaily=teachersActivityTagMaxHoursDailyRealDaysMaxHours[tch].at(cnt);
22666 						double percentage=teachersActivityTagMaxHoursDailyRealDaysPercentage[tch].at(cnt);
22667 
22668 						assert(limitHoursDaily>=0);
22669 						assert(percentage>=0);
22670 						assert(activityTag>=0 /*&& activityTag<gt.rules.nInternalActivityTags*/);
22671 
22672 						//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
22673 						//	continue;
22674 
22675 						bool increased;
22676 
22677 						int nold=0, nnew=0;
22678 						///////////
22679 						for(int h2=0; h2<h; h2++){
22680 							if(newTeachersTimetable(tch,d,h2)>=0){
22681 								int ai2=newTeachersTimetable(tch,d,h2);
22682 								assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22683 								Activity* act=&gt.rules.internalActivitiesList[ai2];
22684 								if(act->iActivityTagsSet.contains(activityTag)){
22685 									nold++;
22686 									nnew++;
22687 								}
22688 							}
22689 						}
22690 						for(int h2=h; h2<h+act->duration; h2++){
22691 							if(oldTeachersTimetable(tch,d,h2)>=0){
22692 								int ai2=oldTeachersTimetable(tch,d,h2);
22693 								assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22694 								Activity* act=&gt.rules.internalActivitiesList[ai2];
22695 								if(act->iActivityTagsSet.contains(activityTag))
22696 									nold++;
22697 							}
22698 						}
22699 						for(int h2=h; h2<h+act->duration; h2++){
22700 							if(newTeachersTimetable(tch,d,h2)>=0){
22701 								int ai2=newTeachersTimetable(tch,d,h2);
22702 								assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22703 								Activity* act=&gt.rules.internalActivitiesList[ai2];
22704 								if(act->iActivityTagsSet.contains(activityTag))
22705 									nnew++;
22706 							}
22707 						}
22708 						for(int h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
22709 							if(newTeachersTimetable(tch,d,h2)>=0){
22710 								int ai2=newTeachersTimetable(tch,d,h2);
22711 								assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22712 								Activity* act=&gt.rules.internalActivitiesList[ai2];
22713 								if(act->iActivityTagsSet.contains(activityTag)){
22714 									nold++;
22715 									nnew++;
22716 								}
22717 							}
22718 						}
22719 						for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
22720 							if(newTeachersTimetable(tch,dpair,h2)>=0){
22721 								int ai2=newTeachersTimetable(tch,dpair,h2);
22722 								assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22723 								Activity* act=&gt.rules.internalActivitiesList[ai2];
22724 								if(act->iActivityTagsSet.contains(activityTag)){
22725 									nold++;
22726 									nnew++;
22727 								}
22728 							}
22729 						}
22730 						/////////
22731 						if(nold<nnew)
22732 							increased=true;
22733 						else
22734 							increased=false;
22735 
22736 						if(percentage==100.0)
22737 							assert(nold<=limitHoursDaily);
22738 						if(!increased && percentage==100.0)
22739 							assert(nnew<=limitHoursDaily);
22740 
22741 						if(!increased || nnew<=limitHoursDaily) //OK
22742 							continue;
22743 
22744 						assert(limitHoursDaily>=0);
22745 
22746 						assert(increased);
22747 						assert(nnew>limitHoursDaily);
22748 						if(!skipRandom(percentage)){
22749 							if(act->duration>limitHoursDaily){
22750 								okteachersactivitytagmaxhoursdailyrealdays=false;
22751 								goto impossibleteachersactivitytagmaxhoursdailyrealdays;
22752 							}
22753 
22754 							if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
22755 								okteachersactivitytagmaxhoursdailyrealdays=false;
22756 								goto impossibleteachersactivitytagmaxhoursdailyrealdays;
22757 							}
22758 
22759 							getTchTimetable(tch, conflActivities[newtime]);
22760 							tchGetNHoursGaps(tch);
22761 
22762 							while(true){
22763 								int ncrt=0;
22764 								for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
22765 									if(tchTimetable(d,h2)>=0){
22766 										int ai2=tchTimetable(d,h2);
22767 										assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22768 										Activity* act=&gt.rules.internalActivitiesList[ai2];
22769 										if(act->iActivityTagsSet.contains(activityTag))
22770 											ncrt++;
22771 									}
22772 								}
22773 								for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
22774 									if(tchTimetable(dpair,h2)>=0){
22775 										int ai2=tchTimetable(dpair,h2);
22776 										assert(ai2>=0 && ai2<gt.rules.nInternalActivities);
22777 										Activity* act=&gt.rules.internalActivitiesList[ai2];
22778 										if(act->iActivityTagsSet.contains(activityTag))
22779 											ncrt++;
22780 									}
22781 								}
22782 
22783 								if(ncrt<=limitHoursDaily)
22784 									break;
22785 
22786 								int ai2=-1;
22787 
22788 								bool ke=teacherRemoveAnActivityFromAnywhereCertainDayDayPairCertainActivityTag(d, dpair, activityTag, level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
22789 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
22790 
22791 								if(!ke){
22792 									if(level==0){
22793 										//...this is not too good, but hopefully there is no problem
22794 									}
22795 									okteachersactivitytagmaxhoursdailyrealdays=false;
22796 									goto impossibleteachersactivitytagmaxhoursdailyrealdays;
22797 								}
22798 
22799 								assert(ai2>=0);
22800 
22801 								assert(gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag));
22802 
22803 								removeAi2FromTchTimetable(ai2);
22804 								updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
22805 							}
22806 						}
22807 					}
22808 				}
22809 			}
22810 		}
22811 
22812 impossibleteachersactivitytagmaxhoursdailyrealdays:
22813 		if(!okteachersactivitytagmaxhoursdailyrealdays){
22814 			if(updateSubgroups || updateTeachers)
22815 				removeAiFromNewTimetable(ai, act, d, h);
22816 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
22817 
22818 			nConflActivities[newtime]=MAX_ACTIVITIES;
22819 			continue;
22820 		}
22821 
22822 /////////////////////////////////////////////////////////////////////////////////////////////
22823 
22824 		//allowed from teachers activity tag max hours continuously
22825 		okteachersactivitytagmaxhourscontinuously=true;
22826 
22827 		if(haveTeachersActivityTagMaxHoursContinuously){
22828 
22829 			for(int tch : qAsConst(act->iTeachersList)){
22830 				for(int cnt=0; cnt<teachersActivityTagMaxHoursContinuouslyMaxHours[tch].count(); cnt++){
22831 					int activityTag=teachersActivityTagMaxHoursContinuouslyActivityTag[tch].at(cnt);
22832 
22833 					if(!gt.rules.internalActivitiesList[ai].iActivityTagsSet.contains(activityTag))
22834 						continue;
22835 
22836 					int limitHoursCont=teachersActivityTagMaxHoursContinuouslyMaxHours[tch].at(cnt);
22837 					double percentage=teachersActivityTagMaxHoursContinuouslyPercentage[tch].at(cnt);
22838 
22839 					assert(limitHoursCont>=0);
22840 					assert(percentage>=0);
22841 					assert(activityTag>=0/* && activityTag<gt.rules.nInternalActivityTags*/);
22842 
22843 					//if(fixedTimeActivity[ai] && percentage<100.0) //added on 21 Feb. 2009 in FET 5.9.1, to solve a bug of impossible timetables for fixed timetables
22844 					//	continue;
22845 
22846 					bool increased;
22847 					int h2;
22848 					for(h2=h; h2<h+act->duration; h2++){
22849 						assert(h2<gt.rules.nHoursPerDay);
22850 						if(teachersTimetable(tch,d,h2)==-1)
22851 							break;
22852 						int ai2=teachersTimetable(tch,d,h2);
22853 						if(!gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag))
22854 							break;
22855 					}
22856 					if(h2<h+act->duration)
22857 						increased=true;
22858 					else
22859 						increased=false;
22860 
22861 					QList<int> removableActs;
22862 
22863 					int nc=act->duration;
22864 					for(h2=h-1; h2>=0; h2--){
22865 						int ai2=teachersTimetable(tch,d,h2);
22866 						assert(ai2==newTeachersTimetable(tch,d,h2));
22867 						assert(ai2!=ai);
22868 						if(ai2<0)
22869 							break;
22870 						if(ai2>=0 && !conflActivities[newtime].contains(ai2) &&
22871 						 gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag)){
22872 							nc++;
22873 
22874 							if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
22875 								removableActs.append(ai2);
22876 						}
22877 						else
22878 							break;
22879 					}
22880 					for(h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
22881 						int ai2=teachersTimetable(tch,d,h2);
22882 						assert(ai2==newTeachersTimetable(tch,d,h2));
22883 						assert(ai2!=ai);
22884 						if(ai2<0)
22885 							break;
22886 						if(ai2>=0 && !conflActivities[newtime].contains(ai2) &&
22887 						 gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag)){
22888 							nc++;
22889 
22890 							if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
22891 								removableActs.append(ai2);
22892 						}
22893 						else
22894 							break;
22895 					}
22896 
22897 					if(!increased && percentage==100.0)
22898 						assert(nc<=limitHoursCont);
22899 
22900 					if(!increased || nc<=limitHoursCont) //OK
22901 						continue;
22902 
22903 					assert(limitHoursCont>=0);
22904 
22905 					if(!skipRandom(percentage) && increased){
22906 						if(act->duration>limitHoursCont){
22907 							okteachersactivitytagmaxhourscontinuously=false;
22908 							goto impossibleteachersactivitytagmaxhourscontinuously;
22909 						}
22910 
22911 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
22912 							okteachersactivitytagmaxhourscontinuously=false;
22913 							goto impossibleteachersactivitytagmaxhourscontinuously;
22914 						}
22915 
22916 						while(true){
22917 							if(removableActs.count()==0){
22918 								okteachersactivitytagmaxhourscontinuously=false;
22919 								goto impossibleteachersactivitytagmaxhourscontinuously;
22920 							}
22921 
22922 							int j=-1;
22923 
22924 							if(level==0){
22925 								int optMinWrong=INF;
22926 
22927 								QList<int> tl;
22928 
22929 								for(int q=0; q<removableActs.count(); q++){
22930 									int ai2=removableActs.at(q);
22931 									if(optMinWrong>triedRemovals(ai2,c.times[ai2])){
22932 									 	optMinWrong=triedRemovals(ai2,c.times[ai2]);
22933 									}
22934 								}
22935 
22936 								for(int q=0; q<removableActs.count(); q++){
22937 									int ai2=removableActs.at(q);
22938 									if(optMinWrong==triedRemovals(ai2,c.times[ai2]))
22939 										tl.append(q);
22940 								}
22941 
22942 								assert(tl.count()>=1);
22943 								j=tl.at(rng.intMRG32k3a(tl.count()));
22944 
22945 								assert(j>=0 && j<removableActs.count());
22946 							}
22947 							else{
22948 								j=rng.intMRG32k3a(removableActs.count());
22949 							}
22950 
22951 							assert(j>=0);
22952 
22953 							int ai2=removableActs.at(j);
22954 
22955 							int t=removableActs.removeAll(ai2);
22956 							assert(t==1);
22957 
22958 							assert(!conflActivities[newtime].contains(ai2));
22959 							conflActivities[newtime].append(ai2);
22960 							nConflActivities[newtime]++;
22961 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
22962 
22963 							////////////
22964 							removableActs.clear();
22965 
22966 							int nc=act->duration;
22967 							int h2;
22968 							for(h2=h-1; h2>=0; h2--){
22969 								int ai2=teachersTimetable(tch,d,h2);
22970 								assert(ai2==newTeachersTimetable(tch,d,h2));
22971 								assert(ai2!=ai);
22972 								if(ai2<0)
22973 									break;
22974 								if(ai2>=0 && !conflActivities[newtime].contains(ai2) &&
22975 								 gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag)){
22976 									nc++;
22977 
22978 									if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
22979 										removableActs.append(ai2);
22980 								}
22981 								else
22982 									break;
22983 							}
22984 							for(h2=h+act->duration; h2<gt.rules.nHoursPerDay; h2++){
22985 								int ai2=teachersTimetable(tch,d,h2);
22986 								assert(ai2==newTeachersTimetable(tch,d,h2));
22987 								assert(ai2!=ai);
22988 								if(ai2<0)
22989 									break;
22990 								if(ai2>=0 && !conflActivities[newtime].contains(ai2) &&
22991 								 gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(activityTag)){
22992 									nc++;
22993 
22994 									if(!removableActs.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
22995 										removableActs.append(ai2);
22996 								}
22997 								else
22998 									break;
22999 							}
23000 
23001 							if(nc<=limitHoursCont) //OK
23002 								break;
23003 							////////////
23004 						}
23005 					}
23006 				}
23007 			}
23008 
23009 		}
23010 
23011 impossibleteachersactivitytagmaxhourscontinuously:
23012 		if(!okteachersactivitytagmaxhourscontinuously){
23013 			if(updateSubgroups || updateTeachers)
23014 				removeAiFromNewTimetable(ai, act, d, h);
23015 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
23016 
23017 			nConflActivities[newtime]=MAX_ACTIVITIES;
23018 			continue;
23019 		}
23020 
23021 /////////////////////////////////////////////////////////////////////////////////////////////
23022 
23023 		/////////begin teacher(s) min hours daily
23024 
23025 		//I think it is best to put this routine after max days per week
23026 
23027 		//Added on 11 September 2009: takes care of teachers min days per week
23028 
23029 		okteachersminhoursdaily=true;
23030 		if(gt.rules.mode!=MORNINGS_AFTERNOONS){
23031 			for(int tch : qAsConst(act->iTeachersList)){
23032 				if(teachersMinHoursDailyMinHours[tch][MIN_HOURS_DAILY_INDEX_IN_ARRAY]>=0){
23033 					assert(teachersMinHoursDailyPercentages[tch][MIN_HOURS_DAILY_INDEX_IN_ARRAY]==100);
23034 
23035 					int minLimitTch=teachersMinHoursDailyMinHours[tch][MIN_HOURS_DAILY_INDEX_IN_ARRAY];
23036 
23037 					bool skip=skipRandom(teachersMinHoursDailyPercentages[tch][MIN_HOURS_DAILY_INDEX_IN_ARRAY]);
23038 					if(!skip){
23039 						//preliminary test
23040 						bool _ok;
23041 						if(teachersMaxGapsPerWeekPercentage[tch]==-1){
23042 							int _reqHours=0;
23043 							int _usedDays=0;
23044 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
23045 								if(newTeachersDayNHours(tch,d2)>0){
23046 									_usedDays++;
23047 									if(teachersMaxGapsPerDayPercentage[tch]==-1){
23048 										_reqHours+=max(newTeachersDayNHours(tch,d2), minLimitTch);
23049 									}
23050 									else{
23051 										int nh=max(0, newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch]);
23052 										_reqHours+=max(newTeachersDayNHours(tch,d2)+nh, minLimitTch);
23053 									}
23054 								}
23055 
23056 							if(teachersMinDaysPerWeekPercentages[tch]>=0){
23057 								assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek);
23058 								assert(teachersMinDaysPerWeekPercentages[tch]==100.0);
23059 								int _md=teachersMinDaysPerWeekMinDays[tch];
23060 								assert(_md>=0);
23061 								if(_md>_usedDays)
23062 									_reqHours+=(_md-_usedDays)*minLimitTch;
23063 							}
23064 
23065 							if(_reqHours <= nHoursPerTeacher[tch])
23066 								_ok=true; //ok
23067 							else
23068 								_ok=false;
23069 						}
23070 						else{
23071 							int remG=0;
23072 							int totalH=0;
23073 							int _usedDays=0;
23074 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
23075 								int remGDay=newTeachersDayNGaps(tch,d2);
23076 								int h=newTeachersDayNHours(tch,d2);
23077 								if(h>0){
23078 									_usedDays++;
23079 								}
23080 								int addh;
23081 								if(teachersMaxGapsPerDayPercentage[tch]>=0)
23082 									addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
23083 								else
23084 									addh=0;
23085 								remGDay-=addh;
23086 								assert(remGDay>=0);
23087 								h+=addh;
23088 								if(h>0){
23089 									if(h<minLimitTch){
23090 										remGDay-=minLimitTch-h;
23091 										totalH+=minLimitTch;
23092 									}
23093 									else
23094 										totalH+=h;
23095 								}
23096 								if(remGDay>0)
23097 									remG+=remGDay;
23098 							}
23099 
23100 							if(teachersMinDaysPerWeekPercentages[tch]>=0){
23101 								assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek);
23102 								assert(teachersMinDaysPerWeekPercentages[tch]==100.0);
23103 								int _md=teachersMinDaysPerWeekMinDays[tch];
23104 								assert(_md>=0);
23105 								if(_md>_usedDays)
23106 									totalH+=(_md-_usedDays)*minLimitTch;
23107 							}
23108 
23109 							if(remG+totalH<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
23110 							  && totalH<=nHoursPerTeacher[tch])
23111 								_ok=true;
23112 							else
23113 								_ok=false;
23114 						}
23115 
23116 						if(_ok)
23117 							continue;
23118 
23119 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
23120 							okteachersminhoursdaily=false;
23121 							goto impossibleteachersminhoursdaily;
23122 						}
23123 
23124 						getTchTimetable(tch, conflActivities[newtime]);
23125 						tchGetNHoursGaps(tch);
23126 
23127 						for(;;){
23128 							bool ok;
23129 							if(teachersMaxGapsPerWeekPercentage[tch]==-1){
23130 								int _reqHours=0;
23131 								int _usedDays=0;
23132 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
23133 									if(tchDayNHours[d2]>0){
23134 										_usedDays++;
23135 										if(teachersMaxGapsPerDayPercentage[tch]==-1){
23136 											_reqHours+=max(tchDayNHours[d2], minLimitTch);
23137 										}
23138 										else{
23139 											int nh=max(0, tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch]);
23140 											_reqHours+=max(tchDayNHours[d2]+nh, minLimitTch);
23141 										}
23142 									}
23143 
23144 								if(teachersMinDaysPerWeekPercentages[tch]>=0){
23145 									assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek);
23146 									assert(teachersMinDaysPerWeekPercentages[tch]==100.0);
23147 									int _md=teachersMinDaysPerWeekMinDays[tch];
23148 									assert(_md>=0);
23149 									if(_md>_usedDays)
23150 										_reqHours+=(_md-_usedDays)*minLimitTch;
23151 								}
23152 
23153 								if(_reqHours <= nHoursPerTeacher[tch])
23154 									ok=true; //ok
23155 								else
23156 									ok=false;
23157 							}
23158 							else{
23159 								int remG=0;
23160 								int totalH=0;
23161 								int _usedDays=0;
23162 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
23163 									int remGDay=tchDayNGaps[d2];
23164 									int h=tchDayNHours[d2];
23165 									if(h>0)
23166 										_usedDays++;
23167 									int addh;
23168 									if(teachersMaxGapsPerDayPercentage[tch]>=0)
23169 										addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
23170 									else
23171 										addh=0;
23172 									remGDay-=addh;
23173 									assert(remGDay>=0);
23174 									h+=addh;
23175 									if(h>0){
23176 										if(h<minLimitTch){
23177 											remGDay-=minLimitTch-h;
23178 											totalH+=minLimitTch;
23179 										}
23180 										else
23181 											totalH+=h;
23182 									}
23183 									if(remGDay>0)
23184 										remG+=remGDay;
23185 								}
23186 								if(teachersMinDaysPerWeekPercentages[tch]>=0){
23187 									assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek);
23188 									assert(teachersMinDaysPerWeekPercentages[tch]==100.0);
23189 									int _md=teachersMinDaysPerWeekMinDays[tch];
23190 									assert(_md>=0);
23191 									if(_md>_usedDays)
23192 										totalH+=(_md-_usedDays)*minLimitTch;
23193 								}
23194 
23195 								if(remG+totalH<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
23196 								  && totalH<=nHoursPerTeacher[tch])
23197 									ok=true;
23198 								else
23199 									ok=false;
23200 							}
23201 
23202 							if(ok)
23203 								break;
23204 
23205 							int ai2=-1;
23206 
23207 							bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
23208 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
23209 							if(!k){
23210 								bool ka=teacherRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
23211 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
23212 
23213 								if(!ka){
23214 									if(level==0){
23215 										//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
23216 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
23217 									}
23218 									okteachersminhoursdaily=false;
23219 									goto impossibleteachersminhoursdaily;
23220 								}
23221 							}
23222 
23223 							assert(ai2>=0);
23224 
23225 							removeAi2FromTchTimetable(ai2);
23226 							updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
23227 						}
23228 					}
23229 				}
23230 			}
23231 		}
23232 		else{
23233 			for(int tch : qAsConst(act->iTeachersList)){
23234 				if(teachersMinHoursDailyMinHours[tch][1]>=0){
23235 					assert(teachersMinHoursDailyPercentages[tch][1]==100);
23236 
23237 					bool skip=skipRandom(teachersMinHoursDailyPercentages[tch][1]);
23238 					if(!skip){
23239 						//preliminary test
23240 						bool _ok;
23241 						if(teachersMaxGapsPerWeekPercentage[tch]==-1){
23242 							int _reqHours=0;
23243 							int _usedDays=0;
23244 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
23245 								if(newTeachersDayNHours(tch,d2)>0){
23246 									//_usedDays++;
23247 									if(teacherNoGapsPerAfternoon(tch)){
23248 										//2019-09-13 - max gaps per afternoon = 0
23249 										if(teachersMaxGapsPerDayPercentage[tch]==-1){
23250 											if(d2%2==0){ //morning
23251 												_reqHours+=max(newTeachersDayNHours(tch,d2), teachersMinHoursDailyMinHours[tch][d2%2]);
23252 											}
23253 											else{ //afternoon
23254 												_reqHours+=max(newTeachersDayNHours(tch,d2)+newTeachersDayNGaps(tch,d2), teachersMinHoursDailyMinHours[tch][d2%2]);
23255 											}
23256 										}
23257 										else{
23258 											if(d2%2==0){ //morning
23259 												int nh=max(0, newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch]);
23260 												_reqHours+=max(newTeachersDayNHours(tch,d2)+nh, teachersMinHoursDailyMinHours[tch][d2%2]);
23261 											}
23262 											else{ //afternoon
23263 												int nh=max(0, newTeachersDayNGaps(tch,d2)-0);
23264 												_reqHours+=max(newTeachersDayNHours(tch,d2)+nh, teachersMinHoursDailyMinHours[tch][d2%2]);
23265 											}
23266 										}
23267 									}
23268 									else{
23269 										if(teachersMaxGapsPerDayPercentage[tch]==-1){
23270 											_reqHours+=max(newTeachersDayNHours(tch,d2), teachersMinHoursDailyMinHours[tch][d2%2]);
23271 										}
23272 										else{
23273 											int nh=max(0, newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch]);
23274 											_reqHours+=max(newTeachersDayNHours(tch,d2)+nh, teachersMinHoursDailyMinHours[tch][d2%2]);
23275 										}
23276 									}
23277 								}
23278 							for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
23279 								if(newTeachersDayNHours(tch,2*d2)>0 || newTeachersDayNHours(tch,2*d2+1)>0)
23280 									_usedDays++;
23281 
23282 							if(teachersMinRealDaysPerWeekPercentages[tch]>=0){
23283 								assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
23284 								assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
23285 								int _md=teachersMinRealDaysPerWeekMinDays[tch];
23286 								assert(_md>=0);
23287 								if(_md>_usedDays)
23288 									_reqHours+=(_md-_usedDays)*teachersMinHoursDailyMinHours[tch][1];
23289 							}
23290 
23291 							if(_reqHours <= nHoursPerTeacher[tch])
23292 								_ok=true; //ok
23293 							else
23294 								_ok=false;
23295 						}
23296 						else{
23297 							int remG=0;
23298 							int totalH=0;
23299 							int _usedDays=0;
23300 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
23301 								int remGDay=newTeachersDayNGaps(tch,d2);
23302 								int h=newTeachersDayNHours(tch,d2);
23303 								/*if(h>0){
23304 									_usedDays++;
23305 								}*/
23306 								int addh;
23307 								//2019-09-13 - max gaps per afternoon = 0
23308 								if(teacherNoGapsPerAfternoon(tch)){
23309 									if(teachersMaxGapsPerDayPercentage[tch]>=0){
23310 										if(d2%2==0){ //morning
23311 											addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
23312 										}
23313 										else{ //afternoon
23314 											addh=max(0, remGDay-0);
23315 										}
23316 									}
23317 									else{
23318 										if(d2%2==0){ //morning
23319 											addh=0;
23320 										}
23321 										else{
23322 											addh=max(0, remGDay-0);
23323 										}
23324 									}
23325 								}
23326 								else{
23327 									if(teachersMaxGapsPerDayPercentage[tch]>=0)
23328 										addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
23329 									else
23330 										addh=0;
23331 								}
23332 								remGDay-=addh;
23333 								assert(remGDay>=0);
23334 								h+=addh;
23335 								if(h>0){
23336 									if(h<teachersMinHoursDailyMinHours[tch][d2%2]){
23337 										remGDay-=teachersMinHoursDailyMinHours[tch][d2%2]-h;
23338 										totalH+=teachersMinHoursDailyMinHours[tch][d2%2];
23339 									}
23340 									else
23341 										totalH+=h;
23342 								}
23343 								if(remGDay>0)
23344 									remG+=remGDay;
23345 							}
23346 							for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
23347 								if(newTeachersDayNHours(tch,2*d2)>0 || newTeachersDayNHours(tch,2*d2+1)>0)
23348 									_usedDays++;
23349 
23350 							if(teachersMinRealDaysPerWeekPercentages[tch]>=0){
23351 								assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
23352 								assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
23353 								int _md=teachersMinRealDaysPerWeekMinDays[tch];
23354 								assert(_md>=0);
23355 								if(_md>_usedDays)
23356 									totalH+=(_md-_usedDays)*teachersMinHoursDailyMinHours[tch][1];
23357 							}
23358 
23359 							if(remG+totalH<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
23360 							  && totalH<=nHoursPerTeacher[tch])
23361 								_ok=true;
23362 							else
23363 								_ok=false;
23364 						}
23365 
23366 						if(_ok)
23367 							continue;
23368 
23369 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
23370 							okteachersminhoursdaily=false;
23371 							goto impossibleteachersminhoursdaily;
23372 						}
23373 
23374 						getTchTimetable(tch, conflActivities[newtime]);
23375 						tchGetNHoursGaps(tch);
23376 
23377 						for(;;){
23378 							bool ok;
23379 							if(teachersMaxGapsPerWeekPercentage[tch]==-1){
23380 								int _reqHours=0;
23381 								int _usedDays=0;
23382 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
23383 									if(tchDayNHours[d2]>0){
23384 										//_usedDays++;
23385 										if(teacherNoGapsPerAfternoon(tch)){
23386 											//2019-09-13 - max gaps per afternoon = 0
23387 											if(teachersMaxGapsPerDayPercentage[tch]==-1){
23388 												if(d2%2==0){ //morning
23389 													_reqHours+=max(tchDayNHours[d2], teachersMinHoursDailyMinHours[tch][d2%2]);
23390 												}
23391 												else{ //afternoon
23392 													_reqHours+=max(tchDayNHours[d2]+tchDayNGaps[d2], teachersMinHoursDailyMinHours[tch][d2%2]);
23393 												}
23394 											}
23395 											else{
23396 												if(d2%2==0){ //morning
23397 													int nh=max(0, tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch]);
23398 													_reqHours+=max(tchDayNHours[d2]+nh, teachersMinHoursDailyMinHours[tch][d2%2]);
23399 												}
23400 												else{ //afternoon
23401 													int nh=max(0, tchDayNGaps[d2]-0);
23402 													_reqHours+=max(tchDayNHours[d2]+nh, teachersMinHoursDailyMinHours[tch][d2%2]);
23403 												}
23404 											}
23405 										}
23406 										else{
23407 											if(teachersMaxGapsPerDayPercentage[tch]==-1){
23408 												_reqHours+=max(tchDayNHours[d2], teachersMinHoursDailyMinHours[tch][d2%2]);
23409 											}
23410 											else{
23411 												int nh=max(0, tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch]);
23412 												_reqHours+=max(tchDayNHours[d2]+nh, teachersMinHoursDailyMinHours[tch][d2%2]);
23413 											}
23414 										}
23415 									}
23416 								for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
23417 									if(tchDayNHours[2*d2]>0 || tchDayNHours[2*d2+1]>0)
23418 										_usedDays++;
23419 
23420 								if(teachersMinRealDaysPerWeekPercentages[tch]>=0){
23421 									assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
23422 									assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
23423 									int _md=teachersMinRealDaysPerWeekMinDays[tch];
23424 									assert(_md>=0);
23425 									if(_md>_usedDays)
23426 										_reqHours+=(_md-_usedDays)*teachersMinHoursDailyMinHours[tch][1];
23427 								}
23428 
23429 								if(_reqHours <= nHoursPerTeacher[tch])
23430 									ok=true; //ok
23431 								else
23432 									ok=false;
23433 							}
23434 							else{
23435 								int remG=0;
23436 								int totalH=0;
23437 								int _usedDays=0;
23438 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
23439 									int remGDay=tchDayNGaps[d2];
23440 									int h=tchDayNHours[d2];
23441 									//if(h>0)
23442 									//	_usedDays++;
23443 									int addh;
23444 									if(teacherNoGapsPerAfternoon(tch)){
23445 										//2019-09-13 - max gaps per afternoon = 0
23446 										if(teachersMaxGapsPerDayPercentage[tch]>=0){
23447 											if(d2%2==0){ //morning
23448 												addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
23449 											}
23450 											else{ //afternoon
23451 												addh=max(0, remGDay-0);
23452 											}
23453 										}
23454 										else{
23455 											if(d2%2==0){ //morning
23456 												addh=0;
23457 											}
23458 											else{ //afternoon
23459 												addh=max(0, remGDay-0);
23460 											}
23461 										}
23462 									}
23463 									else{
23464 										if(teachersMaxGapsPerDayPercentage[tch]>=0)
23465 											addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
23466 										else
23467 											addh=0;
23468 									}
23469 									remGDay-=addh;
23470 									assert(remGDay>=0);
23471 									h+=addh;
23472 									if(h>0){
23473 										if(h<teachersMinHoursDailyMinHours[tch][d2%2]){
23474 											remGDay-=teachersMinHoursDailyMinHours[tch][d2%2]-h;
23475 											totalH+=teachersMinHoursDailyMinHours[tch][d2%2];
23476 										}
23477 										else
23478 											totalH+=h;
23479 									}
23480 									if(remGDay>0)
23481 										remG+=remGDay;
23482 								}
23483 								for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
23484 									if(tchDayNHours[2*d2]>0 || tchDayNHours[2*d2+1]>0)
23485 										_usedDays++;
23486 								if(teachersMinRealDaysPerWeekPercentages[tch]>=0){
23487 									assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
23488 									assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
23489 									int _md=teachersMinRealDaysPerWeekMinDays[tch];
23490 									assert(_md>=0);
23491 									if(_md>_usedDays)
23492 										totalH+=(_md-_usedDays)*teachersMinHoursDailyMinHours[tch][1];
23493 								}
23494 
23495 								if(remG+totalH<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
23496 								  && totalH<=nHoursPerTeacher[tch])
23497 									ok=true;
23498 								else
23499 									ok=false;
23500 							}
23501 
23502 							if(ok)
23503 								break;
23504 
23505 							int ai2=-1;
23506 
23507 							bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
23508 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
23509 							if(!k){
23510 								bool ka=teacherRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
23511 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
23512 
23513 								if(!ka){
23514 									if(level==0){
23515 										//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
23516 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
23517 									}
23518 									okteachersminhoursdaily=false;
23519 									goto impossibleteachersminhoursdaily;
23520 								}
23521 							}
23522 
23523 							assert(ai2>=0);
23524 
23525 							/*Activity* act2=&gt.rules.internalActivitiesList[ai2];
23526 							int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
23527 							int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
23528 
23529 							for(int dur2=0; dur2<act2->duration; dur2++){
23530 								assert(tchTimetable(d2,h2+dur2)==ai2);
23531 								tchTimetable(d2,h2+dur2)=-1;
23532 							}*/
23533 
23534 							removeAi2FromTchTimetable(ai2);
23535 							updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
23536 						}
23537 					}
23538 				}
23539 			}
23540 		}
23541 
23542 impossibleteachersminhoursdaily:
23543 		if(!okteachersminhoursdaily){
23544 			if(updateSubgroups || updateTeachers)
23545 				removeAiFromNewTimetable(ai, act, d, h);
23546 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
23547 
23548 			nConflActivities[newtime]=MAX_ACTIVITIES;
23549 			continue;
23550 		}
23551 
23552 		/////////end teacher(s) min hours daily
23553 
23554 /////////////////////////////////////////////////////////////////////////////////////////////
23555 
23556 		/////////begin teacher(s) min hours daily per real day
23557 
23558 		//Maybe old comments below:
23559 		//I think it is best to put this routine after max days per week
23560 
23561 		//Added on 11 September 2009: takes care of teachers min days per week
23562 
23563 		okteachersminhoursdailyrealdays=true;
23564 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
23565 			for(int tch : qAsConst(act->iTeachersList)){
23566 				if(teachersMinHoursDailyRealDaysMinHours[tch]>=0){
23567 					assert(teachersMinHoursDailyRealDaysPercentages[tch]==100);
23568 
23569 					bool skip=skipRandom(teachersMinHoursDailyRealDaysPercentages[tch]);
23570 					if(!skip){
23571 						//preliminary test
23572 						bool _ok;
23573 						if(teachersMaxGapsPerWeekPercentage[tch]==-1){
23574 							int _reqHours=0;
23575 							int _usedDays=0;
23576 							for(int rd=0; rd<gt.rules.nDaysPerWeek/2; rd++){
23577 								int d1=rd*2;
23578 								int d2=rd*2+1;
23579 								int _crtReqHoursHalfDays=0;
23580 								int _crtReqHoursWholeDay=0;
23581 
23582 								if(teachersMinHoursDailyMinHours[tch][1]>=0){
23583 									if(teacherNoGapsPerAfternoon(tch)){
23584 										//2019-09-13 - max gaps per afternoon = 0
23585 										if(teachersMaxGapsPerDayPercentage[tch]==-1){
23586 											if(newTeachersDayNHours(tch,d1)>0)
23587 												_crtReqHoursHalfDays+=max(newTeachersDayNHours(tch,d1), teachersMinHoursDailyMinHours[tch][0]);
23588 											if(newTeachersDayNHours(tch,d2)>0){
23589 												//_crtReqHoursHalfDays+=max(newTeachersDayNHours(tch,d2), teachersMinHoursDailyMinHours[tch][1]);
23590 												int nh=max(0, newTeachersDayNGaps(tch,d2)-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23591 												_crtReqHoursHalfDays+=max(newTeachersDayNHours(tch,d2)+nh, teachersMinHoursDailyMinHours[tch][1]);
23592 											}
23593 										}
23594 										else{
23595 											if(newTeachersDayNHours(tch,d1)>0){
23596 												int nh=max(0, newTeachersDayNGaps(tch,d1)-teachersMaxGapsPerDayMaxGaps[tch]);
23597 												_crtReqHoursHalfDays+=max(newTeachersDayNHours(tch,d1)+nh, teachersMinHoursDailyMinHours[tch][0]);
23598 											}
23599 											if(newTeachersDayNHours(tch,d2)>0){
23600 												int nh=max(0, newTeachersDayNGaps(tch,d2)-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23601 												_crtReqHoursHalfDays+=max(newTeachersDayNHours(tch,d2)+nh, teachersMinHoursDailyMinHours[tch][1]);
23602 											}
23603 										}
23604 									}
23605 									else{
23606 										if(teachersMaxGapsPerDayPercentage[tch]==-1){
23607 											if(newTeachersDayNHours(tch,d1)>0)
23608 												_crtReqHoursHalfDays+=max(newTeachersDayNHours(tch,d1), teachersMinHoursDailyMinHours[tch][0]);
23609 											if(newTeachersDayNHours(tch,d2)>0)
23610 												_crtReqHoursHalfDays+=max(newTeachersDayNHours(tch,d2), teachersMinHoursDailyMinHours[tch][1]);
23611 										}
23612 										else{
23613 											if(newTeachersDayNHours(tch,d1)>0){
23614 												int nh=max(0, newTeachersDayNGaps(tch,d1)-teachersMaxGapsPerDayMaxGaps[tch]);
23615 												_crtReqHoursHalfDays+=max(newTeachersDayNHours(tch,d1)+nh, teachersMinHoursDailyMinHours[tch][0]);
23616 											}
23617 											if(newTeachersDayNHours(tch,d2)>0){
23618 												int nh=max(0, newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch]);
23619 												_crtReqHoursHalfDays+=max(newTeachersDayNHours(tch,d2)+nh, teachersMinHoursDailyMinHours[tch][1]);
23620 											}
23621 										}
23622 									}
23623 								}
23624 
23625 								if(teacherNoGapsPerAfternoon(tch)){
23626 									//2019-09-13 - max gaps per afternoon = 0
23627 									if(teachersMaxGapsPerDayPercentage[tch]==-1){
23628 										if(newTeachersDayNHours(tch,d1)+newTeachersDayNHours(tch,d2)>0){
23629 											//_crtReqHoursWholeDay+=max(newTeachersDayNHours(tch,d1)+newTeachersDayNHours(tch,d2), teachersMinHoursDailyRealDaysMinHours[tch]);
23630 											int nh=max(0, newTeachersDayNGaps(tch,d2)-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23631 											_crtReqHoursWholeDay+=max(newTeachersDayNHours(tch,d1)+newTeachersDayNHours(tch,d2)+nh, teachersMinHoursDailyRealDaysMinHours[tch]);
23632 										}
23633 									}
23634 									else{
23635 										if(newTeachersDayNHours(tch,d1)+newTeachersDayNHours(tch,d2)>0){
23636 											int nh=max(0, newTeachersDayNGaps(tch,d1)-teachersMaxGapsPerDayMaxGaps[tch])+max(0, newTeachersDayNGaps(tch,d2)-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23637 											_crtReqHoursWholeDay+=max(newTeachersDayNHours(tch,d1)+newTeachersDayNHours(tch,d2)+nh, teachersMinHoursDailyRealDaysMinHours[tch]);
23638 										}
23639 									}
23640 								}
23641 								else{
23642 									if(teachersMaxGapsPerDayPercentage[tch]==-1){
23643 										if(newTeachersDayNHours(tch,d1)+newTeachersDayNHours(tch,d2)>0){
23644 											_crtReqHoursWholeDay+=max(newTeachersDayNHours(tch,d1)+newTeachersDayNHours(tch,d2), teachersMinHoursDailyRealDaysMinHours[tch]);
23645 										}
23646 									}
23647 									else{
23648 										if(newTeachersDayNHours(tch,d1)+newTeachersDayNHours(tch,d2)>0){
23649 											int nh=max(0, newTeachersDayNGaps(tch,d1)-teachersMaxGapsPerDayMaxGaps[tch])+max(0, newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch]);
23650 											_crtReqHoursWholeDay+=max(newTeachersDayNHours(tch,d1)+newTeachersDayNHours(tch,d2)+nh, teachersMinHoursDailyRealDaysMinHours[tch]);
23651 										}
23652 									}
23653 								}
23654 
23655 								if(newTeachersDayNHours(tch,d1)>0 || newTeachersDayNHours(tch,d2)>0) //this 'if' is useless
23656 									_reqHours+=max(_crtReqHoursHalfDays, _crtReqHoursWholeDay);
23657 							}
23658 
23659 							for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
23660 								if(newTeachersDayNHours(tch,2*d2)>0 || newTeachersDayNHours(tch,2*d2+1)>0)
23661 									_usedDays++;
23662 
23663 							if(teachersMinRealDaysPerWeekPercentages[tch]>=0){
23664 								assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
23665 								assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
23666 								int _md=teachersMinRealDaysPerWeekMinDays[tch];
23667 								assert(_md>=0);
23668 								if(_md>_usedDays){
23669 									if(teachersMinHoursDailyMinHours[tch][1]>=0){
23670 										_reqHours+=(_md-_usedDays)*max(teachersMinHoursDailyMinHours[tch][1], teachersMinHoursDailyRealDaysMinHours[tch]);
23671 									}
23672 									else{
23673 										_reqHours+=(_md-_usedDays)*teachersMinHoursDailyRealDaysMinHours[tch];
23674 									}
23675 								}
23676 							}
23677 
23678 							if(_reqHours <= nHoursPerTeacher[tch])
23679 								_ok=true; //ok
23680 							else
23681 								_ok=false;
23682 						}
23683 						else{
23684 							int remG1=0;
23685 							int remG2=0;
23686 							int remG=0;
23687 							int totalH1=0;
23688 							int totalH2=0;
23689 							int totalH=0;
23690 							int _usedDays=0;
23691 							for(int rd=0; rd<gt.rules.nDaysPerWeek/2; rd++){
23692 								int d1=rd*2;
23693 								int d2=rd*2+1;
23694 
23695 								int remGDay1=newTeachersDayNGaps(tch,d1);
23696 								int remGDay2=newTeachersDayNGaps(tch,d2);
23697 								int h1=newTeachersDayNHours(tch,d1);
23698 								int h2=newTeachersDayNHours(tch,d2);
23699 
23700 								int addh1, addh2;
23701 								if(teacherNoGapsPerAfternoon(tch)){
23702 									//2019-09-13 - max gaps per afternoon = 0
23703 									if(teachersMaxGapsPerDayPercentage[tch]>=0){
23704 										addh1=max(0, remGDay1-teachersMaxGapsPerDayMaxGaps[tch]);
23705 										addh2=max(0, remGDay2-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23706 									}
23707 									else{
23708 										addh1=0;
23709 										addh2=max(0, remGDay2-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23710 									}
23711 								}
23712 								else{
23713 									if(teachersMaxGapsPerDayPercentage[tch]>=0){
23714 										addh1=max(0, remGDay1-teachersMaxGapsPerDayMaxGaps[tch]);
23715 										addh2=max(0, remGDay2-teachersMaxGapsPerDayMaxGaps[tch]);
23716 									}
23717 									else{
23718 										addh1=0;
23719 										addh2=0;
23720 									}
23721 								}
23722 
23723 								remGDay1-=addh1;
23724 								remGDay2-=addh2;
23725 								assert(remGDay1>=0);
23726 								assert(remGDay2>=0);
23727 								h1+=addh1;
23728 								h2+=addh2;
23729 								if(teachersMinHoursDailyMinHours[tch][1]>=0){
23730 									if(h1>0){
23731 										if(h1<teachersMinHoursDailyMinHours[tch][0]){
23732 											remGDay1-=teachersMinHoursDailyMinHours[tch][0]-h1;
23733 											totalH1+=teachersMinHoursDailyMinHours[tch][0];
23734 										}
23735 										else
23736 											totalH1+=h1;
23737 									}
23738 									if(remGDay1>0)
23739 										remG1+=remGDay1;
23740 
23741 									if(h2>0){
23742 										if(h2<teachersMinHoursDailyMinHours[tch][1]){
23743 											remGDay2-=teachersMinHoursDailyMinHours[tch][1]-h2;
23744 											totalH2+=teachersMinHoursDailyMinHours[tch][1];
23745 										}
23746 										else
23747 											totalH2+=h2;
23748 									}
23749 									if(remGDay2>0)
23750 										remG2+=remGDay2;
23751 								}
23752 								if(1){
23753 									int remGDay=remGDay1+remGDay2;
23754 									int h=h1+h2;
23755 									if(h>0){
23756 										if(h<teachersMinHoursDailyRealDaysMinHours[tch]){
23757 											remGDay-=teachersMinHoursDailyRealDaysMinHours[tch]-h;
23758 											totalH+=teachersMinHoursDailyRealDaysMinHours[tch];
23759 										}
23760 										else
23761 											totalH+=h;
23762 									}
23763 									if(remGDay>0)
23764 										remG+=remGDay;
23765 								}
23766 							}
23767 							for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
23768 								if(newTeachersDayNHours(tch,2*d2)>0 || newTeachersDayNHours(tch,2*d2+1)>0)
23769 									_usedDays++;
23770 
23771 							if(teachersMinRealDaysPerWeekPercentages[tch]>=0){
23772 								assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
23773 								assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
23774 								int _md=teachersMinRealDaysPerWeekMinDays[tch];
23775 								assert(_md>=0);
23776 								if(_md>_usedDays){
23777 									if(teachersMinHoursDailyMinHours[tch][1]>=0){
23778 										totalH+=(_md-_usedDays)*max(teachersMinHoursDailyMinHours[tch][1], teachersMinHoursDailyRealDaysMinHours[tch]);
23779 									}
23780 									else{
23781 										totalH+=(_md-_usedDays)*teachersMinHoursDailyRealDaysMinHours[tch];
23782 									}
23783 								}
23784 							}
23785 
23786 							if(remG+totalH<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
23787 							  && remG1+remG2+totalH1+totalH2<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
23788 							  && totalH<=nHoursPerTeacher[tch]
23789 							  && totalH1+totalH2<=nHoursPerTeacher[tch])
23790 								_ok=true;
23791 							else
23792 								_ok=false;
23793 						}
23794 
23795 						if(_ok)
23796 							continue;
23797 
23798 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
23799 							okteachersminhoursdailyrealdays=false;
23800 							goto impossibleteachersminhoursdailyrealdays;
23801 						}
23802 
23803 						getTchTimetable(tch, conflActivities[newtime]);
23804 						tchGetNHoursGaps(tch);
23805 
23806 						for(;;){
23807 							bool ok;
23808 							if(teachersMaxGapsPerWeekPercentage[tch]==-1){
23809 								int _reqHours=0;
23810 								int _usedDays=0;
23811 								for(int rd=0; rd<gt.rules.nDaysPerWeek/2; rd++){
23812 									int d1=rd*2;
23813 									int d2=rd*2+1;
23814 									int _crtReqHoursHalfDays=0;
23815 									int _crtReqHoursWholeDay=0;
23816 
23817 									if(teachersMinHoursDailyMinHours[tch][1]>=0){
23818 										if(teacherNoGapsPerAfternoon(tch)){
23819 											//2019-09-13 - max gaps per afternoon = 0
23820 											if(teachersMaxGapsPerDayPercentage[tch]==-1){
23821 												if(tchDayNHours[d1]>0)
23822 													_crtReqHoursHalfDays+=max(tchDayNHours[d1], teachersMinHoursDailyMinHours[tch][0]);
23823 												if(tchDayNHours[d2]>0){
23824 													int nh=max(0, tchDayNGaps[d2]-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23825 													_crtReqHoursHalfDays+=max(tchDayNHours[d2]+nh, teachersMinHoursDailyMinHours[tch][1]);
23826 												}
23827 											}
23828 											else{
23829 												if(tchDayNHours[d1]>0){
23830 													int nh=max(0, tchDayNGaps[d1]-teachersMaxGapsPerDayMaxGaps[tch]);
23831 													_crtReqHoursHalfDays+=max(tchDayNHours[d1]+nh, teachersMinHoursDailyMinHours[tch][0]);
23832 												}
23833 												if(tchDayNHours[d2]>0){
23834 													int nh=max(0, tchDayNGaps[d2]-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23835 													_crtReqHoursHalfDays+=max(tchDayNHours[d2]+nh, teachersMinHoursDailyMinHours[tch][1]);
23836 												}
23837 											}
23838 										}
23839 										else{
23840 											if(teachersMaxGapsPerDayPercentage[tch]==-1){
23841 												if(tchDayNHours[d1]>0)
23842 													_crtReqHoursHalfDays+=max(tchDayNHours[d1], teachersMinHoursDailyMinHours[tch][0]);
23843 												if(tchDayNHours[d2]>0)
23844 													_crtReqHoursHalfDays+=max(tchDayNHours[d2], teachersMinHoursDailyMinHours[tch][1]);
23845 											}
23846 											else{
23847 												if(tchDayNHours[d1]>0){
23848 													int nh=max(0, tchDayNGaps[d1]-teachersMaxGapsPerDayMaxGaps[tch]);
23849 													_crtReqHoursHalfDays+=max(tchDayNHours[d1]+nh, teachersMinHoursDailyMinHours[tch][0]);
23850 												}
23851 												if(tchDayNHours[d2]>0){
23852 													int nh=max(0, tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch]);
23853 													_crtReqHoursHalfDays+=max(tchDayNHours[d2]+nh, teachersMinHoursDailyMinHours[tch][1]);
23854 												}
23855 											}
23856 										}
23857 									}
23858 
23859 									if(teacherNoGapsPerAfternoon(tch)){
23860 										//2019-09-13 - max gaps per afternoon = 0
23861 										if(teachersMaxGapsPerDayPercentage[tch]==-1){
23862 											if(tchDayNHours[d1]+tchDayNHours[d2]>0){
23863 												int nh=max(0, tchDayNGaps[d2]-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23864 												_crtReqHoursWholeDay+=max(tchDayNHours[d1]+tchDayNHours[d2]+nh, teachersMinHoursDailyRealDaysMinHours[tch]);
23865 											}
23866 										}
23867 										else{
23868 											if(tchDayNHours[d1]+tchDayNHours[d2]>0){
23869 												int nh=max(0, tchDayNGaps[d1]-teachersMaxGapsPerDayMaxGaps[tch])+max(0, tchDayNGaps[d2]-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23870 												_crtReqHoursWholeDay+=max(tchDayNHours[d1]+tchDayNHours[d2]+nh, teachersMinHoursDailyRealDaysMinHours[tch]);
23871 											}
23872 										}
23873 									}
23874 									else{
23875 										if(teachersMaxGapsPerDayPercentage[tch]==-1){
23876 											if(tchDayNHours[d1]+tchDayNHours[d2]>0){
23877 												_crtReqHoursWholeDay+=max(tchDayNHours[d1]+tchDayNHours[d2], teachersMinHoursDailyRealDaysMinHours[tch]);
23878 											}
23879 										}
23880 										else{
23881 											if(tchDayNHours[d1]+tchDayNHours[d2]>0){
23882 												int nh=max(0, tchDayNGaps[d1]-teachersMaxGapsPerDayMaxGaps[tch])+max(0, tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch]);
23883 												_crtReqHoursWholeDay+=max(tchDayNHours[d1]+tchDayNHours[d2]+nh, teachersMinHoursDailyRealDaysMinHours[tch]);
23884 											}
23885 										}
23886 									}
23887 
23888 									if(tchDayNHours[d1]>0 || tchDayNHours[d2]>0) //this 'if' is useless
23889 										_reqHours+=max(_crtReqHoursHalfDays, _crtReqHoursWholeDay);
23890 								}
23891 								for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
23892 									if(tchDayNHours[2*d2]>0 || tchDayNHours[2*d2+1]>0)
23893 										_usedDays++;
23894 
23895 								if(teachersMinRealDaysPerWeekPercentages[tch]>=0){
23896 									assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
23897 									assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
23898 									int _md=teachersMinRealDaysPerWeekMinDays[tch];
23899 									assert(_md>=0);
23900 									if(_md>_usedDays){
23901 										if(teachersMinHoursDailyMinHours[tch][1]>=0){
23902 											_reqHours+=(_md-_usedDays)*max(teachersMinHoursDailyMinHours[tch][1], teachersMinHoursDailyRealDaysMinHours[tch]);
23903 										}
23904 										else{
23905 											_reqHours+=(_md-_usedDays)*teachersMinHoursDailyRealDaysMinHours[tch];
23906 										}
23907 									}
23908 								}
23909 
23910 								if(_reqHours <= nHoursPerTeacher[tch])
23911 									ok=true; //ok
23912 								else
23913 									ok=false;
23914 							}
23915 							else{
23916 								int remG1=0;
23917 								int remG2=0;
23918 								int remG=0;
23919 								int totalH1=0;
23920 								int totalH2=0;
23921 								int totalH=0;
23922 								int _usedDays=0;
23923 								for(int rd=0; rd<gt.rules.nDaysPerWeek/2; rd++){
23924 									int d1=rd*2;
23925 									int d2=rd*2+1;
23926 
23927 									int remGDay1=tchDayNGaps[d1];
23928 									int remGDay2=tchDayNGaps[d2];
23929 									int h1=tchDayNHours[d1];
23930 									int h2=tchDayNHours[d2];
23931 
23932 									int addh1, addh2;
23933 									if(teacherNoGapsPerAfternoon(tch)){
23934 										//2019-09-13 - max gaps per afternoon = 0
23935 										if(teachersMaxGapsPerDayPercentage[tch]>=0){
23936 											addh1=max(0, remGDay1-teachersMaxGapsPerDayMaxGaps[tch]);
23937 											addh2=max(0, remGDay2-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23938 										}
23939 										else{
23940 											addh1=0;
23941 											addh2=max(0, remGDay2-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
23942 										}
23943 									}
23944 									else{
23945 										if(teachersMaxGapsPerDayPercentage[tch]>=0){
23946 											addh1=max(0, remGDay1-teachersMaxGapsPerDayMaxGaps[tch]);
23947 											addh2=max(0, remGDay2-teachersMaxGapsPerDayMaxGaps[tch]);
23948 										}
23949 										else{
23950 											addh1=0;
23951 											addh2=0;
23952 										}
23953 									}
23954 									remGDay1-=addh1;
23955 									remGDay2-=addh2;
23956 									assert(remGDay1>=0);
23957 									assert(remGDay2>=0);
23958 									h1+=addh1;
23959 									h2+=addh2;
23960 									if(teachersMinHoursDailyMinHours[tch][1]>=0){
23961 										if(h1>0){
23962 											if(h1<teachersMinHoursDailyMinHours[tch][0]){
23963 												remGDay1-=teachersMinHoursDailyMinHours[tch][0]-h1;
23964 												totalH1+=teachersMinHoursDailyMinHours[tch][0];
23965 											}
23966 											else
23967 												totalH1+=h1;
23968 										}
23969 										if(remGDay1>0)
23970 											remG1+=remGDay1;
23971 
23972 										if(h2>0){
23973 											if(h2<teachersMinHoursDailyMinHours[tch][1]){
23974 												remGDay2-=teachersMinHoursDailyMinHours[tch][1]-h2;
23975 												totalH2+=teachersMinHoursDailyMinHours[tch][1];
23976 											}
23977 											else
23978 												totalH2+=h2;
23979 										}
23980 										if(remGDay2>0)
23981 											remG2+=remGDay2;
23982 									}
23983 									if(1){
23984 										int remGDay=remGDay1+remGDay2;
23985 										int h=h1+h2;
23986 										if(h>0){
23987 											if(h<teachersMinHoursDailyRealDaysMinHours[tch]){
23988 												remGDay-=teachersMinHoursDailyRealDaysMinHours[tch]-h;
23989 												totalH+=teachersMinHoursDailyRealDaysMinHours[tch];
23990 											}
23991 											else
23992 												totalH+=h;
23993 										}
23994 										if(remGDay>0)
23995 											remG+=remGDay;
23996 									}
23997 								}
23998 								for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
23999 									if(tchDayNHours[2*d2]>0 || tchDayNHours[2*d2+1]>0)
24000 										_usedDays++;
24001 
24002 								if(teachersMinRealDaysPerWeekPercentages[tch]>=0){
24003 									assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
24004 									assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
24005 									int _md=teachersMinRealDaysPerWeekMinDays[tch];
24006 									assert(_md>=0);
24007 									if(_md>_usedDays){
24008 										if(teachersMinHoursDailyMinHours[tch][1]>=0){
24009 											totalH+=(_md-_usedDays)*max(teachersMinHoursDailyMinHours[tch][1], teachersMinHoursDailyRealDaysMinHours[tch]);
24010 										}
24011 										else{
24012 											totalH+=(_md-_usedDays)*teachersMinHoursDailyRealDaysMinHours[tch];
24013 										}
24014 									}
24015 								}
24016 
24017 								if(remG+totalH<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
24018 								  && remG1+remG2+totalH1+totalH2<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
24019 								  && totalH<=nHoursPerTeacher[tch]
24020 								  && totalH1+totalH2<=nHoursPerTeacher[tch])
24021 									ok=true;
24022 								else
24023 									ok=false;
24024 							}
24025 
24026 							if(ok)
24027 								break;
24028 
24029 							int ai2=-1;
24030 
24031 							bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
24032 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
24033 							if(!k){
24034 								bool ka=teacherRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
24035 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
24036 
24037 								if(!ka){
24038 									if(level==0){
24039 										//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
24040 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
24041 									}
24042 									okteachersminhoursdailyrealdays=false;
24043 									goto impossibleteachersminhoursdailyrealdays;
24044 								}
24045 							}
24046 
24047 							assert(ai2>=0);
24048 
24049 							/*Activity* act2=&gt.rules.internalActivitiesList[ai2];
24050 							int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
24051 							int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
24052 
24053 							for(int dur2=0; dur2<act2->duration; dur2++){
24054 								assert(tchTimetable(d2,h2+dur2)==ai2);
24055 								tchTimetable(d2,h2+dur2)=-1;
24056 							}*/
24057 
24058 							removeAi2FromTchTimetable(ai2);
24059 							updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
24060 						}
24061 					}
24062 				}
24063 			}
24064 		}
24065 
24066 impossibleteachersminhoursdailyrealdays:
24067 		if(!okteachersminhoursdailyrealdays){
24068 			if(updateSubgroups || updateTeachers)
24069 				removeAiFromNewTimetable(ai, act, d, h);
24070 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
24071 
24072 			nConflActivities[newtime]=MAX_ACTIVITIES;
24073 			continue;
24074 		}
24075 
24076 		/////////end teacher(s) min hours daily real day
24077 
24078 /////////////////////////////////////////////////////////////////////////////////////////////
24079 
24080 		/////////begin teacher(s) min days per week
24081 
24082 		//Put this routine after min hours daily
24083 
24084 		//Added on 11 September 2009
24085 
24086 		okteachersmindaysperweek=true;
24087 		for(int tch : qAsConst(act->iTeachersList)){
24088 			if(teachersMinDaysPerWeekMinDays[tch]>=0 && teachersMinHoursDailyMinHours[tch][MIN_HOURS_DAILY_INDEX_IN_ARRAY]==-1){ //no need to recheck, if min hours daily is set, because I tested above.
24089 				assert(teachersMinDaysPerWeekPercentages[tch]==100);
24090 
24091 				bool skip=skipRandom(teachersMinDaysPerWeekPercentages[tch]);
24092 				if(!skip){
24093 					//preliminary test
24094 					bool _ok;
24095 					if(teachersMaxGapsPerWeekPercentage[tch]==-1){
24096 						int _reqHours=0;
24097 						int _usedDays=0;
24098 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
24099 							if(newTeachersDayNHours(tch,d2)>0){
24100 								_usedDays++;
24101 								if(teachersMaxGapsPerDayPercentage[tch]==-1){
24102 									_reqHours+=newTeachersDayNHours(tch,d2);
24103 								}
24104 								else{
24105 									int nh=max(0, newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch]);
24106 									_reqHours+=newTeachersDayNHours(tch,d2)+nh;
24107 								}
24108 							}
24109 
24110 						assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek);
24111 						assert(teachersMinDaysPerWeekPercentages[tch]==100.0);
24112 						int _md=teachersMinDaysPerWeekMinDays[tch];
24113 						assert(_md>=0);
24114 						if(_md>_usedDays)
24115 							_reqHours+=(_md-_usedDays)*1; //one hour per day minimum
24116 
24117 						if(_reqHours <= nHoursPerTeacher[tch])
24118 							_ok=true; //ok
24119 						else
24120 							_ok=false;
24121 					}
24122 					else{
24123 						int remG=0;
24124 						int totalH=0;
24125 						int _usedDays=0;
24126 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
24127 							int remGDay=newTeachersDayNGaps(tch,d2);
24128 							int h=newTeachersDayNHours(tch,d2);
24129 							if(h>0){
24130 								_usedDays++;
24131 							}
24132 							int addh;
24133 							if(teachersMaxGapsPerDayPercentage[tch]>=0)
24134 								addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
24135 							else
24136 								addh=0;
24137 							remGDay-=addh;
24138 							assert(remGDay>=0);
24139 							h+=addh;
24140 							if(h>0)
24141 								totalH+=h;
24142 							if(remGDay>0)
24143 								remG+=remGDay;
24144 						}
24145 
24146 						assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek);
24147 						assert(teachersMinDaysPerWeekPercentages[tch]==100.0);
24148 						int _md=teachersMinDaysPerWeekMinDays[tch];
24149 						assert(_md>=0);
24150 						if(_md>_usedDays)
24151 							totalH+=(_md-_usedDays)*1; //min 1 hour per day
24152 
24153 						if(remG+totalH<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
24154 						  && totalH<=nHoursPerTeacher[tch])
24155 						  	_ok=true;
24156 						else
24157 							_ok=false;
24158 					}
24159 
24160 					if(_ok)
24161 						continue;
24162 
24163 					if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
24164 						okteachersmindaysperweek=false;
24165 						goto impossibleteachersmindaysperweek;
24166 					}
24167 
24168 					getTchTimetable(tch, conflActivities[newtime]);
24169 					tchGetNHoursGaps(tch);
24170 
24171 					for(;;){
24172 						bool ok;
24173 						if(teachersMaxGapsPerWeekPercentage[tch]==-1){
24174 							int _reqHours=0;
24175 							int _usedDays=0;
24176 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
24177 								if(tchDayNHours[d2]>0){
24178 									_usedDays++;
24179 									if(teachersMaxGapsPerDayPercentage[tch]==-1){
24180 										_reqHours+=tchDayNHours[d2];
24181 									}
24182 									else{
24183 										int nh=max(0, tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch]);
24184 										_reqHours+=tchDayNHours[d2]+nh;
24185 									}
24186 								}
24187 
24188 							assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek);
24189 							assert(teachersMinDaysPerWeekPercentages[tch]==100.0);
24190 							int _md=teachersMinDaysPerWeekMinDays[tch];
24191 							assert(_md>=0);
24192 							if(_md>_usedDays)
24193 								_reqHours+=(_md-_usedDays)*1; //min 1 hour for each day
24194 
24195 							if(_reqHours <= nHoursPerTeacher[tch])
24196 								ok=true; //ok
24197 							else
24198 								ok=false;
24199 						}
24200 						else{
24201 							int remG=0;
24202 							int totalH=0;
24203 							int _usedDays=0;
24204 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
24205 								int remGDay=tchDayNGaps[d2];
24206 								int h=tchDayNHours[d2];
24207 								if(h>0)
24208 									_usedDays++;
24209 								int addh;
24210 								if(teachersMaxGapsPerDayPercentage[tch]>=0)
24211 									addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
24212 								else
24213 									addh=0;
24214 								remGDay-=addh;
24215 								assert(remGDay>=0);
24216 								h+=addh;
24217 								if(h>0)
24218 									totalH+=h;
24219 								if(remGDay>0)
24220 									remG+=remGDay;
24221 							}
24222 							assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek);
24223 							assert(teachersMinDaysPerWeekPercentages[tch]==100.0);
24224 							int _md=teachersMinDaysPerWeekMinDays[tch];
24225 							assert(_md>=0);
24226 							if(_md>_usedDays)
24227 								totalH+=(_md-_usedDays)*1; //min 1 hour each day
24228 
24229 							if(remG+totalH<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
24230 							  && totalH<=nHoursPerTeacher[tch])
24231 							  	ok=true;
24232 							else
24233 								ok=false;
24234 						}
24235 
24236 						if(ok)
24237 							break;
24238 
24239 						int ai2=-1;
24240 
24241 						bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
24242 						assert(conflActivities[newtime].count()==nConflActivities[newtime]);
24243 						if(!k){
24244 							bool ka=teacherRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
24245 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
24246 
24247 							if(!ka){
24248 								if(level==0){
24249 									//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
24250 									//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
24251 								}
24252 								okteachersmindaysperweek=false;
24253 								goto impossibleteachersmindaysperweek;
24254 							}
24255 						}
24256 
24257 						assert(ai2>=0);
24258 
24259 						/*Activity* act2=&gt.rules.internalActivitiesList[ai2];
24260 						int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
24261 						int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
24262 
24263 						for(int dur2=0; dur2<act2->duration; dur2++){
24264 							assert(tchTimetable(d2,h2+dur2)==ai2);
24265 							tchTimetable(d2,h2+dur2)=-1;
24266 						}*/
24267 
24268 						removeAi2FromTchTimetable(ai2);
24269 						updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
24270 					}
24271 				}
24272 			}
24273 		}
24274 
24275 impossibleteachersmindaysperweek:
24276 		if(!okteachersmindaysperweek){
24277 			if(updateSubgroups || updateTeachers)
24278 				removeAiFromNewTimetable(ai, act, d, h);
24279 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
24280 
24281 			nConflActivities[newtime]=MAX_ACTIVITIES;
24282 			continue;
24283 		}
24284 
24285 		/////////end teacher(s) min days per week
24286 
24287 /////////////////////////////////////////////////////////////////////////////////////////////
24288 
24289 		/////////begin teacher(s) min real days per week
24290 
24291 		//Maybe old comments below:
24292 		//Put this routine after min hours daily
24293 
24294 		//Added on 11 September 2009
24295 
24296 		okteachersminrealdaysperweek=true;
24297 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
24298 			for(int tch : qAsConst(act->iTeachersList)){
24299 				if(teachersMinRealDaysPerWeekMinDays[tch]>=0 && teachersMinHoursDailyMinHours[tch][1]==-1 && teachersMinHoursDailyRealDaysMinHours[tch]==-1){
24300 				//no need to recheck, if min hours daily (or for a real day) is set, because I tested above.
24301 					assert(teachersMinRealDaysPerWeekPercentages[tch]==100);
24302 
24303 					bool skip=skipRandom(teachersMinRealDaysPerWeekPercentages[tch]);
24304 					if(!skip){
24305 						//preliminary test
24306 						bool _ok;
24307 						if(teachersMaxGapsPerWeekPercentage[tch]==-1){
24308 							int _reqHours=0;
24309 							int _usedDays=0;
24310 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
24311 								if(newTeachersDayNHours(tch,d2)>0){
24312 									//_usedDays++;
24313 									//2019-09-13 - max gaps per afternoon = 0
24314 									if(teacherNoGapsPerAfternoon(tch)){
24315 										if(teachersMaxGapsPerDayPercentage[tch]==-1){
24316 											if(d2%2==0){
24317 												_reqHours+=newTeachersDayNHours(tch,d2);
24318 											}
24319 											else{
24320 												_reqHours+=newTeachersDayNHours(tch,d2)+newTeachersDayNGaps(tch,d2);
24321 											}
24322 										}
24323 										else{
24324 											if(d2%2==0){
24325 												int nh=max(0, newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch]);
24326 												_reqHours+=newTeachersDayNHours(tch,d2)+nh;
24327 											}
24328 											else{
24329 												int nh=max(0, newTeachersDayNGaps(tch,d2)-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
24330 												_reqHours+=newTeachersDayNHours(tch,d2)+nh;
24331 											}
24332 										}
24333 									}
24334 									else{
24335 										if(teachersMaxGapsPerDayPercentage[tch]==-1){
24336 											_reqHours+=newTeachersDayNHours(tch,d2);
24337 										}
24338 										else{
24339 											int nh=max(0, newTeachersDayNGaps(tch,d2)-teachersMaxGapsPerDayMaxGaps[tch]);
24340 											_reqHours+=newTeachersDayNHours(tch,d2)+nh;
24341 										}
24342 									}
24343 								}
24344 							for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
24345 								if(newTeachersDayNHours(tch,2*d2)>0 || newTeachersDayNHours(tch,2*d2+1)>0)
24346 									_usedDays++;
24347 
24348 							assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
24349 							assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
24350 							int _md=teachersMinRealDaysPerWeekMinDays[tch];
24351 							assert(_md>=0);
24352 							if(_md>_usedDays)
24353 								_reqHours+=(_md-_usedDays)*1; //one hour per day minimum
24354 
24355 							if(_reqHours <= nHoursPerTeacher[tch])
24356 								_ok=true; //ok
24357 							else
24358 								_ok=false;
24359 						}
24360 						else{
24361 							int remG=0;
24362 							int totalH=0;
24363 							int _usedDays=0;
24364 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
24365 								int remGDay=newTeachersDayNGaps(tch,d2);
24366 								int h=newTeachersDayNHours(tch,d2);
24367 								//if(h>0){
24368 								//	_usedDays++;
24369 								//}
24370 								int addh;
24371 								//2019-09-13 - max gaps per afternoon = 0
24372 								if(teacherNoGapsPerAfternoon(tch)){
24373 									if(teachersMaxGapsPerDayPercentage[tch]>=0){
24374 										if(d2%2==0){
24375 											addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
24376 										}
24377 										else{
24378 											addh=max(0, remGDay-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
24379 										}
24380 									}
24381 									else{
24382 										if(d2%2==0){
24383 											addh=0;
24384 										}
24385 										else{
24386 											addh=max(0, remGDay-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
24387 										}
24388 									}
24389 								}
24390 								else{
24391 									if(teachersMaxGapsPerDayPercentage[tch]>=0)
24392 										addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
24393 									else
24394 										addh=0;
24395 								}
24396 								remGDay-=addh;
24397 								assert(remGDay>=0);
24398 								h+=addh;
24399 								if(h>0)
24400 									totalH+=h;
24401 								if(remGDay>0)
24402 									remG+=remGDay;
24403 							}
24404 							for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
24405 								if(newTeachersDayNHours(tch,2*d2)>0 || newTeachersDayNHours(tch,2*d2+1)>0)
24406 									_usedDays++;
24407 
24408 							assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
24409 							assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
24410 							int _md=teachersMinRealDaysPerWeekMinDays[tch];
24411 							assert(_md>=0);
24412 							if(_md>_usedDays)
24413 								totalH+=(_md-_usedDays)*1; //min 1 hour per day
24414 
24415 							if(remG+totalH<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
24416 							  && totalH<=nHoursPerTeacher[tch])
24417 								_ok=true;
24418 							else
24419 								_ok=false;
24420 						}
24421 
24422 						if(_ok)
24423 							continue;
24424 
24425 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
24426 							okteachersminrealdaysperweek=false;
24427 							goto impossibleteachersminrealdaysperweek;
24428 						}
24429 
24430 						getTchTimetable(tch, conflActivities[newtime]);
24431 						tchGetNHoursGaps(tch);
24432 
24433 						for(;;){
24434 							bool ok;
24435 							if(teachersMaxGapsPerWeekPercentage[tch]==-1){
24436 								int _reqHours=0;
24437 								int _usedDays=0;
24438 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
24439 									if(tchDayNHours[d2]>0){
24440 										//_usedDays++;
24441 										//2019-09-13 - max gaps per afternoon = 0
24442 
24443 										if(teacherNoGapsPerAfternoon(tch)){
24444 											if(teachersMaxGapsPerDayPercentage[tch]==-1){
24445 												if(d2%2==0){
24446 													_reqHours+=tchDayNHours[d2];
24447 												}
24448 												else{
24449 													int nh=max(0, tchDayNGaps[d2]-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
24450 													_reqHours+=tchDayNHours[d2]+nh;
24451 												}
24452 											}
24453 											else{
24454 												if(d2%2==0){
24455 													int nh=max(0, tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch]);
24456 													_reqHours+=tchDayNHours[d2]+nh;
24457 												}
24458 												else{
24459 													int nh=max(0, tchDayNGaps[d2]-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
24460 													_reqHours+=tchDayNHours[d2]+nh;
24461 												}
24462 											}
24463 										}
24464 										else{
24465 											if(teachersMaxGapsPerDayPercentage[tch]==-1){
24466 												_reqHours+=tchDayNHours[d2];
24467 											}
24468 											else{
24469 												int nh=max(0, tchDayNGaps[d2]-teachersMaxGapsPerDayMaxGaps[tch]);
24470 												_reqHours+=tchDayNHours[d2]+nh;
24471 											}
24472 										}
24473 
24474 									}
24475 								for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
24476 									if(tchDayNHours[2*d2]>0 || tchDayNHours[2*d2+1]>0)
24477 										_usedDays++;
24478 
24479 								assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
24480 								assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
24481 								int _md=teachersMinRealDaysPerWeekMinDays[tch];
24482 								assert(_md>=0);
24483 								if(_md>_usedDays)
24484 									_reqHours+=(_md-_usedDays)*1; //min 1 hour for each day
24485 
24486 								if(_reqHours <= nHoursPerTeacher[tch])
24487 									ok=true; //ok
24488 								else
24489 									ok=false;
24490 							}
24491 							else{
24492 								int remG=0;
24493 								int totalH=0;
24494 								int _usedDays=0;
24495 								for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
24496 									int remGDay=tchDayNGaps[d2];
24497 									int h=tchDayNHours[d2];
24498 									//if(h>0)
24499 									//	_usedDays++;
24500 									int addh;
24501 									//2019-09-13 - max gaps per afternoon = 0
24502 									if(teacherNoGapsPerAfternoon(tch)){
24503 										if(teachersMaxGapsPerDayPercentage[tch]>=0){
24504 											if(d2%2==0){
24505 												addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
24506 											}
24507 											else{
24508 												addh=max(0, remGDay-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
24509 											}
24510 										}
24511 										else{
24512 											if(d2%2==0){
24513 												addh=0;
24514 											}
24515 											else{
24516 												addh=max(0, remGDay-0/*teachersMaxGapsPerDayMaxGaps[tch]*/);
24517 											}
24518 										}
24519 									}
24520 									else{
24521 										if(teachersMaxGapsPerDayPercentage[tch]>=0)
24522 											addh=max(0, remGDay-teachersMaxGapsPerDayMaxGaps[tch]);
24523 										else
24524 											addh=0;
24525 									}
24526 									remGDay-=addh;
24527 									assert(remGDay>=0);
24528 									h+=addh;
24529 									if(h>0)
24530 										totalH+=h;
24531 									if(remGDay>0)
24532 										remG+=remGDay;
24533 								}
24534 								for(int d2=0; d2<gt.rules.nDaysPerWeek/2; d2++)
24535 									if(tchDayNHours[2*d2]>0 || tchDayNHours[2*d2+1]>0)
24536 										_usedDays++;
24537 								assert(_usedDays>=0 && _usedDays<=gt.rules.nDaysPerWeek/2);
24538 								assert(teachersMinRealDaysPerWeekPercentages[tch]==100.0);
24539 								int _md=teachersMinRealDaysPerWeekMinDays[tch];
24540 								assert(_md>=0);
24541 								if(_md>_usedDays)
24542 									totalH+=(_md-_usedDays)*1; //min 1 hour each day
24543 
24544 								if(remG+totalH<=nHoursPerTeacher[tch]+teachersMaxGapsPerWeekMaxGaps[tch]
24545 								  && totalH<=nHoursPerTeacher[tch])
24546 									ok=true;
24547 								else
24548 									ok=false;
24549 							}
24550 
24551 							if(ok)
24552 								break;
24553 
24554 							int ai2=-1;
24555 
24556 							bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
24557 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
24558 							if(!k){
24559 								bool ka=teacherRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
24560 								assert(conflActivities[newtime].count()==nConflActivities[newtime]);
24561 
24562 								if(!ka){
24563 									if(level==0){
24564 										//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
24565 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
24566 									}
24567 									okteachersminrealdaysperweek=false;
24568 									goto impossibleteachersminrealdaysperweek;
24569 								}
24570 							}
24571 
24572 							assert(ai2>=0);
24573 
24574 							/*Activity* act2=&gt.rules.internalActivitiesList[ai2];
24575 							int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
24576 							int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
24577 
24578 							for(int dur2=0; dur2<act2->duration; dur2++){
24579 								assert(tchTimetable(d2,h2+dur2)==ai2);
24580 								tchTimetable(d2,h2+dur2)=-1;
24581 							}*/
24582 
24583 							removeAi2FromTchTimetable(ai2);
24584 							updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
24585 						}
24586 					}
24587 				}
24588 			}
24589 		}
24590 
24591 impossibleteachersminrealdaysperweek:
24592 		if(!okteachersminrealdaysperweek){
24593 			if(updateSubgroups || updateTeachers)
24594 				removeAiFromNewTimetable(ai, act, d, h);
24595 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
24596 
24597 			nConflActivities[newtime]=MAX_ACTIVITIES;
24598 			continue;
24599 		}
24600 
24601 		/////////end teacher(s) min real days per week
24602 
24603 /////////////////////////////////////////////////////////////////////////////////////////////
24604 
24605 		okteachersminmorningsafternoonsperweek=true;
24606 
24607 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
24608 			for(int tch : qAsConst(act->iTeachersList)){
24609 				if(teachersMinMorningsPerWeekMinMornings[tch]>=0 || teachersMinAfternoonsPerWeekMinAfternoons[tch]>=0){
24610 					//assert(teachersMinHoursDailyPercentages[tch]==100);
24611 					int mhd[2];
24612 					mhd[1]=1; //afternoon
24613 					mhd[0]=1; //morning, at least as large as for daily
24614 					if(teachersMinHoursDailyPercentages[tch][1]==100){
24615 						assert(mhd[1]<teachersMinHoursDailyMinHours[tch][1]);
24616 						mhd[1]=teachersMinHoursDailyMinHours[tch][1];
24617 					}
24618 					if(teachersMinHoursDailyPercentages[tch][0]==100){
24619 						assert(mhd[0]<teachersMinHoursDailyMinHours[tch][0]);
24620 						mhd[0]=teachersMinHoursDailyMinHours[tch][0];
24621 					}
24622 					//assert(teachersMaxGapsPerWeekMaxGaps[tch]==0 || teachersMaxGapsPerDayMaxGaps[tch]==0);
24623 
24624 					bool skip=false;
24625 					if(!skip){
24626 						//preliminary test
24627 						bool _ok;
24628 
24629 						int requested=0;
24630 						int filledMornings=0;
24631 						int filledAfternoons=0;
24632 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
24633 							int p=/*newTeachersDayNGaps(tch, d2)+*/newTeachersDayNHours(tch, d2);
24634 							if(teachersMaxGapsPerWeekMaxGaps[tch]==0 || teachersMaxGapsPerDayMaxGaps[tch]==0
24635 							 || (teachersMaxGapsPerRealDayMaxGaps[tch]==0 && teachersMaxGapsPerRealDayAllowException[tch]==false) ||
24636 							 teachersMaxGapsPerWeekForRealDaysMaxGaps[tch]==0)
24637 								p+=newTeachersDayNGaps(tch, d2);
24638 							if(p>0 && p<mhd[d2%2])
24639 								p=mhd[d2%2];
24640 							requested+=p;
24641 							if(p>0){
24642 								if(d2%2==0)
24643 									filledMornings++;
24644 								else
24645 									filledAfternoons++;
24646 							}
24647 						}
24648 						if(teachersMinMorningsPerWeekMinMornings[tch]>0){
24649 							if(filledMornings<teachersMinMorningsPerWeekMinMornings[tch])
24650 								requested+=mhd[0]*(-filledMornings+teachersMinMorningsPerWeekMinMornings[tch]);
24651 						}
24652 						if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>0){
24653 							if(filledAfternoons<teachersMinAfternoonsPerWeekMinAfternoons[tch])
24654 								requested+=mhd[1]*(-filledAfternoons+teachersMinAfternoonsPerWeekMinAfternoons[tch]);
24655 						}
24656 
24657 						_ok=(requested<=nHoursPerTeacher[tch]);
24658 
24659 						if(_ok)
24660 							continue;
24661 
24662 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
24663 							okteachersminmorningsafternoonsperweek=false;
24664 							goto impossibleteachersminmorningsafternoonsperweek;
24665 						}
24666 
24667 						getTchTimetable(tch, conflActivities[newtime]);
24668 						tchGetNHoursGaps(tch);
24669 
24670 						for(;;){
24671 							bool ok;
24672 
24673 							int requested=0;
24674 							int filledMornings=0;
24675 							int filledAfternoons=0;
24676 							for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
24677 								int p=/*tchDayNGaps[d2]+*/tchDayNHours[d2];
24678 								if(teachersMaxGapsPerWeekMaxGaps[tch]==0 || teachersMaxGapsPerDayMaxGaps[tch]==0
24679 								 || (teachersMaxGapsPerRealDayMaxGaps[tch]==0 && teachersMaxGapsPerRealDayAllowException[tch]==false)
24680 								 || teachersMaxGapsPerWeekForRealDaysMaxGaps[tch]==0)
24681 									p+=tchDayNGaps[d2];
24682 								if(p>0 && p<mhd[d2%2])
24683 									p=mhd[d2%2];
24684 								requested+=p;
24685 								if(p>0){
24686 									if(d2%2==0)
24687 										filledMornings++;
24688 									else
24689 										filledAfternoons++;
24690 								}
24691 							}
24692 							if(teachersMinMorningsPerWeekMinMornings[tch]>0){
24693 								if(filledMornings<teachersMinMorningsPerWeekMinMornings[tch])
24694 									requested+=mhd[0]*(-filledMornings+teachersMinMorningsPerWeekMinMornings[tch]);
24695 							}
24696 							if(teachersMinAfternoonsPerWeekMinAfternoons[tch]>0){
24697 								if(filledAfternoons<teachersMinAfternoonsPerWeekMinAfternoons[tch])
24698 									requested+=mhd[1]*(-filledAfternoons+teachersMinAfternoonsPerWeekMinAfternoons[tch]);
24699 							}
24700 
24701 							ok=(requested<=nHoursPerTeacher[tch]);
24702 
24703 							if(ok)
24704 								break;
24705 
24706 							int ai2=-1;
24707 
24708 							bool k=teacherRemoveAnActivityFromBeginOrEnd(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
24709 							assert(conflActivities[newtime].count()==nConflActivities[newtime]);
24710 							if(!k){
24711 								bool k2=false;
24712 								if(!(teachersMaxGapsPerWeekMaxGaps[tch]==0 || teachersMaxGapsPerDayMaxGaps[tch]==0
24713 								 || (teachersMaxGapsPerRealDayMaxGaps[tch]==0 && teachersMaxGapsPerRealDayAllowException[tch]==false))
24714 								 || teachersMaxGapsPerWeekForRealDaysMaxGaps[tch]==0){
24715 									k2=teacherRemoveAnActivityFromAnywhere(level, ai, conflActivities[newtime], nConflActivities[newtime], ai2);
24716 									assert(conflActivities[newtime].count()==nConflActivities[newtime]);
24717 								}
24718 								if(!k2){
24719 									if(level==0){
24720 										//Liviu: inactivated from version 5.12.4 (7 Feb. 2010), because it may take too long for some files
24721 										//cout<<"WARNING - mb - file "<<__FILE__<<" line "<<__LINE__<<endl;
24722 									}
24723 									okteachersminmorningsafternoonsperweek=false;
24724 									goto impossibleteachersminmorningsafternoonsperweek;
24725 								}
24726 							}
24727 
24728 							assert(ai2>=0);
24729 
24730 							/*Activity* act2=&gt.rules.internalActivitiesList[ai2];
24731 							int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
24732 							int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
24733 
24734 							for(int dur2=0; dur2<act2->duration; dur2++){
24735 								assert(tchTimetable(d2,h2+dur2)==ai2);
24736 								tchTimetable(d2,h2+dur2)=-1;
24737 							}*/
24738 
24739 							removeAi2FromTchTimetable(ai2);
24740 							updateTchNHoursGaps(tch, c.times[ai2]%gt.rules.nDaysPerWeek);
24741 						}
24742 					}
24743 				}
24744 			}
24745 		}
24746 
24747 impossibleteachersminmorningsafternoonsperweek:
24748 		if(!okteachersminmorningsafternoonsperweek){
24749 			if(updateSubgroups || updateTeachers)
24750 				removeAiFromNewTimetable(ai, act, d, h);
24751 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
24752 
24753 			nConflActivities[newtime]=MAX_ACTIVITIES;
24754 			continue;
24755 		}
24756 
24757 		/////////end teacher(s) min mornings or afternoons per week
24758 
24759 /////////////////////////////////////////////////////////////////////////////////////////////
24760 
24761 		/////////begin teacher(s) max two activity tags per day from N1 N2 N3
24762 
24763 		okteachersmaxtwoactivitytagsperdayfromn1n2n3=true;
24764 
24765 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
24766 			for(int tch : qAsConst(act->iTeachersList)){
24767 				if(teachersMaxTwoActivityTagsPerDayFromN1N2N3Percentages[tch]>=0){
24768 					assert(teachersMaxTwoActivityTagsPerDayFromN1N2N3Percentages[tch]==100);
24769 					int cnt[4]; //cnt[3] is for activities which have no tag from N1, N2, or N3. Crash bug fixed on 2021-07-25.
24770 					cnt[0]=cnt[1]=cnt[2]=cnt[3]=0;
24771 					for(int ai2 : qAsConst(teacherActivitiesOfTheDay[tch][d]))
24772 						if(!conflActivities[newtime].contains(ai2)){
24773 							int actTag=activityTagN1N2N3[ai2];
24774 							cnt[actTag]++;
24775 						}
24776 
24777 					int cntTags=0;
24778 					for(int i=0; i<3; i++)
24779 						if(cnt[i]>0)
24780 							cntTags++;
24781 					assert(cntTags<=2);
24782 					for(int i=0; i<3; i++){
24783 						int c0, c1, c2;
24784 						if(i==0){
24785 							c0=0; c1=1; c2=2;
24786 						}
24787 						else if(i==1){
24788 							c0=0; c1=2; c2=1;
24789 						}
24790 						else if(i==2){
24791 							c0=1; c1=2; c2=0;
24792 						}
24793 						else{
24794 							assert(0);
24795 						}
24796 
24797 						if(cnt[c0]>0 && cnt[c1]>0 && activityTagN1N2N3[ai]==c2){
24798 							int rnd=rng.intMRG32k3a(2);
24799 							if(rnd==0){
24800 								//empty c0
24801 								for(int ai2 : qAsConst(teacherActivitiesOfTheDay[tch][d])){
24802 									if(!conflActivities[newtime].contains(ai2) && activityTagN1N2N3[ai2]==c0){
24803 										if(!fixedTimeActivity[ai2] && !swappedActivities[ai2]){
24804 											conflActivities[newtime].append(ai2);
24805 											nConflActivities[newtime]++;
24806 										}
24807 										else{
24808 											okteachersmaxtwoactivitytagsperdayfromn1n2n3=false;
24809 											goto impossibleteachersmaxtwoactivitytagsperdayfromn1n2n3;
24810 										}
24811 									}
24812 								}
24813 							}
24814 							else{
24815 								//empty c1
24816 								for(int ai2 : qAsConst(teacherActivitiesOfTheDay[tch][d])){
24817 									if(!conflActivities[newtime].contains(ai2) && activityTagN1N2N3[ai2]==c1){
24818 										if(!fixedTimeActivity[ai2] && !swappedActivities[ai2]){
24819 											conflActivities[newtime].append(ai2);
24820 											nConflActivities[newtime]++;
24821 										}
24822 										else{
24823 											okteachersmaxtwoactivitytagsperdayfromn1n2n3=false;
24824 											goto impossibleteachersmaxtwoactivitytagsperdayfromn1n2n3;
24825 										}
24826 									}
24827 								}
24828 							}
24829 						}
24830 					}
24831 				}
24832 			}
24833 		}
24834 
24835 impossibleteachersmaxtwoactivitytagsperdayfromn1n2n3:
24836 		if(!okteachersmaxtwoactivitytagsperdayfromn1n2n3){
24837 			if(updateSubgroups || updateTeachers)
24838 				removeAiFromNewTimetable(ai, act, d, h);
24839 
24840 			nConflActivities[newtime]=MAX_ACTIVITIES;
24841 			continue;
24842 		}
24843 
24844 		/////////end teacher(s) max two activity tags per day from N1 N2 N3
24845 
24846 /////////////////////////////////////////////////////////////////////////////////////////////
24847 
24848 		//2020-06-28
24849 		//teachers max hours per all afternoons.
24850 
24851 		okteachersmaxhoursperallafternoons=true;
24852 
24853 		if(gt.rules.mode==MORNINGS_AFTERNOONS){
24854 			for(int tch : qAsConst(act->iTeachersList)){
24855 				if(teachersMaxHoursPerAllAfternoonsMaxHours[tch]>=0){
24856 					assert(teachersMaxHoursPerAllAfternoonsPercentages[tch]==100.0);
24857 
24858 					if(d%2==1){ //afternoon
24859 						//preliminary
24860 						int _nOcc=0;
24861 
24862 						for(int d2=1; d2<gt.rules.nDaysPerWeek; d2+=2){ //afternoon
24863 							for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
24864 								int ai2=newTeachersTimetable(tch,d2,h2);
24865 								if(ai2>=0)
24866 									_nOcc++;
24867 							}
24868 						}
24869 
24870 						if(_nOcc<=teachersMaxHoursPerAllAfternoonsMaxHours[tch])
24871 							continue; //ok
24872 
24873 						getTchTimetable(tch, conflActivities[newtime]);
24874 
24875 						int nOccupied=0;
24876 
24877 						QSet<int> candidates;
24878 
24879 						//static int slotActivity[MAX_DAYS_PER_WEEK*MAX_HOURS_PER_DAY];
24880 
24881 						for(int d2=1; d2<gt.rules.nDaysPerWeek; d2+=2){ //afternoon
24882 							for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
24883 								int t=d2+gt.rules.nDaysPerWeek*h2;
24884 
24885 								int ai2=tchTimetable(d2,h2);
24886 								slotActivity[t]=ai2;
24887 								if(ai2>=0){
24888 									nOccupied++;
24889 									if(ai2!=ai && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
24890 										candidates.insert(t);
24891 								}
24892 							}
24893 						}
24894 
24895 						if(nOccupied > teachersMaxHoursPerAllAfternoonsMaxHours[tch]){
24896 							int target=nOccupied - teachersMaxHoursPerAllAfternoonsMaxHours[tch];
24897 
24898 							while(target>0){
24899 								bool decreased=false;
24900 
24901 								if(candidates.count()==0){
24902 									okteachersmaxhoursperallafternoons=false;
24903 									goto impossibleteachersmaxhoursperallafternoons;
24904 								}
24905 
24906 								//To keep the generation identical on all computers
24907 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
24908 								QList<int> tmpSortedList=QList<int>(candidates.constBegin(), candidates.constEnd());
24909 #else
24910 								QList<int> tmpSortedList=candidates.toList();
24911 #endif
24912 								std::stable_sort(tmpSortedList.begin(), tmpSortedList.end());
24913 
24914 								int t=-1;
24915 								if(level>0){
24916 									assert(candidates.count()==tmpSortedList.count());
24917 									int q=rng.intMRG32k3a(candidates.count());
24918 									t=tmpSortedList.at(q);
24919 								}
24920 								else{
24921 									assert(level==0);
24922 
24923 									int optMinWrong=INF;
24924 									QList<int> tl;
24925 
24926 									for(int t2 : qAsConst(tmpSortedList)){
24927 										int ai3=slotActivity[t2];
24928 										if(optMinWrong>triedRemovals(ai3,c.times[ai3])){
24929 											optMinWrong=triedRemovals(ai3,c.times[ai3]);
24930 											tl.clear();
24931 											tl.append(t2);
24932 										}
24933 										else if(optMinWrong==triedRemovals(ai3,c.times[ai3])){
24934 											tl.append(t2);
24935 										}
24936 									}
24937 
24938 									assert(tl.count()>0);
24939 									int q=rng.intMRG32k3a(tl.count());
24940 									t=tl.at(q);
24941 								}
24942 
24943 								assert(t>=0);
24944 								int ai2=slotActivity[t];
24945 
24946 								assert(ai2>=0);
24947 								assert(ai2!=ai);
24948 								assert(c.times[ai2]!=UNALLOCATED_TIME);
24949 								assert(!fixedTimeActivity[ai2] && !swappedActivities[ai2]);
24950 
24951 								for(int tt=c.times[ai2]; tt<c.times[ai2]+gt.rules.internalActivitiesList[ai2].duration*gt.rules.nDaysPerWeek; tt+=gt.rules.nDaysPerWeek){
24952 									assert(slotActivity[tt]==ai2);
24953 									slotActivity[tt]=-1;
24954 									assert(candidates.contains(tt));
24955 									candidates.remove(tt);
24956 									target--;
24957 
24958 									decreased=true;
24959 								}
24960 
24961 								assert(!conflActivities[newtime].contains(ai2));
24962 								conflActivities[newtime].append(ai2);
24963 								nConflActivities[newtime]++;
24964 								assert(nConflActivities[newtime]==conflActivities[newtime].count());
24965 
24966 								removeAi2FromTchTimetable(ai2); //not really needed
24967 
24968 								assert(decreased);
24969 							}
24970 						}
24971 					}
24972 				}
24973 			}
24974 		}
24975 
24976 impossibleteachersmaxhoursperallafternoons:
24977 				if(!okteachersmaxhoursperallafternoons){
24978 					if(updateSubgroups || updateTeachers)
24979 						removeAiFromNewTimetable(ai, act, d, h);
24980 					//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
24981 
24982 					nConflActivities[newtime]=MAX_ACTIVITIES;
24983 					continue;
24984 				}
24985 
24986 /////////////////////////////////////////////////////////////////////////////////////////////
24987 
24988 		//allowed from teachers activity tag min hours daily
24989 
24990 		//!!!NOT PERFECT, there is room for improvement
24991 
24992 		okteachersactivitytagminhoursdaily=true;
24993 
24994 		if(haveTeachersActivityTagMinHoursDaily){
24995 			for(int tch : qAsConst(act->iTeachersList)){
24996 				for(TeacherActivityTagMinHoursDaily_item* item : qAsConst(tatmhdListForTeacher[tch])){
24997 					if(!gt.rules.internalActivitiesList[ai].iActivityTagsSet.contains(item->activityTag))
24998 						continue;
24999 
25000 					//int tchDayNHoursWithTag[MAX_DAYS_PER_WEEK];
25001 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++)
25002 						tchDayNHoursWithTag[d2]=0;
25003 
25004 					//bool possibleToEmptyDay[MAX_DAYS_PER_WEEK];
25005 
25006 					//code similar to getTchTimetable
25007 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
25008 						possibleToEmptyDay[d2]=true;
25009 						for(int h2=0; h2<gt.rules.nHoursPerDay; h2++){
25010 							int ai2=newTeachersTimetable(tch,d2,h2);
25011 							if(ai2>=0 && !conflActivities[newtime].contains(ai2)){
25012 								if(gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(item->activityTag)){
25013 									tchTimetable(d2,h2)=ai2;
25014 									tchDayNHoursWithTag[d2]++;
25015 
25016 									if(item->allowEmptyDays && (ai2==ai || fixedTimeActivity[ai2] || swappedActivities[ai2]))
25017 										possibleToEmptyDay[d2]=false;
25018 								}
25019 								else{
25020 									tchTimetable(d2,h2)=-1;
25021 								}
25022 							}
25023 							else{
25024 								tchTimetable(d2,h2)=-1;
25025 							}
25026 						}
25027 					}
25028 
25029 					int necessary=0;
25030 					for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
25031 						if(tchDayNHoursWithTag[d2]>0 || (tchDayNHoursWithTag[d2]==0 && !item->allowEmptyDays)){
25032 							necessary+=max(item->minHoursDaily, tchDayNHoursWithTag[d2]);
25033 						}
25034 					}
25035 
25036 					if(necessary > item->durationOfActivitiesWithActivityTagForTeacher){
25037 						//not OK
25038 						if(level>=LEVEL_STOP_CONFLICTS_CALCULATION){
25039 							okteachersactivitytagminhoursdaily=false;
25040 							goto impossibleteachersactivitytagminhoursdaily;
25041 						}
25042 
25043 						QSet<int> candidatesSet;
25044 
25045 						for(int d2=0; d2<gt.rules.nDaysPerWeek; d2++){
25046 							if(tchDayNHoursWithTag[d2]>0){
25047 								if((item->allowEmptyDays && possibleToEmptyDay[d2]) || tchDayNHoursWithTag[d2] > item->minHoursDaily){
25048 									for(int h2=0; h2<gt.rules.nHoursPerDay; ){
25049 										int ai2=tchTimetable(d2,h2);
25050 										if(ai2>=0){
25051 											if(ai2!=ai && !fixedTimeActivity[ai2] && !swappedActivities[ai2])
25052 												candidatesSet.insert(ai2);
25053 											h2+=gt.rules.internalActivitiesList[ai2].duration;
25054 										}
25055 										else{
25056 											h2++;
25057 										}
25058 									}
25059 								}
25060 							}
25061 						}
25062 
25063 						for(;;){
25064 							if(candidatesSet.count()==0){
25065 								okteachersactivitytagminhoursdaily=false;
25066 								goto impossibleteachersactivitytagminhoursdaily;
25067 							}
25068 							else{
25069 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
25070 								QList<int> candidatesList(candidatesSet.constBegin(), candidatesSet.constEnd());
25071 #else
25072 								QList<int> candidatesList=candidatesSet.toList();
25073 #endif
25074 								std::stable_sort(candidatesList.begin(), candidatesList.end()); //To keep the generation identical on all computers.
25075 
25076 								int ai2;
25077 								if(level>0){
25078 									int q=rng.intMRG32k3a(candidatesList.count());
25079 									ai2=candidatesList.at(q);
25080 								}
25081 								else{
25082 									assert(level==0);
25083 
25084 									int optMinWrong=INF;
25085 
25086 									QList<int> tl;
25087 
25088 									for(int ai3 : qAsConst(candidatesList)){
25089 										if(optMinWrong>triedRemovals(ai3,c.times[ai3])){
25090 										 	optMinWrong=triedRemovals(ai3,c.times[ai3]);
25091 										 	tl.clear();
25092 										 	tl.append(ai3);
25093 										}
25094 										else if(optMinWrong==triedRemovals(ai3,c.times[ai3])){
25095 										 	tl.append(ai3);
25096 										}
25097 									}
25098 
25099 									assert(tl.count()>0);
25100 									int q=rng.intMRG32k3a(tl.count());
25101 									ai2=tl.at(q);
25102 								}
25103 
25104 								assert(ai2!=ai);
25105 								assert(!fixedTimeActivity[ai2] && !swappedActivities[ai2]);
25106 
25107 								assert(!conflActivities[newtime].contains(ai2));
25108 								conflActivities[newtime].append(ai2);
25109 
25110 								nConflActivities[newtime]++;
25111 								assert(nConflActivities[newtime]==conflActivities[newtime].count());
25112 
25113 								int d2=c.times[ai2]%gt.rules.nDaysPerWeek;
25114 								int h2=c.times[ai2]/gt.rules.nDaysPerWeek;
25115 								int dur2=gt.rules.internalActivitiesList[ai2].duration;
25116 								tchDayNHoursWithTag[d2]-=dur2;
25117 								assert(tchDayNHoursWithTag[d2]>=0);
25118 
25119 								if(tchDayNHoursWithTag[d2]==0 && item->allowEmptyDays){
25120 									necessary-=max(item->minHoursDaily, dur2);
25121 									assert(necessary>=0);
25122 								}
25123 								else{
25124 									int oldNecessary=necessary;
25125 									necessary-=max(item->minHoursDaily, tchDayNHoursWithTag[d2]+dur2);
25126 									assert(necessary>=0);
25127 									necessary+=max(item->minHoursDaily, tchDayNHoursWithTag[d2]);
25128 									if(!item->allowEmptyDays)
25129 										assert(oldNecessary>necessary);
25130 								}
25131 
25132 								for(int h3=h2; h3<h2+dur2; h3++){
25133 									assert(tchTimetable(d2,h3)==ai2);
25134 									tchTimetable(d2,h3)=-1;
25135 								}
25136 
25137 								if(necessary <= item->durationOfActivitiesWithActivityTagForTeacher)
25138 									break; //OK
25139 
25140 								bool tr=candidatesSet.remove(ai2);
25141 								assert(tr);
25142 
25143 								if(tchDayNHoursWithTag[d2]>0 && tchDayNHoursWithTag[d2]<=item->minHoursDaily &&
25144 								 !(item->allowEmptyDays && possibleToEmptyDay[d2])){
25145 									for(int h3=0; h3<gt.rules.nHoursPerDay; ){
25146 										int ai3=tchTimetable(d2,h3);
25147 										if(ai3>=0){
25148 											if(ai3!=ai && ai3!=ai2 && !fixedTimeActivity[ai3] && !swappedActivities[ai3]){
25149 												assert(candidatesSet.contains(ai3));
25150 												candidatesSet.remove(ai3);
25151 											}
25152 											h3+=gt.rules.internalActivitiesList[ai3].duration;
25153 										}
25154 										else{
25155 											h3++;
25156 										}
25157 									}
25158 								}
25159 							}
25160 						}
25161 					}
25162 				}
25163 			}
25164 		}
25165 
25166 impossibleteachersactivitytagminhoursdaily:
25167 		if(!okteachersactivitytagminhoursdaily){
25168 			if(updateSubgroups || updateTeachers)
25169 				removeAiFromNewTimetable(ai, act, d, h);
25170 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
25171 
25172 			nConflActivities[newtime]=MAX_ACTIVITIES;
25173 			continue;
25174 		}
25175 
25176 /////////////////////////////////////////////////////////////////////////////////////////////
25177 
25178 		//allowed from teachers min gaps between ordered pair of activity tags
25179 
25180 		okteachersmingapsbetweenorderedpairofactivitytags=true;
25181 
25182 		if(1){
25183 			for(TeachersMinGapsBetweenOrderedPairOfActivityTags_item* item : qAsConst(tmgbopoatListForActivity[ai])){
25184 				bool first, second;
25185 				if(act->iActivityTagsSet.contains(item->firstActivityTag))
25186 					first=true;
25187 				else
25188 					first=false;
25189 
25190 				if(act->iActivityTagsSet.contains(item->secondActivityTag))
25191 					second=true;
25192 				else
25193 					second=false;
25194 
25195 				assert((first && !second) || (!first && second));
25196 
25197 				if(first){
25198 					assert(!second);
25199 					//after the first activity tag we need to have at least minGaps until the second activity tag.
25200 
25201 					for(int tch : qAsConst(act->iTeachersList)){
25202 						if(item->canonicalSetOfTeachers.contains(tch)){
25203 							for(int startSecond=h+act->duration; startSecond<gt.rules.nHoursPerDay; startSecond++){
25204 								if(startSecond-h-act->duration >= item->minGaps)
25205 									break;
25206 								int ai2=teachersTimetable(tch,d,startSecond);
25207 								if(ai2>=0){
25208 									if(gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(item->secondActivityTag)){
25209 										if(!conflActivities[newtime].contains(ai2)){
25210 											if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
25211 												okteachersmingapsbetweenorderedpairofactivitytags=false;
25212 												goto impossibleteachersmingapsbetweenorderedpairofactivitytags;
25213 											}
25214 											else{
25215 												conflActivities[newtime].append(ai2);
25216 												nConflActivities[newtime]++;
25217 												assert(conflActivities[newtime].count()==nConflActivities[newtime]);
25218 											}
25219 										}
25220 									}
25221 								}
25222 							}
25223 						}
25224 					}
25225 				}
25226 				else{
25227 					assert(second);
25228 					//before the second activity tag we need to have at least minGaps until the first activity tag.
25229 
25230 					for(int tch : qAsConst(act->iTeachersList)){
25231 						if(item->canonicalSetOfTeachers.contains(tch)){
25232 							for(int endFirst=h-1; endFirst>=0; endFirst--){
25233 								if(h-1-endFirst >= item->minGaps)
25234 									break;
25235 								int ai2=teachersTimetable(tch,d,endFirst);
25236 								if(ai2>=0){
25237 									if(gt.rules.internalActivitiesList[ai2].iActivityTagsSet.contains(item->firstActivityTag)){
25238 										if(!conflActivities[newtime].contains(ai2)){
25239 											if(fixedTimeActivity[ai2] || swappedActivities[ai2]){
25240 												okteachersmingapsbetweenorderedpairofactivitytags=false;
25241 												goto impossibleteachersmingapsbetweenorderedpairofactivitytags;
25242 											}
25243 											else{
25244 												conflActivities[newtime].append(ai2);
25245 												nConflActivities[newtime]++;
25246 												assert(conflActivities[newtime].count()==nConflActivities[newtime]);
25247 											}
25248 										}
25249 									}
25250 								}
25251 							}
25252 						}
25253 					}
25254 				}
25255 			}
25256 		}
25257 
25258 impossibleteachersmingapsbetweenorderedpairofactivitytags:
25259 		if(!okteachersmingapsbetweenorderedpairofactivitytags){
25260 			if(updateSubgroups || updateTeachers)
25261 				removeAiFromNewTimetable(ai, act, d, h);
25262 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
25263 
25264 			nConflActivities[newtime]=MAX_ACTIVITIES;
25265 			continue;
25266 		}
25267 
25268 
25269 /////////////////////////////////////////////////////////////////////////////////////////////
25270 
25271 		//2011-09-30
25272 		//care about activities max simultaneous in selected time slots
25273 		//I think it is best to put this after all other major constraints
25274 		//I think it is better to put this before activities occupy max time slots from selection
25275 		if(haveActivitiesMaxSimultaneousConstraints && activityHasMaxSimultaneousConstraints[ai]){
25276 			okactivitiesmaxsimultaneousinselectedtimeslots=true;
25277 
25278 			for(ActivitiesMaxSimultaneousInSelectedTimeSlots_item* item : qAsConst(amsistsListForActivity[ai])){
25279 				bool inSpecifiedTimeSlots=false;
25280 				for(int t=newtime; t<newtime+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek)
25281 					if(item->selectedTimeSlotsSet.contains(t)){
25282 						inSpecifiedTimeSlots=true;
25283 						break;
25284 					}
25285 
25286 				if(!inSpecifiedTimeSlots)
25287 					continue;
25288 
25289 				bool correct=true;
25290 
25291 				for(int t=newtime; t<newtime+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25292 					if(item->selectedTimeSlotsSet.contains(t)){
25293 						slotSetOfActivities[t]=activitiesAtTime[t];
25294 
25295 						if(slotSetOfActivities[t].count()+1 <= item->maxSimultaneous)
25296 							continue;
25297 
25298 						slotSetOfActivities[t].intersect(item->activitiesSet);
25299 
25300 						if(slotSetOfActivities[t].count()+1 <= item->maxSimultaneous)
25301 							continue;
25302 
25303 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
25304 						slotSetOfActivities[t].subtract(QSet<int>(conflActivities[newtime].constBegin(), conflActivities[newtime].constEnd()));
25305 #else
25306 						slotSetOfActivities[t].subtract(conflActivities[newtime].toSet());
25307 #endif
25308 
25309 						if(slotSetOfActivities[t].count()+1 <= item->maxSimultaneous)
25310 							continue;
25311 
25312 						assert(!slotSetOfActivities[t].contains(ai));
25313 						slotSetOfActivities[t].insert(ai);
25314 
25315 						correct=false;
25316 					}
25317 				}
25318 
25319 				if(!correct){
25320 					QList<QSet<int>> candidates;
25321 					QSet<int> allCandidates;
25322 					for(int t=newtime; t<newtime+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25323 						if(item->selectedTimeSlotsSet.contains(t)){
25324 							if(slotSetOfActivities[t].count() > item->maxSimultaneous){
25325 								QSet<int> tmpSet;
25326 
25327 								assert(slotSetOfActivities[t].count() == item->maxSimultaneous+1);
25328 								for(int ai2 : qAsConst(slotSetOfActivities[t]))
25329 									if(ai2!=ai && !fixedTimeActivity[ai2] && !swappedActivities[ai2]){
25330 										tmpSet.insert(ai2);
25331 										if(!allCandidates.contains(ai2))
25332 											allCandidates.insert(ai2);
25333 									}
25334 
25335 								candidates.append(tmpSet);
25336 							}
25337 						}
25338 					}
25339 
25340 					for(const QSet<int>& tmpSet : qAsConst(candidates))
25341 						if(tmpSet.count()==0){
25342 							okactivitiesmaxsimultaneousinselectedtimeslots=false;
25343 							goto impossibleactivitiesmaxsimultaneousinselectedtimeslots;
25344 						}
25345 
25346 					//possible to fix
25347 					while(candidates.count()>0){
25348 						int tc=candidates.count();
25349 
25350 						//To keep the generation identical on all computers - 2013-01-03
25351 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
25352 						QList<int> tmpSortedList=QList<int>(allCandidates.constBegin(), allCandidates.constEnd());
25353 #else
25354 						QList<int> tmpSortedList=allCandidates.toList();
25355 #endif
25356 						std::stable_sort(tmpSortedList.begin(), tmpSortedList.end());
25357 
25358 						int ai2;
25359 
25360 						if(level>0){
25361 							assert(allCandidates.count()==tmpSortedList.count());
25362 							int q=rng.intMRG32k3a(allCandidates.count());
25363 
25364 							ai2=tmpSortedList.at(q);
25365 							//int ai2=allCandidates.toList().at(q);
25366 						}
25367 						else{
25368 							assert(level==0);
25369 
25370 							int optMinWrong=INF;
25371 							QList<int> tl;
25372 
25373 							for(int ai3 : qAsConst(tmpSortedList)){
25374 								if(optMinWrong>triedRemovals(ai3,c.times[ai3])){
25375 								 	optMinWrong=triedRemovals(ai3,c.times[ai3]);
25376 								 	tl.clear();
25377 								 	tl.append(ai3);
25378 								}
25379 								else if(optMinWrong==triedRemovals(ai3,c.times[ai3])){
25380 								 	tl.append(ai3);
25381 								}
25382 							}
25383 
25384 							assert(tl.count()>0);
25385 							int q=rng.intMRG32k3a(tl.count());
25386 							ai2=tl.at(q);
25387 						}
25388 
25389 						////
25390 						assert(ai2!=ai);
25391 						assert(c.times[ai2]!=UNALLOCATED_TIME);
25392 						assert(!fixedTimeActivity[ai2] && !swappedActivities[ai2]);
25393 
25394 						assert(!conflActivities[newtime].contains(ai2));
25395 						conflActivities[newtime].append(ai2);
25396 
25397 						nConflActivities[newtime]++;
25398 						assert(nConflActivities[newtime]==conflActivities[newtime].count());
25399 						////
25400 
25401 						QList<QSet<int>> newCandidates;
25402 						QSet<int> newAllCandidates;
25403 
25404 						for(const QSet<int>& tmpSet : qAsConst(candidates))
25405 							if(!tmpSet.contains(ai2)){
25406 								newCandidates.append(tmpSet);
25407 								newAllCandidates.unite(tmpSet);
25408 							}
25409 
25410 						allCandidates=newAllCandidates;
25411 						candidates=newCandidates;
25412 
25413 						assert(candidates.count()<tc); //need to have a progress, not an endless loop
25414 					}
25415 				}
25416 			}
25417 
25418 impossibleactivitiesmaxsimultaneousinselectedtimeslots:
25419 			if(!okactivitiesmaxsimultaneousinselectedtimeslots){
25420 				if(updateSubgroups || updateTeachers)
25421 					removeAiFromNewTimetable(ai, act, d, h);
25422 				//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
25423 
25424 				nConflActivities[newtime]=MAX_ACTIVITIES;
25425 				continue;
25426 			}
25427 		}
25428 
25429 /////////////////////////////////////////////////////////////////////////////////////////////
25430 
25431 		//2011-09-25
25432 		//care about activities max number of occupied time slots from selection
25433 		//I think it is best to put this after all other major constraints
25434 		//I think it is best to put this after activities max simultaneous in selected time slots
25435 
25436 		if(haveActivitiesOccupyMaxConstraints && activityHasOccupyMaxConstraints[ai]){
25437 			okactivitiesoccupymaxtimeslotsfromselection=true;
25438 
25439 			for(ActivitiesOccupyMaxTimeSlotsFromSelection_item* item : qAsConst(aomtsListForActivity[ai])){
25440 				//preliminary, for speed
25441 				bool contained=false;
25442 				for(int t=newtime; t<newtime+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek)
25443 					if(item->selectedTimeSlotsSet.contains(t)){
25444 						contained=true;
25445 						break;
25446 					}
25447 				if(!contained)
25448 					continue;
25449 
25450 				int _nOcc=0;
25451 				for(int t : qAsConst(item->selectedTimeSlotsList))
25452 					if(activitiesAtTime[t].count()>0)
25453 						_nOcc++;
25454 				for(int t=newtime; t<newtime+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek)
25455 					if(item->selectedTimeSlotsSet.contains(t))
25456 						if(activitiesAtTime[t].count()==0)
25457 							_nOcc++;
25458 				if(_nOcc<=item->maxOccupiedTimeSlots)
25459 					continue;
25460 				///////
25461 
25462 				for(int t : qAsConst(item->selectedTimeSlotsList)){
25463 					slotSetOfActivities[t].clear();
25464 					slotCanEmpty[t]=true;
25465 				}
25466 
25467 				for(int ai2 : qAsConst(item->activitiesList)){
25468 					if(ai2!=ai && c.times[ai2]!=UNALLOCATED_TIME && !conflActivities[newtime].contains(ai2)){
25469 						for(int t=c.times[ai2]; t<c.times[ai2]+gt.rules.internalActivitiesList[ai2].duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25470 							if(item->selectedTimeSlotsSet.contains(t)){
25471 								slotSetOfActivities[t].insert(ai2);
25472 
25473 								if(fixedTimeActivity[ai2] || swappedActivities[ai2])
25474 									slotCanEmpty[t]=false;
25475 							}
25476 						}
25477 					}
25478 					else if(ai2==ai){
25479 						for(int t=newtime; t<newtime+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25480 							if(item->selectedTimeSlotsSet.contains(t)){
25481 								slotSetOfActivities[t].insert(ai);
25482 								slotCanEmpty[t]=false;
25483 							}
25484 						}
25485 					}
25486 				}
25487 
25488 				int nOccupied=0;
25489 				QSet<int> candidates;
25490 				for(int t : qAsConst(item->selectedTimeSlotsList)){
25491 					if(slotSetOfActivities[t].count()>0){
25492 						nOccupied++;
25493 
25494 						if(slotCanEmpty[t])
25495 							candidates.insert(t);
25496 					}
25497 				}
25498 
25499 				if(nOccupied > item->maxOccupiedTimeSlots){
25500 					int target = nOccupied - item->maxOccupiedTimeSlots;
25501 					while(target>0){
25502 						bool decreased=false;
25503 
25504 						if(candidates.count()==0){
25505 							okactivitiesoccupymaxtimeslotsfromselection=false;
25506 							goto impossibleactivitiesoccupymaxtimeslotsfromselection;
25507 						}
25508 
25509 						//To keep the generation identical on all computers - 2013-01-03
25510 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
25511 						QList<int> tmpSortedList=QList<int>(candidates.constBegin(), candidates.constEnd());
25512 #else
25513 						QList<int> tmpSortedList=candidates.toList();
25514 #endif
25515 						std::stable_sort(tmpSortedList.begin(), tmpSortedList.end());
25516 
25517 						int t;
25518 						if(level>0){
25519 							assert(candidates.count()==tmpSortedList.count());
25520 							int q=rng.intMRG32k3a(candidates.count());
25521 							t=tmpSortedList.at(q);
25522 						}
25523 						else{
25524 							assert(level==0);
25525 
25526 							QList<int> optCandidates;
25527 
25528 							int optConflActivities=MAX_ACTIVITIES;
25529 							int optMinWrong=INF;
25530 							int optNWrong=INF;
25531 							int optMinIndexAct=gt.rules.nInternalActivities;
25532 
25533 							for(int k : qAsConst(tmpSortedList)){
25534 								const QSet<int>& acts=slotSetOfActivities[k];
25535 								assert(acts.count()>0);
25536 
25537 								int tmp_n_confl_acts=acts.count();
25538 								int tmp_minWrong=INF;
25539 								int tmp_nWrong=0;
25540 								int tmp_minIndexAct=gt.rules.nInternalActivities;
25541 
25542 								for(int ai2 : qAsConst(acts)){
25543 									tmp_minWrong=min(tmp_minWrong, triedRemovals(ai2,c.times[ai2]));
25544 									tmp_nWrong+=triedRemovals(ai2,c.times[ai2]);
25545 									tmp_minIndexAct=min(tmp_minIndexAct, invPermutation[ai2]);
25546 								}
25547 
25548 								if(optMinWrong>tmp_minWrong ||
25549 								  (optMinWrong==tmp_minWrong && optNWrong>tmp_nWrong) ||
25550 								  (optMinWrong==tmp_minWrong && optNWrong==tmp_nWrong && optConflActivities>tmp_n_confl_acts) ||
25551 								  (optMinWrong==tmp_minWrong && optNWrong==tmp_nWrong && optConflActivities==tmp_n_confl_acts && optMinIndexAct>tmp_minIndexAct)){
25552 									optConflActivities=tmp_n_confl_acts;
25553 									optMinWrong=tmp_minWrong;
25554 									optNWrong=tmp_nWrong;
25555 									optMinIndexAct=tmp_minIndexAct;
25556 
25557 									optCandidates.clear();
25558 									optCandidates.append(k);
25559 								}
25560 								else if(optMinWrong==tmp_minWrong && optNWrong==tmp_nWrong && optConflActivities==tmp_n_confl_acts && optMinIndexAct==tmp_minIndexAct){
25561 									//be very careful, this case is possible if we have activities with duration >= 2. Don't assert(0).
25562 									optCandidates.append(k);
25563 								}
25564 							}
25565 
25566 							assert(optCandidates.count()>0);
25567 							int q=rng.intMRG32k3a(optCandidates.count());
25568 							t=optCandidates.at(q);
25569 						}
25570 						/*int t=tmpSortedList.at(q);*/
25571 						//int t=candidates.toList().at(q);
25572 
25573 						const QSet<int>& tmpSet=slotSetOfActivities[t];
25574 						//To keep the generation identical on all computers - 2013-01-03
25575 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
25576 						QList<int> tmpListFromSet=QList<int>(tmpSet.constBegin(), tmpSet.constEnd());
25577 #else
25578 						QList<int> tmpListFromSet=tmpSet.toList();
25579 #endif
25580 						std::stable_sort(tmpListFromSet.begin(), tmpListFromSet.end());
25581 						//Randomize list
25582 						for(int i=0; i<tmpListFromSet.count(); i++){
25583 							int t=tmpListFromSet.at(i);
25584 							int r=rng.intMRG32k3a(tmpListFromSet.count()-i);
25585 							tmpListFromSet[i]=tmpListFromSet[i+r];
25586 							tmpListFromSet[i+r]=t;
25587 						}
25588 
25589 						for(int ai2 : qAsConst(tmpListFromSet)){
25590 							assert(ai2!=ai);
25591 							assert(c.times[ai2]!=UNALLOCATED_TIME);
25592 							assert(!fixedTimeActivity[ai2] && !swappedActivities[ai2]);
25593 
25594 							for(int tt=c.times[ai2]; tt<c.times[ai2]+gt.rules.internalActivitiesList[ai2].duration*gt.rules.nDaysPerWeek; tt+=gt.rules.nDaysPerWeek)
25595 								if(item->selectedTimeSlotsSet.contains(tt)){
25596 									assert(slotSetOfActivities[tt].contains(ai2));
25597 									slotSetOfActivities[tt].remove(ai2);
25598 									if(slotSetOfActivities[tt].count()==0){
25599 										assert(candidates.contains(tt));
25600 										candidates.remove(tt);
25601 										target--;
25602 
25603 										decreased=true;
25604 									}
25605 								}
25606 
25607 							assert(!conflActivities[newtime].contains(ai2));
25608 							conflActivities[newtime].append(ai2);
25609 
25610 							nConflActivities[newtime]++;
25611 							assert(nConflActivities[newtime]==conflActivities[newtime].count());
25612 						}
25613 
25614 						assert(decreased);
25615 					}
25616 				}
25617 			}
25618 
25619 impossibleactivitiesoccupymaxtimeslotsfromselection:
25620 			if(!okactivitiesoccupymaxtimeslotsfromselection){
25621 				if(updateSubgroups || updateTeachers)
25622 					removeAiFromNewTimetable(ai, act, d, h);
25623 				//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
25624 
25625 				nConflActivities[newtime]=MAX_ACTIVITIES;
25626 				continue;
25627 			}
25628 		}
25629 
25630 /////////////////////////////////////////////////////////////////////////////////////////////
25631 
25632 		//2020-04-30
25633 		//care about max total activities from set in selected timeslots
25634 		//I think it is best to put this after all other major constraints
25635 		//I think it is best to put this after activities max simultaneous in selected time slots
25636 		//I think it is best to put this after activities occupy max time slots from selection
25637 
25638 		okmaxtotalactivitiesfromsetinselectedtimeslots=true;
25639 
25640 		if(gt.rules.mode==BLOCK_PLANNING){
25641 			for(ActivitiesMaxTotalFromSetInSelectedTimeSlots_item* item : qAsConst(amtfsistsListForActivity[ai])){
25642 				bool aiContained=false;
25643 				for(int t=newtime; t<newtime+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25644 					if(item->selectedTimeSlotsSet.contains(t)){
25645 						aiContained=true;
25646 						break;
25647 					}
25648 				}
25649 
25650 				if(aiContained){
25651 					int _nOcc=1; //current activity is counted
25652 
25653 					QList<int> candidatesList;
25654 
25655 					for(int ai2 : qAsConst(item->activitiesList)){
25656 						if(ai2!=ai && c.times[ai2]!=UNALLOCATED_TIME && !conflActivities[newtime].contains(ai2)){
25657 							for(int t=c.times[ai2]; t<c.times[ai2]+gt.rules.internalActivitiesList[ai2].duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25658 								if(item->selectedTimeSlotsSet.contains(t)){
25659 									_nOcc++;
25660 
25661 									if(!fixedTimeActivity[ai2] && !swappedActivities[ai2])
25662 										candidatesList.append(ai2);
25663 
25664 									break;
25665 								}
25666 							}
25667 						}
25668 					}
25669 
25670 					if(_nOcc>item->maxActivities){ //not OK
25671 						assert(_nOcc==item->maxActivities+1);
25672 
25673 						if(candidatesList.count()==0){
25674 							okmaxtotalactivitiesfromsetinselectedtimeslots=false;
25675 							goto impossiblemaxtotalactivitiesfromsetinselectedtimeslots;
25676 						}
25677 						else{
25678 							int ai2;
25679 							if(level>0){
25680 								int q=rng.intMRG32k3a(candidatesList.count());
25681 								ai2=candidatesList.at(q);
25682 							}
25683 							else{
25684 								assert(level==0);
25685 
25686 								int optMinWrong=INF;
25687 
25688 								QList<int> tl;
25689 
25690 								for(int ai3 : qAsConst(candidatesList)){
25691 									if(optMinWrong>triedRemovals(ai3,c.times[ai3])){
25692 										optMinWrong=triedRemovals(ai3,c.times[ai3]);
25693 										tl.clear();
25694 										tl.append(ai3);
25695 									}
25696 									else if(optMinWrong==triedRemovals(ai3,c.times[ai3])){
25697 										tl.append(ai3);
25698 									}
25699 								}
25700 
25701 								assert(tl.count()>0);
25702 								int q=rng.intMRG32k3a(tl.count());
25703 								ai2=tl.at(q);
25704 							}
25705 
25706 							assert(ai2!=ai);
25707 							assert(!fixedTimeActivity[ai2] && !swappedActivities[ai2]);
25708 
25709 							assert(!conflActivities[newtime].contains(ai2));
25710 							conflActivities[newtime].append(ai2);
25711 
25712 							nConflActivities[newtime]++;
25713 							assert(nConflActivities[newtime]==conflActivities[newtime].count());
25714 						}
25715 					}
25716 				}
25717 			}
25718 		}
25719 
25720 impossiblemaxtotalactivitiesfromsetinselectedtimeslots:
25721 		if(!okmaxtotalactivitiesfromsetinselectedtimeslots){
25722 			if(updateSubgroups || updateTeachers)
25723 				removeAiFromNewTimetable(ai, act, d, h);
25724 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
25725 
25726 			nConflActivities[newtime]=MAX_ACTIVITIES;
25727 			continue;
25728 		}
25729 
25730 /////////////////////////////////////////////////////////////////////////////////////////////
25731 
25732 		//2019-11-16
25733 		//care about activities min number of occupied time slots from selection
25734 		//I think it is best to put this after all other major constraints
25735 		//I think it is best to put this after activities max simultaneous in selected time slots
25736 		//I think it is best to put this after activities occupy max time slots from selection
25737 
25738 		okactivitiesoccupymintimeslotsfromselection=true;
25739 
25740 		if(!aomintsListForActivity[ai].isEmpty())
25741 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
25742 			conflActivitiesSet=QSet<int>(conflActivities[newtime].constBegin(), conflActivities[newtime].constEnd());
25743 #else
25744 			conflActivitiesSet=conflActivities[newtime].toSet();
25745 #endif
25746 
25747 		for(ActivitiesOccupyMinTimeSlotsFromSelection_item* item : qAsConst(aomintsListForActivity[ai])){
25748 			int availableDuration=0;
25749 			int nOccupied=0;
25750 			//static int aminoCnt[MAX_DAYS_PER_WEEK*MAX_HOURS_PER_DAY];
25751 
25752 			for(int t : qAsConst(item->selectedTimeSlotsList))
25753 				aminoCnt[t]=0;
25754 			for(int ai2 : qAsConst(item->activitiesList)){
25755 				if(ai2==ai){
25756 					for(int t=newtime; t<newtime+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25757 						if(item->selectedTimeSlotsSet.contains(t)){
25758 							if(aminoCnt[t]==0)
25759 								nOccupied++;
25760 							aminoCnt[t]++;
25761 						}
25762 					}
25763 				}
25764 				else if(c.times[ai2]!=UNALLOCATED_TIME && !conflActivitiesSet.contains(ai2)){
25765 					assert(ai2!=ai);
25766 					for(int t=c.times[ai2]; t<c.times[ai2]+gt.rules.internalActivitiesList[ai2].duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25767 						if(item->selectedTimeSlotsSet.contains(t)){
25768 							if(aminoCnt[t]==0)
25769 								nOccupied++;
25770 							aminoCnt[t]++;
25771 						}
25772 					}
25773 
25774 				}
25775 				else{
25776 					assert(ai2!=ai);
25777 					availableDuration+=gt.rules.internalActivitiesList[ai2].duration;
25778 				}
25779 
25780 				if(nOccupied+availableDuration >= item->minOccupiedTimeSlots){
25781 					break; //OK
25782 				}
25783 			}
25784 
25785 			assert(nOccupied<=item->selectedTimeSlotsSet.count());
25786 
25787 			if(nOccupied+availableDuration < item->minOccupiedTimeSlots){
25788 				//not OK
25789 				for(;;){
25790 					QList<int> candidatesList;
25791 					for(int ai2 : qAsConst(item->activitiesList)){
25792 						if(ai2!=ai && c.times[ai2]!=UNALLOCATED_TIME && !conflActivitiesSet.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2]){
25793 							for(int t=c.times[ai2]; t<c.times[ai2]+gt.rules.internalActivitiesList[ai2].duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25794 								if(item->selectedTimeSlotsSet.contains(t)){
25795 									assert(aminoCnt[t]>=1);
25796 									if(aminoCnt[t]>=2){
25797 										candidatesList.append(ai2);
25798 										break;
25799 									}
25800 								}
25801 								else{
25802 									candidatesList.append(ai2);
25803 									break;
25804 								}
25805 							}
25806 						}
25807 					}
25808 
25809 					if(candidatesList.count()==0){
25810 						okactivitiesoccupymintimeslotsfromselection=false;
25811 						goto impossibleactivitiesoccupymintimeslotsfromselection;
25812 					}
25813 					else{
25814 						int ai2;
25815 						if(level>0){
25816 							int q=rng.intMRG32k3a(candidatesList.count());
25817 							ai2=candidatesList.at(q);
25818 						}
25819 						else{
25820 							assert(level==0);
25821 
25822 							int optMinWrong=INF;
25823 
25824 							QList<int> tl;
25825 
25826 							for(int ai3 : qAsConst(candidatesList)){
25827 								if(optMinWrong>triedRemovals(ai3,c.times[ai3])){
25828 									optMinWrong=triedRemovals(ai3,c.times[ai3]);
25829 									tl.clear();
25830 									tl.append(ai3);
25831 								}
25832 								else if(optMinWrong==triedRemovals(ai3,c.times[ai3])){
25833 							 		tl.append(ai3);
25834 								}
25835 							}
25836 
25837 							assert(tl.count()>0);
25838 							int q=rng.intMRG32k3a(tl.count());
25839 							ai2=tl.at(q);
25840 						}
25841 
25842 						assert(ai2!=ai);
25843 						assert(!fixedTimeActivity[ai2] && !swappedActivities[ai2]);
25844 
25845 						assert(!conflActivitiesSet.contains(ai2));
25846 						assert(!conflActivities[newtime].contains(ai2));
25847 						conflActivitiesSet.insert(ai2);
25848 						conflActivities[newtime].append(ai2);
25849 
25850 						nConflActivities[newtime]++;
25851 						assert(nConflActivities[newtime]==conflActivities[newtime].count());
25852 
25853 						for(int t=c.times[ai2]; t<c.times[ai2]+gt.rules.internalActivitiesList[ai2].duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25854 							if(item->selectedTimeSlotsSet.contains(t)){
25855 								assert(aminoCnt[t]>=1);
25856 								if(aminoCnt[t]==1){
25857 									nOccupied--;
25858 									assert(nOccupied>=0);
25859 								}
25860 
25861 								aminoCnt[t]--;
25862 							}
25863 						}
25864 						availableDuration+=gt.rules.internalActivitiesList[ai2].duration;
25865 
25866 						if(nOccupied+availableDuration >= item->minOccupiedTimeSlots){
25867 							break; //OK
25868 						}
25869 					}
25870 				}
25871 			}
25872 		}
25873 
25874 impossibleactivitiesoccupymintimeslotsfromselection:
25875 		if(!okactivitiesoccupymintimeslotsfromselection){
25876 			if(updateSubgroups || updateTeachers)
25877 				removeAiFromNewTimetable(ai, act, d, h);
25878 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
25879 
25880 			nConflActivities[newtime]=MAX_ACTIVITIES;
25881 			continue;
25882 		}
25883 
25884 /////////////////////////////////////////////////////////////////////////////////////////////
25885 
25886 		//2019-11-16
25887 		//care about activities min simultaneous in selected time slots
25888 		//I think it is best to put this after all other major constraints
25889 		//I think it is best to put this after activities max simultaneous in selected time slots
25890 		//I think it is best to put this after activities occupy max time slots from selection
25891 		//I think it is best to put this after activities occupy min time slots from selection
25892 
25893 		okactivitiesminsimultaneousinselectedtimeslots=true;
25894 
25895 		if(!aminsistsListForActivity[ai].isEmpty())
25896 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
25897 			conflActivitiesSet=QSet<int>(conflActivities[newtime].constBegin(), conflActivities[newtime].constEnd());
25898 #else
25899 			conflActivitiesSet=conflActivities[newtime].toSet();
25900 #endif
25901 
25902 		for(ActivitiesMinSimultaneousInSelectedTimeSlots_item* item : qAsConst(aminsistsListForActivity[ai])){
25903 			int totalRequired;
25904 			if(!item->allowEmptySlots)
25905 				totalRequired = item->selectedTimeSlotsList.count() * item->minSimultaneous;
25906 			else
25907 				totalRequired=0;
25908 
25909 			int availableDuration=0;
25910 			//static int aminsCnt[MAX_DAYS_PER_WEEK*MAX_HOURS_PER_DAY];
25911 
25912 			int partialFilled=0;
25913 
25914 			//static bool possibleToEmptySlot[MAX_DAYS_PER_WEEK*MAX_HOURS_PER_DAY];
25915 
25916 			for(int t : qAsConst(item->selectedTimeSlotsList)){
25917 				aminsCnt[t]=0;
25918 				possibleToEmptySlot[t]=true;
25919 			}
25920 			for(int ai2 : qAsConst(item->activitiesList)){
25921 				if(ai2==ai){
25922 					for(int t=newtime; t<newtime+act->duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25923 						if(item->selectedTimeSlotsSet.contains(t)){
25924 							if(item->allowEmptySlots && aminsCnt[t]==0)
25925 								totalRequired+=item->minSimultaneous;
25926 
25927 							if(aminsCnt[t]<item->minSimultaneous)
25928 								partialFilled++;
25929 
25930 							aminsCnt[t]++;
25931 
25932 							if(item->allowEmptySlots)
25933 								if(possibleToEmptySlot[t])
25934 									possibleToEmptySlot[t]=false;
25935 						}
25936 					}
25937 				}
25938 				else if(c.times[ai2]!=UNALLOCATED_TIME && !conflActivitiesSet.contains(ai2)){
25939 					assert(ai2!=ai);
25940 					for(int t=c.times[ai2]; t<c.times[ai2]+gt.rules.internalActivitiesList[ai2].duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25941 						if(item->selectedTimeSlotsSet.contains(t)){
25942 							if(item->allowEmptySlots && aminsCnt[t]==0)
25943 								totalRequired+=item->minSimultaneous;
25944 
25945 							if(aminsCnt[t]<item->minSimultaneous)
25946 								partialFilled++;
25947 
25948 							aminsCnt[t]++;
25949 
25950 							if(item->allowEmptySlots && (fixedTimeActivity[ai2] || swappedActivities[ai2]))
25951 								if(possibleToEmptySlot[t])
25952 									possibleToEmptySlot[t]=false;
25953 						}
25954 					}
25955 				}
25956 				else{
25957 					assert(ai2!=ai);
25958 					availableDuration+=gt.rules.internalActivitiesList[ai2].duration;
25959 				}
25960 
25961 				if(!item->allowEmptySlots && partialFilled+availableDuration >= totalRequired)
25962 					break; //OK
25963 			}
25964 
25965 			if(partialFilled+availableDuration < totalRequired){
25966 				//not OK
25967 				for(;;){
25968 					QList<int> candidatesList;
25969 					for(int ai2 : qAsConst(item->activitiesList)){
25970 						if(ai2!=ai && c.times[ai2]!=UNALLOCATED_TIME && !conflActivitiesSet.contains(ai2) && !fixedTimeActivity[ai2] && !swappedActivities[ai2]){
25971 							for(int t=c.times[ai2]; t<c.times[ai2]+gt.rules.internalActivitiesList[ai2].duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
25972 								if(item->selectedTimeSlotsSet.contains(t)){
25973 									if(aminsCnt[t]>item->minSimultaneous || (item->allowEmptySlots && possibleToEmptySlot[t])){
25974 										candidatesList.append(ai2);
25975 										break;
25976 									}
25977 								}
25978 								else{
25979 									candidatesList.append(ai2);
25980 									break;
25981 								}
25982 							}
25983 						}
25984 					}
25985 
25986 					if(candidatesList.count()==0){
25987 						okactivitiesminsimultaneousinselectedtimeslots=false;
25988 						goto impossibleactivitiesminsimultaneousinselectedtimeslots;
25989 					}
25990 					else{
25991 						int ai2;
25992 						if(level>0){
25993 							int q=rng.intMRG32k3a(candidatesList.count());
25994 							ai2=candidatesList.at(q);
25995 						}
25996 						else{
25997 							assert(level==0);
25998 
25999 							int optMinWrong=INF;
26000 
26001 							QList<int> tl;
26002 
26003 							for(int ai3 : qAsConst(candidatesList)){
26004 								if(optMinWrong>triedRemovals(ai3,c.times[ai3])){
26005 								 	optMinWrong=triedRemovals(ai3,c.times[ai3]);
26006 								 	tl.clear();
26007 								 	tl.append(ai3);
26008 								}
26009 								else if(optMinWrong==triedRemovals(ai3,c.times[ai3])){
26010 								 	tl.append(ai3);
26011 								}
26012 							}
26013 
26014 							assert(tl.count()>0);
26015 							int q=rng.intMRG32k3a(tl.count());
26016 							ai2=tl.at(q);
26017 						}
26018 
26019 						assert(ai2!=ai);
26020 						assert(!fixedTimeActivity[ai2] && !swappedActivities[ai2]);
26021 
26022 						assert(!conflActivitiesSet.contains(ai2));
26023 						assert(!conflActivities[newtime].contains(ai2));
26024 						conflActivitiesSet.insert(ai2);
26025 						conflActivities[newtime].append(ai2);
26026 
26027 						nConflActivities[newtime]++;
26028 						assert(nConflActivities[newtime]==conflActivities[newtime].count());
26029 
26030 						for(int t=c.times[ai2]; t<c.times[ai2]+gt.rules.internalActivitiesList[ai2].duration*gt.rules.nDaysPerWeek; t+=gt.rules.nDaysPerWeek){
26031 							if(item->selectedTimeSlotsSet.contains(t)){
26032 								assert(aminsCnt[t]>=1);
26033 								if(aminsCnt[t]<=item->minSimultaneous){
26034 									partialFilled--;
26035 									assert(partialFilled>=0);
26036 								}
26037 
26038 								aminsCnt[t]--;
26039 
26040 								if(item->allowEmptySlots && aminsCnt[t]==0){
26041 									totalRequired-=item->minSimultaneous;
26042 									assert(totalRequired>=0);
26043 								}
26044 							}
26045 						}
26046 						availableDuration+=gt.rules.internalActivitiesList[ai2].duration;
26047 
26048 						if(partialFilled+availableDuration >= totalRequired)
26049 							break; //OK
26050 					}
26051 				}
26052 			}
26053 		}
26054 
26055 impossibleactivitiesminsimultaneousinselectedtimeslots:
26056 		if(!okactivitiesminsimultaneousinselectedtimeslots){
26057 			if(updateSubgroups || updateTeachers)
26058 				removeAiFromNewTimetable(ai, act, d, h);
26059 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
26060 
26061 			nConflActivities[newtime]=MAX_ACTIVITIES;
26062 			continue;
26063 		}
26064 
26065 /////////////////////////////////////////////////////////////////////////////////////////////
26066 
26067 		//2020-01-14
26068 
26069 		okactivitiesmaxinaterm=true;
26070 		if(gt.rules.mode==TERMS){
26071 			if(amiatListForActivity[ai].count()>0){
26072 				int termai = (newtime%gt.rules.nDaysPerWeek)/gt.rules.nDaysPerTerm;
26073 				for(ActivitiesMaxInATerm_item* item : qAsConst(amiatListForActivity[ai])){
26074 					int countTerm=1; //ai
26075 					QList<int> candidates;
26076 
26077 					for(int ai2 : qAsConst(item->activitiesList))
26078 						if(ai2!=ai && c.times[ai2]!=UNALLOCATED_TIME && !conflActivities[newtime].contains(ai2)){
26079 							int termai2 = (c.times[ai2]%gt.rules.nDaysPerWeek)/gt.rules.nDaysPerTerm;
26080 							if(termai2==termai){
26081 								countTerm++;
26082 								if(!fixedTimeActivity[ai2] && !swappedActivities[ai2])
26083 									candidates.append(ai2);
26084 							}
26085 						}
26086 
26087 					if(countTerm > item->maxActivitiesInATerm){
26088 						assert(countTerm==item->maxActivitiesInATerm+1);
26089 						if(candidates.count()==0){
26090 							okactivitiesmaxinaterm=false;
26091 							goto impossibleactivitiesmaxinaterm;
26092 						}
26093 						int ai2=-1;
26094 						if(level>0){
26095 							ai2=candidates.at(rng.intMRG32k3a(candidates.count()));
26096 						}
26097 						else{
26098 							assert(level==0);
26099 
26100 							int optMinWrong=INF;
26101 
26102 							QList<int> tl;
26103 
26104 							for(int ai3 : qAsConst(candidates)){
26105 								if(optMinWrong>triedRemovals(ai3,c.times[ai3])){
26106 									optMinWrong=triedRemovals(ai3,c.times[ai3]);
26107 									tl.clear();
26108 									tl.append(ai3);
26109 								}
26110 								else if(optMinWrong==triedRemovals(ai3,c.times[ai3])){
26111 									tl.append(ai3);
26112 								}
26113 							}
26114 
26115 							assert(tl.count()>0);
26116 							int q=rng.intMRG32k3a(tl.count());
26117 							ai2=tl.at(q);
26118 						}
26119 
26120 						assert(ai2>=0);
26121 
26122 						assert(!conflActivities[newtime].contains(ai2));
26123 						conflActivities[newtime].append(ai2);
26124 
26125 						nConflActivities[newtime]++;
26126 						assert(nConflActivities[newtime]==conflActivities[newtime].count());
26127 					}
26128 				}
26129 			}
26130 		}
26131 
26132 impossibleactivitiesmaxinaterm:
26133 		if(!okactivitiesmaxinaterm){
26134 			if(updateSubgroups || updateTeachers)
26135 				removeAiFromNewTimetable(ai, act, d, h);
26136 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
26137 
26138 			nConflActivities[newtime]=MAX_ACTIVITIES;
26139 			continue;
26140 		}
26141 
26142 /////////////////////////////////////////////////////////////////////////////////////////////
26143 
26144 		//2020-01-14
26145 
26146 		okactivitiesoccupymaxterms=true;
26147 		if(gt.rules.mode==TERMS){
26148 			if(aomtListForActivity[ai].count()>0){
26149 				int termai = (newtime%gt.rules.nDaysPerWeek)/gt.rules.nDaysPerTerm;
26150 				for(ActivitiesOccupyMaxTerms_item* item : qAsConst(aomtListForActivity[ai])){
26151 					//bool canEmptyTerm[5];
26152 					for(int i=0; i<gt.rules.nTerms; i++)
26153 						canEmptyTerm[i]=true;
26154 
26155 					//QList<int> termActivities[5];
26156 					for(int i=0; i<gt.rules.nTerms; i++)
26157 						termActivities[i].clear();
26158 
26159 					termActivities[termai].append(ai);
26160 					canEmptyTerm[termai]=false;
26161 
26162 					for(int ai2 : qAsConst(item->activitiesList))
26163 						if(ai2!=ai && c.times[ai2]!=UNALLOCATED_TIME && !conflActivities[newtime].contains(ai2)){
26164 							int termai2 = (c.times[ai2]%gt.rules.nDaysPerWeek)/gt.rules.nDaysPerTerm;
26165 							termActivities[termai2].append(ai2);
26166 							if(fixedTimeActivity[ai2] || swappedActivities[ai2])
26167 								canEmptyTerm[termai2]=false;
26168 						}
26169 
26170 					int nocc=0;
26171 					for(int i=0; i<gt.rules.nTerms; i++)
26172 						if(termActivities[i].count()>0)
26173 							nocc++;
26174 
26175 					if(nocc > item->maxOccupiedTerms){
26176 						assert(nocc == item->maxOccupiedTerms+1);
26177 
26178 						QList<int> candidateTerms;
26179 						for(int i=0; i<gt.rules.nTerms; i++)
26180 							if(canEmptyTerm[i] && termActivities[i].count()>0)
26181 								candidateTerms.append(i);
26182 
26183 						if(candidateTerms.count()==0){
26184 							okactivitiesoccupymaxterms=false;
26185 							goto impossibleactivitiesoccupymaxterms;
26186 						}
26187 
26188 						int t=-1;
26189 						if(level>0){
26190 							t=candidateTerms.at(rng.intMRG32k3a(candidateTerms.count()));
26191 						}
26192 						else{
26193 							assert(level==0);
26194 
26195 							QList<int> optCandidates;
26196 
26197 							int optConflActivities=MAX_ACTIVITIES;
26198 							int optMinWrong=INF;
26199 							int optNWrong=INF;
26200 							int optMinIndexAct=gt.rules.nInternalActivities;
26201 
26202 							for(int k : qAsConst(candidateTerms)){
26203 								assert(canEmptyTerm[k]);
26204 								QList<int> acts=termActivities[k];
26205 								assert(acts.count()>0);
26206 
26207 								int tmp_n_confl_acts=acts.count();
26208 								int tmp_minWrong=INF;
26209 								int tmp_nWrong=0;
26210 								int tmp_minIndexAct=gt.rules.nInternalActivities;
26211 
26212 								for(int ai2 : qAsConst(acts)){
26213 									tmp_minWrong=min(tmp_minWrong, triedRemovals(ai2,c.times[ai2]));
26214 									tmp_nWrong+=triedRemovals(ai2,c.times[ai2]);
26215 									tmp_minIndexAct=min(tmp_minIndexAct, invPermutation[ai2]);
26216 								}
26217 
26218 								if(optMinWrong>tmp_minWrong ||
26219 								  (optMinWrong==tmp_minWrong && optNWrong>tmp_nWrong) ||
26220 								  (optMinWrong==tmp_minWrong && optNWrong==tmp_nWrong && optConflActivities>tmp_n_confl_acts) ||
26221 								  (optMinWrong==tmp_minWrong && optNWrong==tmp_nWrong && optConflActivities==tmp_n_confl_acts && optMinIndexAct>tmp_minIndexAct)){
26222 									optConflActivities=tmp_n_confl_acts;
26223 									optMinWrong=tmp_minWrong;
26224 									optNWrong=tmp_nWrong;
26225 									optMinIndexAct=tmp_minIndexAct;
26226 
26227 									optCandidates.clear();
26228 									optCandidates.append(k);
26229 								}
26230 								else if(optMinWrong==tmp_minWrong && optNWrong==tmp_nWrong && optConflActivities==tmp_n_confl_acts && optMinIndexAct==tmp_minIndexAct){
26231 									//be very careful, this case is possible if we have activities with duration >= 2. Don't assert(0).
26232 									optCandidates.append(k);
26233 								}
26234 							}
26235 
26236 							assert(optCandidates.count()>0);
26237 							int q=rng.intMRG32k3a(optCandidates.count());
26238 							t=optCandidates.at(q);
26239 						}
26240 
26241 						assert(canEmptyTerm[t]);
26242 						assert(termActivities[t].count()>0);
26243 
26244 						for(int ai2 : qAsConst(termActivities[t])){
26245 							assert(ai2!=ai);
26246 							assert(c.times[ai2]!=UNALLOCATED_TIME);
26247 							assert(!fixedTimeActivity[ai2] && !swappedActivities[ai2]);
26248 
26249 							assert(!conflActivities[newtime].contains(ai2));
26250 							conflActivities[newtime].append(ai2);
26251 
26252 							nConflActivities[newtime]++;
26253 							assert(nConflActivities[newtime]==conflActivities[newtime].count());
26254 						}
26255 					}
26256 				}
26257 			}
26258 		}
26259 
26260 impossibleactivitiesoccupymaxterms:
26261 		if(!okactivitiesoccupymaxterms){
26262 			if(updateSubgroups || updateTeachers)
26263 				removeAiFromNewTimetable(ai, act, d, h);
26264 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
26265 
26266 			nConflActivities[newtime]=MAX_ACTIVITIES;
26267 			continue;
26268 		}
26269 
26270 /////////////////////////////////////////////////////////////////////////////////////////////
26271 
26272 skip_here_if_already_allocated_in_time:
26273 
26274 		//////////////////rooms
26275 		realRoomsListLevel[newtime].clear();
26276 		bool okroomnotavailable=getRoom(level, act, ai, d, h, roomSlots[newtime], selectedRoom[newtime], conflActivities[newtime], nConflActivities[newtime], realRoomsListLevel[newtime]);
26277 
26278 //impossibleroomnotavailable:
26279 		if(!okroomnotavailable){
26280 			if(c.times[ai]==UNALLOCATED_TIME){
26281 				if(updateSubgroups || updateTeachers)
26282 					removeAiFromNewTimetable(ai, act, d, h);
26283 			}
26284 			//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
26285 
26286 			nConflActivities[newtime]=MAX_ACTIVITIES;
26287 			continue;
26288 		}
26289 		///////////////////////
26290 
26291 		if(c.times[ai]==UNALLOCATED_TIME){
26292 			if(updateSubgroups || updateTeachers)
26293 				removeAiFromNewTimetable(ai, act, d, h);
26294 		}
26295 		//removeConflActivities(conflActivities[newtime], nConflActivities[newtime], act, newtime);
26296 
26297 		///////////////////////////////
26298 		//5.0.0-preview28
26299 		//no conflicting activities for this timeslot - place the activity and return
26300 		if(nConflActivities[newtime]==0 && nMinDaysBroken[newtime]==0.0){
26301 			assert(c.times[ai]==UNALLOCATED_TIME || (fixedTimeActivity[ai] && !fixedSpaceActivity[ai]));
26302 
26303 			if(c.times[ai]!=UNALLOCATED_TIME && fixedTimeActivity[ai] && !fixedSpaceActivity[ai])
26304 				assert(c.times[ai]==newtime);
26305 
26306 			assert(conflActivities[newtime].count()==0);
26307 
26308 			restoreActIndex[nRestore]=ai;
26309 			restoreTime[nRestore]=c.times[ai];
26310 			restoreRoom[nRestore]=c.rooms[ai];
26311 			restoreRealRoomsList[nRestore]=c.realRoomsList[ai];
26312 			nRestore++;
26313 
26314 			//5.0.0-preview25
26315 			assert(swappedActivities[ai]);
26316 
26317 			moveActivity(ai, c.times[ai], newtime, c.rooms[ai], selectedRoom[newtime], c.realRoomsList[ai], realRoomsListLevel[newtime]);
26318 
26319 			foundGoodSwap=true;
26320 			return;
26321 		}
26322 		///////////////////////////////
26323 
26324 		assert(nConflActivities[newtime]==conflActivities[newtime].count());
26325 	}
26326 
26327 	//O(n*log(n)) stable sorting
26328 	currentLevel=level;
26329 	std::stable_sort(perm+0, perm+gt.rules.nHoursPerWeek, [this](int i, int j){return compareFunctionGenerate(i, j);});
26330 
26331 	for(int i=1; i<gt.rules.nHoursPerWeek; i++){
26332 		assert( (nConflActivities[perm[i-1]]<nConflActivities[perm[i]])
26333 		 || ( (nConflActivities[perm[i-1]]==nConflActivities[perm[i]]) &&
26334 		 (nMinDaysBroken[perm[i-1]]<=nMinDaysBroken[perm[i]]) ) );
26335 	}
26336 
26337 	if(level==0 && (nConflActivities[perm[0]]==MAX_ACTIVITIES)){
26338 		//to check if the generation was stopped
26339 		if(this->isThreaded){
26340 			myMutex.unlock();
26341 			myMutex.lock();
26342 		}
26343 		if(!abortOptimization && activity_count_impossible_tries<MAX_RETRIES_FOR_AN_ACTIVITY_AT_LEVEL_0){
26344 			activity_count_impossible_tries++;
26345 			goto again_if_impossible_activity;
26346 		}
26347 		else{
26348 			if(VERBOSE){
26349 				cout<<__FILE__<<" line "<<__LINE__<<" - WARNING - after retrying for "<<activity_count_impossible_tries
26350 				<<" times - no possible time slot for activity with id=="<<gt.rules.internalActivitiesList[ai].id<<endl;
26351 			}
26352 		}
26353 	}
26354 
26355 	if(level==0){
26356 		for(int i=0; i<gt.rules.nHoursPerWeek; i++){
26357 			l0nWrong[i]=INF;
26358 			l0minWrong[i]=INF;
26359 			l0minIndexAct[i]=gt.rules.nInternalActivities;
26360 		}
26361 
26362 		QList<int> tim;
26363 		for(int i=0; i<gt.rules.nHoursPerWeek; i++)
26364 			if(nConflActivities[perm[i]]>0 && nConflActivities[perm[i]]<MAX_ACTIVITIES
26365 			 && roomSlots[perm[i]]!=UNALLOCATED_SPACE)
26366 				tim.append(perm[i]);
26367 		if(tim.count()==0 && nConflActivities[perm[0]]==MAX_ACTIVITIES){
26368 			//cout<<__FILE__<<" line "<<__LINE__<<" - WARNING - no possible time slot for activity with id=="<<
26369 			// gt.rules.internalActivitiesList[ai].id<<endl;
26370 
26371 			impossibleActivity=true;
26372 		}
26373 		if(tim.count()>0){
26374 			for(int i : qAsConst(tim)){
26375 				int cnt=0;
26376 				int m=gt.rules.nInternalActivities;
26377 				for(int aii : qAsConst(conflActivities[i])){
26378 					if(triedRemovals(aii,c.times[aii])>0)
26379 						cnt+=triedRemovals(aii,c.times[aii]);
26380 
26381 					if(l0minWrong[i]>triedRemovals(aii,c.times[aii]))
26382 						l0minWrong[i]=triedRemovals(aii,c.times[aii]);
26383 
26384 					int j=invPermutation[aii];
26385 					if(m>j)
26386 						m=j;
26387 				}
26388 				l0nWrong[i]=cnt;
26389 				l0minIndexAct[i]=m;
26390 			}
26391 
26392 			int optMinIndex=gt.rules.nInternalActivities;
26393 			int optNWrong=INF;
26394 			int optMinWrong=INF;
26395 			int optNConflActs=gt.rules.nInternalActivities;
26396 			int j=-1;
26397 
26398 			for(int i : qAsConst(tim)){
26399 				//choose a random time out of those with the minimum number of wrongly replaced activities
26400 				if(optMinWrong>l0minWrong[i]
26401 				 || (optMinWrong==l0minWrong[i] && optNWrong>l0nWrong[i])
26402 				 || (optMinWrong==l0minWrong[i] && optNWrong==l0nWrong[i] && optNConflActs>nConflActivities[i])
26403 				 || (optMinWrong==l0minWrong[i] && optNWrong==l0nWrong[i] && optNConflActs==nConflActivities[i] && optMinIndex>l0minIndexAct[i])){
26404 					optNWrong=l0nWrong[i];
26405 					optMinWrong=l0minWrong[i];
26406 					optNConflActs=nConflActivities[i];
26407 					optMinIndex=l0minIndexAct[i];
26408 					j=i;
26409 				}
26410 			}
26411 
26412 			assert(j>=0);
26413 			QList<int> tim2;
26414 			for(int i : qAsConst(tim))
26415 				if(optNWrong==l0nWrong[i] && l0minWrong[i]==optMinWrong && optNConflActs==nConflActivities[i] && optMinIndex==l0minIndexAct[i])
26416 					tim2.append(i);
26417 			assert(tim2.count()>0);
26418 			int rnd=rng.intMRG32k3a(tim2.count());
26419 			j=tim2.at(rnd);
26420 
26421 			assert(j>=0);
26422 			timeSlot=j;
26423 			assert(roomSlots[j]!=UNALLOCATED_SPACE);
26424 			roomSlot=roomSlots[j];
26425 			realRoomsSlot=realRoomsListLevel[j];
26426 
26427 			conflActivitiesTimeSlot=conflActivities[timeSlot];
26428 		}
26429 	}
26430 
26431 	for(int i=0; i<gt.rules.nHoursPerWeek; i++){
26432 		int newtime=perm[i]; //the considered time
26433 		if(nConflActivities[newtime]>=MAX_ACTIVITIES)
26434 			break;
26435 
26436 		assert(c.times[ai]==UNALLOCATED_TIME || (fixedTimeActivity[ai]&&!fixedSpaceActivity[ai]));
26437 
26438 		//no conflicting activities for this timeslot - place the activity and return
26439 		if(nConflActivities[newtime]==0){
26440 			assert(c.times[ai]==UNALLOCATED_TIME || (fixedTimeActivity[ai]&&!fixedSpaceActivity[ai]));
26441 
26442 			if(c.times[ai]!=UNALLOCATED_TIME && fixedTimeActivity[ai] && !fixedSpaceActivity[ai])
26443 				assert(c.times[ai]==newtime);
26444 
26445 			assert(conflActivities[newtime].count()==0);
26446 
26447 			restoreActIndex[nRestore]=ai;
26448 			restoreTime[nRestore]=c.times[ai];
26449 			restoreRoom[nRestore]=c.rooms[ai];
26450 			restoreRealRoomsList[nRestore]=c.realRoomsList[ai];
26451 			nRestore++;
26452 
26453 			//5.0.0-preview25
26454 			assert(swappedActivities[ai]);
26455 
26456 			moveActivity(ai, c.times[ai], newtime, c.rooms[ai], selectedRoom[newtime], c.realRoomsList[ai], realRoomsListLevel[newtime]);
26457 
26458 			foundGoodSwap=true;
26459 			return;
26460 		}
26461 		else{
26462 			if(level==level_limit-1){
26463 				foundGoodSwap=false;
26464 				break;
26465 			}
26466 
26467 			if(ncallsrandomswap>=limitcallsrandomswap){
26468 				foundGoodSwap=false;
26469 				break;
26470 			}
26471 
26472 			int ok=true;
26473 			for(int a : qAsConst(conflActivities[newtime])){
26474 				if(swappedActivities[a]){
26475 					assert(0);
26476 					ok=false;
26477 					break;
26478 				}
26479 				assert(!(fixedTimeActivity[a] && fixedSpaceActivity[a]));
26480 			}
26481 
26482 			if(!ok){
26483 				assert(0);
26484 				continue;
26485 			}
26486 
26487 			//////////////place it at a new time
26488 
26489 			int oldNRestore=nRestore;
26490 
26491 			////////////////
26492 			assert(conflActivities[newtime].count()>0);
26493 
26494 			for(int a : qAsConst(conflActivities[newtime])){
26495 				restoreActIndex[nRestore]=a;
26496 				restoreTime[nRestore]=c.times[a];
26497 				restoreRoom[nRestore]=c.rooms[a];
26498 				restoreRealRoomsList[nRestore]=c.realRoomsList[a];
26499 				nRestore++;
26500 
26501 				assert(c.times[a]!=UNALLOCATED_TIME);
26502 				assert(c.rooms[a]!=UNALLOCATED_SPACE);
26503 				int nt=UNALLOCATED_TIME;
26504 				if(fixedTimeActivity[a] && !fixedSpaceActivity[a])
26505 					nt=c.times[a];
26506 				moveActivity(a, c.times[a], nt, c.rooms[a], UNALLOCATED_SPACE, c.realRoomsList[a], QList<int>());
26507 			}
26508 			////////////////
26509 
26510 			restoreActIndex[nRestore]=ai;
26511 			restoreTime[nRestore]=c.times[ai];
26512 			restoreRoom[nRestore]=c.rooms[ai];
26513 			restoreRealRoomsList[nRestore]=c.realRoomsList[ai];
26514 			nRestore++;
26515 
26516 			moveActivity(ai, c.times[ai], newtime, c.rooms[ai], selectedRoom[newtime], c.realRoomsList[ai], realRoomsListLevel[newtime]);
26517 			//////////////////
26518 
26519 			for(int a : qAsConst(conflActivities[newtime]))
26520 				swappedActivities[a]=true;
26521 
26522 			foundGoodSwap=false;
26523 
26524 			ok=false;
26525 
26526 			assert(conflActivities[newtime].count()>0);
26527 			ok=true;
26528 
26529 			for(int a : qAsConst(conflActivities[newtime])){
26530 				randomSwap(a, level+1);
26531 				if(!foundGoodSwap){
26532 					ok=false;
26533 					break;
26534 				}
26535 				assert(c.times[a]!=UNALLOCATED_TIME);
26536 				assert(foundGoodSwap);
26537 				foundGoodSwap=false;
26538 			}
26539 
26540 			if(ok){
26541 				for(int a : qAsConst(conflActivities[newtime]))
26542 					assert(c.times[a]!=UNALLOCATED_TIME);
26543 				assert(c.times[ai]!=UNALLOCATED_TIME);
26544 
26545 				foundGoodSwap=true;
26546 				return;
26547 			}
26548 
26549 			//////////////restore times from the restore list
26550 			for(int j=nRestore-1; j>=oldNRestore; j--){
26551 				int aii=restoreActIndex[j];
26552 
26553 				if(aii!=ai)
26554 					swappedActivities[aii]=false;
26555 				else
26556 					assert(swappedActivities[aii]);
26557 
26558 				moveActivity(aii, c.times[aii], restoreTime[j], c.rooms[aii], restoreRoom[j], c.realRoomsList[aii], restoreRealRoomsList[j]);
26559 			}
26560 			nRestore=oldNRestore;
26561 
26562 			//////////////////////////////
26563 			for(int a : qAsConst(conflActivities[newtime])){
26564 				assert(c.times[a]!=UNALLOCATED_TIME);
26565 				assert(c.rooms[a]!=UNALLOCATED_SPACE);
26566 				//nothing to assert for realRoomsList
26567 				assert(!swappedActivities[a]);
26568 				assert(!(fixedTimeActivity[a] && fixedSpaceActivity[a]));
26569 			}
26570 			//////////////////////////////
26571 
26572 			assert(!foundGoodSwap);
26573 
26574 			if(level>=5) //7 might also be used? This is a value found practically.
26575 				return;
26576 		}
26577 	}
26578 }
26579