1 //
2 //
3 // Description: This file is part of FET
4 //
5 //
6 // Author: Lalescu Liviu <Please see https://lalescu.ro/liviu/ for details about contacting Liviu Lalescu (in particular, you can find here the e-mail address)>
7 // Copyright (C) 2003 Liviu Lalescu <https://lalescu.ro/liviu/>
8 //
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software: you can redistribute it and/or modify  *
12  *   it under the terms of the GNU Affero General Public License as        *
13  *   published by the Free Software Foundation, either version 3 of the    *
14  *   License, or (at your option) any later version.                       *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "removeredundantform.h"
19 
20 #include "timetable.h"
21 
22 extern Timetable gt;
23 
24 #include <QHash>
25 #include <QMultiHash>
26 #include <QList>
27 #include <QQueue>
28 
29 #include <QMessageBox>
30 
31 #include <QPushButton>
32 #include <QCheckBox>
33 #include <QPlainTextEdit>
34 #include <QLineEdit>
35 #include <QHBoxLayout>
36 #include <QVBoxLayout>
37 
38 #include <algorithm>
39 //using namespace std;
40 
RemoveRedundantForm(QWidget * parent)41 RemoveRedundantForm::RemoveRedundantForm(QWidget* parent): QDialog(parent)
42 {
43 	setupUi(this);
44 
45 	centerWidgetOnScreen(this);
46 	restoreFETDialogGeometry(this);
47 
48 	okPushButton->setDefault(true);
49 
50 	connect(okPushButton, SIGNAL(clicked()), this, SLOT(wasAccepted()));
51 	connect(cancelPushButton, SIGNAL(clicked()), this, SLOT(wasCanceled()));
52 }
53 
~RemoveRedundantForm()54 RemoveRedundantForm::~RemoveRedundantForm()
55 {
56 	saveFETDialogGeometry(this);
57 }
58 
wasAccepted()59 void RemoveRedundantForm::wasAccepted()
60 {
61 	QMultiHash<int, int> adjMatrix;
62 
63 	for(TimeConstraint* tc : qAsConst(gt.rules.timeConstraintsList)){
64 		if(tc->weightPercentage==100.0){
65 			if(tc->type==CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME){
66 				ConstraintActivitiesSameStartingTime* cst=(ConstraintActivitiesSameStartingTime*) tc;
67 
68 				for(int i=1; i<cst->n_activities; i++){
69 					adjMatrix.insert(cst->activitiesId[0], cst->activitiesId[i]);
70 					adjMatrix.insert(cst->activitiesId[i], cst->activitiesId[0]);
71 				}
72 			}
73 			else if(tc->type==CONSTRAINT_ACTIVITIES_SAME_STARTING_DAY){
74 				ConstraintActivitiesSameStartingDay* csd=(ConstraintActivitiesSameStartingDay*) tc;
75 
76 				for(int i=1; i<csd->n_activities; i++){
77 					adjMatrix.insert(csd->activitiesId[0], csd->activitiesId[i]);
78 					adjMatrix.insert(csd->activitiesId[i], csd->activitiesId[0]);
79 				}
80 			}
81 		}
82 	}
83 
84 	QHash<int, int> repr;
85 
86 	QQueue<int> queue;
87 
88 	for(Activity* act : qAsConst(gt.rules.activitiesList)){
89 		int start=act->id;
90 
91 		if(repr.value(start, -1)==-1){ //not visited
92 			repr.insert(start, start);
93 			queue.enqueue(start);
94 			while(!queue.isEmpty()){
95 				int crtHead=queue.dequeue();
96 				assert(repr.value(crtHead, -1)==start);
97 				QList<int> neighList=adjMatrix.values(crtHead);
98 				for(int neigh : qAsConst(neighList)){
99 					if(repr.value(neigh, -1)==-1){
100 						queue.enqueue(neigh);
101 						repr.insert(neigh, start);
102 					}
103 					else{
104 						assert(repr.value(neigh, -1)==start);
105 					}
106 				}
107 			}
108 		}
109 	}
110 
111 	QList<ConstraintMinDaysBetweenActivities*> mdcList;
112 
113 	for(TimeConstraint* tc : qAsConst(gt.rules.timeConstraintsList)){
114 		if(tc->type==CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES){
115 			mdcList.append((ConstraintMinDaysBetweenActivities*)tc);
116 		}
117 	}
118 	std::reverse(mdcList.begin(), mdcList.end()); //inverse order, so earlier constraints are not removed (remove the older ones)
119 
120 	QList<ConstraintMinDaysBetweenActivities*> toBeRemovedList;
121 
122 	for(int i=0; i<mdcList.count()-1; i++){
123 		ConstraintMinDaysBetweenActivities* c1=mdcList.at(i);
124 
125 		QList<int> a1List;
126 		for(int k=0; k<c1->n_activities; k++){
127 			int m=repr.value(c1->activitiesId[k], -1);
128 			assert(m>0);
129 			a1List.append(m);
130 		}
131 
132 		//qSort(a1List);
133 		std::stable_sort(a1List.begin(), a1List.end());
134 
135 		for(int j=i+1; j<mdcList.count(); j++){
136 			ConstraintMinDaysBetweenActivities* c2=mdcList.at(j);
137 
138 			QList<int> a2List;
139 			for(int k=0; k<c2->n_activities; k++){
140 				int m=repr.value(c2->activitiesId[k], -1);
141 				assert(m>0);
142 				a2List.append(m);
143 			}
144 
145 			//qSort(a2List);
146 			std::stable_sort(a2List.begin(), a2List.end());
147 
148 			bool equal=true;
149 
150 			if(a1List.count()!=a2List.count())
151 				equal=false;
152 			if(a1List!=a2List)
153 				equal=false;
154 			if(c1->minDays!=c2->minDays)
155 				equal=false;
156 
157 			//if(c1->weightPercentage!=c2->weightPercentage)
158 			//	equal=false;
159 			//if(c1->consecutiveIfSameDay!=c2->consecutiveIfSameDay)
160 			//	equal=false;
161 			if(c1->weightPercentage > c2->weightPercentage){
162 				if(!c1->consecutiveIfSameDay && c2->consecutiveIfSameDay){
163 					equal=false;
164 				}
165 			}
166 			else if(c1->weightPercentage < c2->weightPercentage){
167 				if(c1->consecutiveIfSameDay && !c2->consecutiveIfSameDay){
168 					equal=false;
169 				}
170 			}
171 			else{
172 				//
173 			}
174 
175 			if(equal){
176 				if(c1->weightPercentage > c2->weightPercentage){
177 					ConstraintMinDaysBetweenActivities* tmp;
178 					tmp=mdcList[i];
179 					mdcList[i]=mdcList[j];
180 					mdcList[j]=tmp;
181 
182 					c1=mdcList.at(i);
183 					c2=mdcList.at(j);
184 				}
185 				if(c1->weightPercentage==c2->weightPercentage && c1->consecutiveIfSameDay && !c2->consecutiveIfSameDay){
186 					ConstraintMinDaysBetweenActivities* tmp;
187 					tmp=mdcList[i];
188 					mdcList[i]=mdcList[j];
189 					mdcList[j]=tmp;
190 
191 					c1=mdcList.at(i);
192 					c2=mdcList.at(j);
193 				}
194 
195 				assert( ! (c1->consecutiveIfSameDay && !c2->consecutiveIfSameDay) );
196 				assert(c1->weightPercentage <= c2->weightPercentage);
197 
198 				int kk=0;
199 				for(kk=0; kk<toBeRemovedList.count(); kk++)
200 					if(toBeRemovedList.at(kk)->activitiesId[0] >= c1->activitiesId[0])
201 						break;
202 				toBeRemovedList.insert(kk, c1);
203 
204 				//toBeRemovedList.prepend(c1);
205 				break;
206 			}
207 		}
208 	}
209 
210 	///////////
211 	QDialog dialog(this);
212 	dialog.setWindowTitle(tr("Last confirmation needed"));
213 
214 	QVBoxLayout* top=new QVBoxLayout(&dialog);
215 	QLabel* topLabel=new QLabel();
216 	topLabel->setText(tr("Operations that will be done:"));
217 	top->addWidget(topLabel);
218 
219 	QPushButton* acceptPB=new QPushButton(tr("Accept"));
220 	QPushButton* cancelPB=new QPushButton(tr("Cancel"));
221 	QHBoxLayout* hl=new QHBoxLayout();
222 	hl->addStretch();
223 	hl->addWidget(acceptPB);
224 	hl->addWidget(cancelPB);
225 
226 	QObject::connect(acceptPB, SIGNAL(clicked()), &dialog, SLOT(accept()));
227 	QObject::connect(cancelPB, SIGNAL(clicked()), &dialog, SLOT(reject()));
228 
229 	QPlainTextEdit* removedText=new QPlainTextEdit();
230 
231 	QString s=tr("The following time constraints will be inactivated (their weight will be made 0%):");
232 	s+="\n\n";
233 	for(ConstraintMinDaysBetweenActivities* ctr : qAsConst(toBeRemovedList)){
234 		if(ctr->weightPercentage>0.0){
235 			s+=ctr->getDetailedDescription(gt.rules);
236 			s+="\n";
237 			s+=tr("will be inactivated, by making its weight 0%");
238 			s+="\n\n";
239 			s+="---------------------------------";
240 			s+="\n\n";
241 		}
242 	}
243 
244 	removedText->setPlainText(s);
245 	removedText->setReadOnly(true);
246 
247 	top->addWidget(removedText);
248 
249 	top->addLayout(hl);
250 
251 	const QString settingsName=QString("RemoveRedundantConstraintsLastConfirmationForm");
252 
253 	dialog.resize(600, 400);
254 	centerWidgetOnScreen(&dialog);
255 	restoreFETDialogGeometry(&dialog, settingsName);
256 
257 	acceptPB->setFocus();
258 	acceptPB->setDefault(true);
259 
260 	setParentAndOtherThings(&dialog, this);
261 	int res=dialog.exec();
262 	saveFETDialogGeometry(&dialog, settingsName);
263 
264 	if(res==QDialog::Rejected){
265 		toBeRemovedList.clear();
266 
267 		return;
268 	}
269 
270 	assert(res==QDialog::Accepted);
271 
272 	gt.rules.internalStructureComputed=false;
273 	setRulesModifiedAndOtherThings(&gt.rules);
274 
275 	for(ConstraintMinDaysBetweenActivities* mdc : qAsConst(toBeRemovedList))
276 		mdc->weightPercentage=0.0;
277 
278 	toBeRemovedList.clear();
279 
280 	this->accept();
281 }
282 
wasCanceled()283 void RemoveRedundantForm::wasCanceled()
284 {
285 	this->reject();
286 }
287 
on_removeRedundantCheckBox_toggled()288 void RemoveRedundantForm::on_removeRedundantCheckBox_toggled()
289 {
290 	int k=removeRedundantCheckBox->isChecked();
291 	if(!k){
292 		removeRedundantCheckBox->setChecked(true);
293 		QMessageBox::information(this, tr("FET information"), tr("This box must remain checked, so that you can remove"
294 		 " redundant constraints of type min days between activities"));
295 	}
296 }
297