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(>.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