001package org.cpsolver.studentsct.online.selection; 002 003import java.util.Hashtable; 004import java.util.Set; 005 006import org.cpsolver.ifs.assignment.Assignment; 007import org.cpsolver.studentsct.model.CourseRequest; 008import org.cpsolver.studentsct.model.Enrollment; 009import org.cpsolver.studentsct.model.FreeTimeRequest; 010import org.cpsolver.studentsct.model.Request; 011import org.cpsolver.studentsct.model.Section; 012import org.cpsolver.studentsct.model.Student; 013import org.cpsolver.studentsct.model.Subpart; 014import org.cpsolver.studentsct.online.OnlineSectioningModel; 015 016/** 017* Equal weighting multi-criteria selection criterion. Much like the {@link OnlineSectioningCriterion}, but 018* course request priorities are ignored. Most complete solution is preferred instead. 019* 020* @version StudentSct 1.3 (Student Sectioning)<br> 021* Copyright (C) 2014 Tomas Muller<br> 022* <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 023* <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 024* <br> 025* This library is free software; you can redistribute it and/or modify 026* it under the terms of the GNU Lesser General Public License as 027* published by the Free Software Foundation; either version 3 of the 028* License, or (at your option) any later version. <br> 029* <br> 030* This library is distributed in the hope that it will be useful, but 031* WITHOUT ANY WARRANTY; without even the implied warranty of 032* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 033* Lesser General Public License for more details. <br> 034* <br> 035* You should have received a copy of the GNU Lesser General Public 036* License along with this library; if not see <a 037* href='http://www.gnu.org/licenses'>http://www.gnu.org/licenses</a>. 038* 039*/ 040public class EqualWeightCriterion extends OnlineSectioningCriterion { 041 042 public EqualWeightCriterion(Student student, OnlineSectioningModel model, 043 Assignment<Request, Enrollment> assignment, Hashtable<CourseRequest, Set<Section>> preferredSections) { 044 super(student, model, assignment, preferredSections); 045 } 046 047 @Override 048 public int compare(Assignment<Request, Enrollment> assignment, Enrollment[] current, Enrollment[] best) { 049 if (best == null) 050 return -1; 051 052 // 0. best number of assigned course requests (including alternativity & 053 // priority) 054 int currentAssignedCourseReq = 0, bestAssignedCourseReq = 0; 055 int currentAssignedRequests = 0, bestAssignedRequests = 0; 056 int currentAssignedPriority = 0, bestAssignedPriority = 0; 057 int currentAssignedAlternativity = 0, bestAssignedAlternativity = 0; 058 for (int idx = 0; idx < current.length; idx++) { 059 if (current[idx] != null && current[idx].getAssignments() != null) { 060 currentAssignedRequests++; 061 if (current[idx].isCourseRequest()) 062 currentAssignedCourseReq++; 063 currentAssignedPriority += current[idx].getPriority() * current[idx].getPriority(); 064 currentAssignedAlternativity += (current[idx].getRequest().isAlternative() ? 1 : 0); 065 } 066 if (best[idx] != null && best[idx].getAssignments() != null) { 067 bestAssignedRequests++; 068 if (best[idx].isCourseRequest()) 069 bestAssignedCourseReq++; 070 bestAssignedPriority += best[idx].getPriority() * best[idx].getPriority(); 071 bestAssignedAlternativity += (best[idx].getRequest().isAlternative() ? 1 : 0); 072 } 073 } 074 if (currentAssignedCourseReq > bestAssignedCourseReq) 075 return -1; 076 if (bestAssignedCourseReq > currentAssignedCourseReq) 077 return 1; 078 if (currentAssignedPriority < bestAssignedPriority) 079 return -1; 080 if (bestAssignedPriority < currentAssignedPriority) 081 return 1; 082 if (currentAssignedAlternativity < bestAssignedAlternativity) 083 return -1; 084 if (bestAssignedAlternativity < currentAssignedAlternativity) 085 return 1; 086 087 // 0.1. allowed, but not available sections 088 int bestNotAvailable = 0, currentNotAvailable = 0; 089 for (int idx = 0; idx < current.length; idx++) { 090 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].getRequest() instanceof CourseRequest && best[idx].getReservation() != null && best[idx].getReservation().canAssignOverLimit()) { 091 for (Section section: best[idx].getSections()) { 092 if (section.getLimit() == 0) 093 bestNotAvailable ++; 094 } 095 } 096 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].getRequest() instanceof CourseRequest && current[idx].getReservation() != null && current[idx].getReservation().canAssignOverLimit()) { 097 for (Section section: current[idx].getSections()) { 098 if (section.getLimit() == 0) 099 currentNotAvailable ++; 100 } 101 } 102 } 103 if (bestNotAvailable > currentNotAvailable) return -1; 104 if (bestNotAvailable < currentNotAvailable) return 1; 105 106 // 0.5. avoid course overlaps & unavailabilities 107 if (getModel().getTimeOverlaps() != null) { 108 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 109 for (int idx = 0; idx < current.length; idx++) { 110 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].getRequest() instanceof CourseRequest) { 111 for (int x = 0; x < idx; x++) { 112 if (best[x] != null && best[x].getAssignments() != null 113 && best[x].getRequest() instanceof CourseRequest) 114 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 115 } 116 } 117 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].getRequest() instanceof CourseRequest) { 118 for (int x = 0; x < idx; x++) { 119 if (current[x] != null && current[x].getAssignments() != null 120 && current[x].getRequest() instanceof CourseRequest) 121 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 122 } 123 } 124 } 125 for (int idx = 0; idx < current.length; idx++) { 126 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 127 bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]); 128 } 129 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 130 currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]); 131 } 132 } 133 if (currentTimeOverlaps < bestTimeOverlaps) 134 return -1; 135 if (bestTimeOverlaps < currentTimeOverlaps) 136 return 1; 137 } 138 139 // 1. minimize number of penalties 140 double bestPenalties = 0, currentPenalties = 0; 141 for (int idx = 0; idx < current.length; idx++) { 142 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 143 for (Section section : best[idx].getSections()) 144 bestPenalties += getModel().getOverExpected(assignment, section, best[idx].getRequest()); 145 } 146 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 147 for (Section section : current[idx].getSections()) 148 currentPenalties += getModel().getOverExpected(assignment, section, current[idx].getRequest()); 149 } 150 } 151 if (currentPenalties < bestPenalties) 152 return -1; 153 if (bestPenalties < currentPenalties) 154 return 1; 155 156 // 2. best number of assigned requests (including free time requests) 157 if (currentAssignedRequests > bestAssignedRequests) 158 return -1; 159 if (bestAssignedRequests > currentAssignedRequests) 160 return 1; 161 162 // 3. maximize selection 163 int bestSelected = 0, currentSelected = 0; 164 for (int idx = 0; idx < current.length; idx++) { 165 Set<Section> preferred = getPreferredSections(getRequest(idx)); 166 if (preferred != null && !preferred.isEmpty()) { 167 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 168 for (Section section : best[idx].getSections()) 169 if (preferred.contains(section)) 170 bestSelected++; 171 } 172 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 173 for (Section section : current[idx].getSections()) 174 if (preferred.contains(section)) 175 currentSelected++; 176 } 177 } 178 } 179 if (currentSelected > bestSelected) 180 return -1; 181 if (bestSelected > currentSelected) 182 return 1; 183 184 // 3.5 maximize preferences 185 double bestSelectedConfigs = 0, currentSelectedConfigs = 0; 186 double bestSelectedSections = 0, currentSelectedSections = 0; 187 for (int idx = 0; idx < current.length; idx++) { 188 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 189 bestSelectedSections += best[idx].percentSelectedSameSection(); 190 bestSelectedConfigs += best[idx].percentSelectedSameConfig(); 191 } 192 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 193 currentSelectedSections += current[idx].percentSelectedSameSection(); 194 currentSelectedConfigs += current[idx].percentSelectedSameConfig(); 195 } 196 } 197 if (0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections > 0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections) return -1; 198 if (0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections > 0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections) return 1; 199 200 // 4. avoid time overlaps 201 if (getModel().getTimeOverlaps() != null) { 202 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 203 for (int idx = 0; idx < current.length; idx++) { 204 if (best[idx] != null && best[idx].getAssignments() != null) { 205 for (int x = 0; x < idx; x++) { 206 if (best[x] != null && best[x].getAssignments() != null) 207 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 208 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 209 bestTimeOverlaps += getModel().getTimeOverlaps() 210 .nrConflicts( 211 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 212 best[idx]); 213 } 214 } 215 if (current[idx] != null && current[idx].getAssignments() != null) { 216 for (int x = 0; x < idx; x++) { 217 if (current[x] != null && current[x].getAssignments() != null) 218 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 219 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 220 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts( 221 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 222 current[idx]); 223 } 224 } 225 } 226 for (int idx = 0; idx < current.length; idx++) { 227 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 228 bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]); 229 } 230 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 231 currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]); 232 } 233 } 234 if (currentTimeOverlaps < bestTimeOverlaps) 235 return -1; 236 if (bestTimeOverlaps < currentTimeOverlaps) 237 return 1; 238 } 239 240 // 5. avoid distance conflicts 241 if (getModel().getDistanceConflict() != null) { 242 int bestDistanceConf = 0, currentDistanceConf = 0; 243 for (int idx = 0; idx < current.length; idx++) { 244 if (best[idx] != null && best[idx].getAssignments() != null) { 245 for (int x = 0; x < idx; x++) { 246 if (best[x] != null && best[x].getAssignments() != null) 247 bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[idx]); 248 } 249 } 250 if (current[idx] != null && current[idx].getAssignments() != null) { 251 for (int x = 0; x < idx; x++) { 252 if (current[x] != null && current[x].getAssignments() != null) 253 currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[x], 254 current[idx]); 255 } 256 } 257 } 258 if (currentDistanceConf < bestDistanceConf) 259 return -1; 260 if (bestDistanceConf < currentDistanceConf) 261 return 1; 262 } 263 264 // 6. avoid no-time sections 265 int bestNoTime = 0, currentNoTime = 0; 266 for (int idx = 0; idx < current.length; idx++) { 267 if (best[idx] != null && best[idx].getAssignments() != null) { 268 for (Section section : best[idx].getSections()) 269 if (section.getTime() == null) 270 bestNoTime++; 271 } 272 if (current[idx] != null && current[idx].getAssignments() != null) { 273 for (Section section : current[idx].getSections()) 274 if (section.getTime() == null) 275 currentNoTime++; 276 } 277 } 278 if (currentNoTime < bestNoTime) 279 return -1; 280 if (bestNoTime < currentNoTime) 281 return 1; 282 283 // 7. balance sections 284 double bestUnavailableSize = 0.0, currentUnavailableSize = 0.0; 285 int bestAltSectionsWithLimit = 0, currentAltSectionsWithLimit = 0; 286 for (int idx = 0; idx < current.length; idx++) { 287 if (best[idx] != null && best[idx].getAssignments() != null) { 288 for (Section section : best[idx].getSections()) { 289 Subpart subpart = section.getSubpart(); 290 // skip unlimited and single section subparts 291 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 292 continue; 293 // average size 294 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 295 // section is below average 296 if (section.getLimit() < averageSize) 297 bestUnavailableSize += (averageSize - section.getLimit()) / averageSize; 298 bestAltSectionsWithLimit++; 299 } 300 } 301 if (current[idx] != null && current[idx].getAssignments() != null) { 302 for (Section section : current[idx].getSections()) { 303 Subpart subpart = section.getSubpart(); 304 // skip unlimited and single section subparts 305 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 306 continue; 307 // average size 308 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 309 // section is below average 310 if (section.getLimit() < averageSize) 311 currentUnavailableSize += (averageSize - section.getLimit()) / averageSize; 312 currentAltSectionsWithLimit++; 313 } 314 } 315 } 316 double bestUnavailableSizeFraction = (bestUnavailableSize > 0 ? bestUnavailableSize / bestAltSectionsWithLimit 317 : 0.0); 318 double currentUnavailableSizeFraction = (currentUnavailableSize > 0 ? currentUnavailableSize 319 / currentAltSectionsWithLimit : 0.0); 320 if (currentUnavailableSizeFraction < bestUnavailableSizeFraction) 321 return -1; 322 if (bestUnavailableSizeFraction < currentUnavailableSizeFraction) 323 return 1; 324 325 // 8. average penalty sections 326 double bestPenalty = 0.0, currentPenalty = 0.0; 327 for (int idx = 0; idx < current.length; idx++) { 328 if (best[idx] != null && best[idx].getAssignments() != null) { 329 for (Section section : best[idx].getSections()) 330 bestPenalty += section.getPenalty() / best[idx].getSections().size(); 331 } 332 if (current[idx] != null && current[idx].getAssignments() != null) { 333 for (Section section : current[idx].getSections()) 334 currentPenalty += section.getPenalty() / current[idx].getSections().size(); 335 } 336 } 337 if (currentPenalty < bestPenalty) 338 return -1; 339 if (bestPenalty < currentPenalty) 340 return 1; 341 342 return 0; 343 } 344 345 @Override 346 public boolean canImprove(Assignment<Request, Enrollment> assignment, int maxIdx, Enrollment[] current, 347 Enrollment[] best) { 348 // 0. best number of assigned course requests (including alternativity & 349 // priority) 350 int currentAssignedCourseReq = 0, bestAssignedCourseReq = 0; 351 int currentAssignedRequests = 0, bestAssignedRequests = 0; 352 int currentAssignedPriority = 0, bestAssignedPriority = 0; 353 int currentAssignedAlternativity = 0, bestAssignedAlternativity = 0; 354 int alt = 0; 355 for (int idx = 0; idx < current.length; idx++) { 356 if (idx < maxIdx) { 357 if (current[idx] != null && current[idx].getAssignments() != null) { 358 currentAssignedRequests++; 359 if (current[idx].isCourseRequest()) 360 currentAssignedCourseReq++; 361 currentAssignedPriority += current[idx].getPriority() * current[idx].getPriority(); 362 currentAssignedAlternativity += (current[idx].getRequest().isAlternative() ? 1 : 0); 363 } else if (!isFreeTime(idx) && !getRequest(idx).isAlternative()) { 364 alt++; 365 } 366 } else { 367 if (!getRequest(idx).isAlternative()) { 368 currentAssignedRequests++; 369 if (!isFreeTime(idx)) 370 currentAssignedCourseReq++; 371 } else if (alt > 0) { 372 currentAssignedRequests++; 373 currentAssignedCourseReq++; 374 alt--; 375 currentAssignedAlternativity++; 376 } 377 } 378 if (best[idx] != null && best[idx].getAssignments() != null) { 379 bestAssignedRequests++; 380 if (best[idx].isCourseRequest()) 381 bestAssignedCourseReq++; 382 bestAssignedPriority += best[idx].getPriority() * best[idx].getPriority(); 383 bestAssignedAlternativity += (best[idx].getRequest().isAlternative() ? 1 : 0); 384 } 385 } 386 if (currentAssignedCourseReq > bestAssignedCourseReq) 387 return true; 388 if (bestAssignedCourseReq > currentAssignedCourseReq) 389 return false; 390 if (currentAssignedPriority < bestAssignedPriority) 391 return true; 392 if (bestAssignedPriority < currentAssignedPriority) 393 return false; 394 if (currentAssignedAlternativity < bestAssignedAlternativity) 395 return true; 396 if (bestAssignedAlternativity < currentAssignedAlternativity) 397 return false; 398 399 // 0.1. allowed, but not available sections 400 int notAvailable = 0; 401 for (int idx = 0; idx < current.length; idx++) { 402 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].getRequest() instanceof CourseRequest && best[idx].getReservation() != null && best[idx].getReservation().canAssignOverLimit()) { 403 for (Section section: best[idx].getSections()) { 404 if (section.getLimit() == 0) 405 notAvailable ++; 406 } 407 } 408 if (idx < maxIdx && current[idx] != null && current[idx].getAssignments() != null && current[idx].getRequest() instanceof CourseRequest && current[idx].getReservation() != null && current[idx].getReservation().canAssignOverLimit()) { 409 for (Section section: current[idx].getSections()) { 410 if (section.getLimit() == 0) 411 notAvailable --; 412 } 413 } 414 } 415 if (notAvailable > 0) { 416 return true; 417 } 418 419 // 0.5. avoid course time overlaps & unavailabilities 420 if (getModel().getTimeOverlaps() != null) { 421 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 422 for (int idx = 0; idx < current.length; idx++) { 423 if (best[idx] != null && best[idx].getRequest() instanceof CourseRequest) { 424 for (int x = 0; x < idx; x++) { 425 if (best[x] != null && best[x].getRequest() instanceof CourseRequest) 426 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 427 } 428 } 429 if (current[idx] != null && idx < maxIdx && current[idx].getRequest() instanceof CourseRequest) { 430 for (int x = 0; x < idx; x++) { 431 if (current[x] != null && current[x].getRequest() instanceof CourseRequest) 432 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 433 } 434 } 435 } 436 for (int idx = 0; idx < current.length; idx++) { 437 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 438 bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]); 439 } 440 if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 441 currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]); 442 } 443 } 444 if (currentTimeOverlaps < bestTimeOverlaps) 445 return true; 446 if (bestTimeOverlaps < currentTimeOverlaps) 447 return false; 448 } 449 450 // 1. maximize number of penalties 451 double bestPenalties = 0, currentPenalties = 0; 452 for (int idx = 0; idx < current.length; idx++) { 453 if (best[idx] != null) { 454 for (Section section : best[idx].getSections()) 455 bestPenalties += getModel().getOverExpected(assignment, section, best[idx].getRequest()); 456 } 457 if (current[idx] != null && idx < maxIdx) { 458 for (Section section : current[idx].getSections()) 459 currentPenalties += getModel().getOverExpected(assignment, section, current[idx].getRequest()); 460 } 461 } 462 if (currentPenalties < bestPenalties) 463 return true; 464 if (bestPenalties < currentPenalties) 465 return false; 466 467 // 2. best number of assigned requests (including free time requests) 468 if (currentAssignedRequests > bestAssignedRequests) 469 return true; 470 if (bestAssignedRequests > currentAssignedRequests) 471 return false; 472 473 // 3. maximize selection 474 int bestSelected = 0, currentSelected = 0; 475 for (int idx = 0; idx < current.length; idx++) { 476 if (best[idx] != null && best[idx].isCourseRequest()) { 477 Set<Section> preferred = getPreferredSections(best[idx].getRequest()); 478 if (preferred != null && !preferred.isEmpty()) { 479 for (Section section : best[idx].getSections()) 480 if (preferred.contains(section)) { 481 if (idx < maxIdx) 482 bestSelected++; 483 } else if (idx >= maxIdx) 484 bestSelected--; 485 } 486 } 487 if (current[idx] != null && idx < maxIdx && current[idx].isCourseRequest()) { 488 Set<Section> preferred = getPreferredSections(current[idx].getRequest()); 489 if (preferred != null && !preferred.isEmpty()) { 490 for (Section section : current[idx].getSections()) 491 if (preferred.contains(section)) 492 currentSelected++; 493 } 494 } 495 } 496 if (currentSelected > bestSelected) 497 return true; 498 if (bestSelected > currentSelected) 499 return false; 500 501 // 3.5 maximize preferences 502 double bestSelectedConfigs = 0, currentSelectedConfigs = 0; 503 double bestSelectedSections = 0, currentSelectedSections = 0; 504 for (int idx = 0; idx < current.length; idx++) { 505 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 506 bestSelectedSections += best[idx].percentSelectedSameSection(); 507 bestSelectedConfigs += best[idx].percentSelectedSameConfig(); 508 if (idx >= maxIdx) { 509 bestSelectedSections -= 1.0; 510 bestSelectedConfigs -= 1.0; 511 } 512 } 513 if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 514 currentSelectedSections += current[idx].percentSelectedSameSection(); 515 currentSelectedConfigs += current[idx].percentSelectedSameConfig(); 516 } 517 } 518 if (0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections > 0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections) return true; 519 if (0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections > 0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections) return false; 520 521 // 4. avoid time overlaps 522 if (getModel().getTimeOverlaps() != null) { 523 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 524 for (int idx = 0; idx < current.length; idx++) { 525 if (best[idx] != null) { 526 for (int x = 0; x < idx; x++) { 527 if (best[x] != null) 528 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 529 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 530 bestTimeOverlaps += getModel().getTimeOverlaps() 531 .nrConflicts( 532 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 533 best[idx]); 534 } 535 } 536 if (current[idx] != null && idx < maxIdx) { 537 for (int x = 0; x < idx; x++) { 538 if (current[x] != null) 539 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 540 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 541 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts( 542 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 543 current[idx]); 544 } 545 } 546 } 547 for (int idx = 0; idx < current.length; idx++) { 548 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 549 bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]); 550 } 551 if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 552 currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]); 553 } 554 } 555 if (currentTimeOverlaps < bestTimeOverlaps) 556 return true; 557 if (bestTimeOverlaps < currentTimeOverlaps) 558 return false; 559 } 560 561 // 5. avoid distance conflicts 562 if (getModel().getDistanceConflict() != null) { 563 int bestDistanceConf = 0, currentDistanceConf = 0; 564 for (int idx = 0; idx < current.length; idx++) { 565 if (best[idx] != null) { 566 for (int x = 0; x < idx; x++) { 567 if (best[x] != null) 568 bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[idx]); 569 } 570 } 571 if (current[idx] != null && idx < maxIdx) { 572 for (int x = 0; x < idx; x++) { 573 if (current[x] != null) 574 currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[x], 575 current[idx]); 576 } 577 } 578 } 579 if (currentDistanceConf < bestDistanceConf) 580 return true; 581 if (bestDistanceConf < currentDistanceConf) 582 return false; 583 } 584 585 // 6. avoid no-time sections 586 int bestNoTime = 0, currentNoTime = 0; 587 for (int idx = 0; idx < current.length; idx++) { 588 if (best[idx] != null) { 589 for (Section section : best[idx].getSections()) 590 if (section.getTime() == null) 591 bestNoTime++; 592 } 593 if (current[idx] != null && idx < maxIdx) { 594 for (Section section : current[idx].getSections()) 595 if (section.getTime() == null) 596 currentNoTime++; 597 } 598 } 599 if (currentNoTime < bestNoTime) 600 return true; 601 if (bestNoTime < currentNoTime) 602 return false; 603 604 // 7. balance sections 605 double bestUnavailableSize = 0.0, currentUnavailableSize = 0.0; 606 int bestAltSectionsWithLimit = 0, currentAltSectionsWithLimit = 0; 607 for (int idx = 0; idx < current.length; idx++) { 608 if (best[idx] != null) { 609 for (Section section : best[idx].getSections()) { 610 Subpart subpart = section.getSubpart(); 611 // skip unlimited and single section subparts 612 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 613 continue; 614 // average size 615 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 616 // section is below average 617 if (section.getLimit() < averageSize) 618 bestUnavailableSize += (averageSize - section.getLimit()) / averageSize; 619 bestAltSectionsWithLimit++; 620 } 621 } 622 if (current[idx] != null && idx < maxIdx) { 623 for (Section section : current[idx].getSections()) { 624 Subpart subpart = section.getSubpart(); 625 // skip unlimited and single section subparts 626 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 627 continue; 628 // average size 629 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 630 // section is below average 631 if (section.getLimit() < averageSize) 632 currentUnavailableSize += (averageSize - section.getLimit()) / averageSize; 633 currentAltSectionsWithLimit++; 634 } 635 } 636 } 637 double bestUnavailableSizeFraction = (bestUnavailableSize > 0 ? bestUnavailableSize / bestAltSectionsWithLimit 638 : 0.0); 639 double currentUnavailableSizeFraction = (currentUnavailableSize > 0 ? currentUnavailableSize 640 / currentAltSectionsWithLimit : 0.0); 641 if (currentUnavailableSizeFraction < bestUnavailableSizeFraction) 642 return true; 643 if (bestUnavailableSizeFraction < currentUnavailableSizeFraction) 644 return false; 645 646 // 8. average penalty sections 647 double bestPenalty = 0.0, currentPenalty = 0.0; 648 for (int idx = 0; idx < current.length; idx++) { 649 if (best[idx] != null) { 650 for (Section section : best[idx].getSections()) 651 bestPenalty += section.getPenalty() / best[idx].getSections().size(); 652 if (idx >= maxIdx && best[idx].isCourseRequest()) 653 bestPenalty -= ((CourseRequest) best[idx].getRequest()).getMinPenalty(); 654 } 655 if (current[idx] != null && idx < maxIdx) { 656 for (Section section : current[idx].getSections()) 657 currentPenalty += section.getPenalty() / current[idx].getSections().size(); 658 } 659 } 660 if (currentPenalty < bestPenalty) 661 return true; 662 if (bestPenalty < currentPenalty) 663 return false; 664 665 return true; 666 } 667}