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, best, idx, 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, current, idx, 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 bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[idx]); 246 for (int x = 0; x < idx; x++) { 247 if (best[x] != null && best[x].getAssignments() != null) 248 bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[idx]); 249 } 250 } 251 if (current[idx] != null && current[idx].getAssignments() != null) { 252 currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[idx]); 253 for (int x = 0; x < idx; x++) { 254 if (current[x] != null && current[x].getAssignments() != null) 255 currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[x], 256 current[idx]); 257 } 258 } 259 } 260 if (currentDistanceConf < bestDistanceConf) 261 return -1; 262 if (bestDistanceConf < currentDistanceConf) 263 return 1; 264 } 265 266 // 6. avoid no-time sections 267 int bestNoTime = 0, currentNoTime = 0; 268 for (int idx = 0; idx < current.length; idx++) { 269 if (best[idx] != null && best[idx].getAssignments() != null) { 270 for (Section section : best[idx].getSections()) 271 if (section.getTime() == null) 272 bestNoTime++; 273 } 274 if (current[idx] != null && current[idx].getAssignments() != null) { 275 for (Section section : current[idx].getSections()) 276 if (section.getTime() == null) 277 currentNoTime++; 278 } 279 } 280 if (currentNoTime < bestNoTime) 281 return -1; 282 if (bestNoTime < currentNoTime) 283 return 1; 284 285 // 7. balance sections 286 double bestUnavailableSize = 0.0, currentUnavailableSize = 0.0; 287 int bestAltSectionsWithLimit = 0, currentAltSectionsWithLimit = 0; 288 for (int idx = 0; idx < current.length; idx++) { 289 if (best[idx] != null && best[idx].getAssignments() != null) { 290 for (Section section : best[idx].getSections()) { 291 Subpart subpart = section.getSubpart(); 292 // skip unlimited and single section subparts 293 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 294 continue; 295 // average size 296 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 297 // section is below average 298 if (section.getLimit() < averageSize) 299 bestUnavailableSize += (averageSize - section.getLimit()) / averageSize; 300 bestAltSectionsWithLimit++; 301 } 302 } 303 if (current[idx] != null && current[idx].getAssignments() != null) { 304 for (Section section : current[idx].getSections()) { 305 Subpart subpart = section.getSubpart(); 306 // skip unlimited and single section subparts 307 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 308 continue; 309 // average size 310 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 311 // section is below average 312 if (section.getLimit() < averageSize) 313 currentUnavailableSize += (averageSize - section.getLimit()) / averageSize; 314 currentAltSectionsWithLimit++; 315 } 316 } 317 } 318 double bestUnavailableSizeFraction = (bestUnavailableSize > 0 ? bestUnavailableSize / bestAltSectionsWithLimit 319 : 0.0); 320 double currentUnavailableSizeFraction = (currentUnavailableSize > 0 ? currentUnavailableSize 321 / currentAltSectionsWithLimit : 0.0); 322 if (currentUnavailableSizeFraction < bestUnavailableSizeFraction) 323 return -1; 324 if (bestUnavailableSizeFraction < currentUnavailableSizeFraction) 325 return 1; 326 327 // 8. average penalty sections 328 double bestPenalty = 0.0, currentPenalty = 0.0; 329 for (int idx = 0; idx < current.length; idx++) { 330 if (best[idx] != null && best[idx].getAssignments() != null) { 331 for (Section section : best[idx].getSections()) 332 bestPenalty += section.getPenalty() / best[idx].getSections().size(); 333 } 334 if (current[idx] != null && current[idx].getAssignments() != null) { 335 for (Section section : current[idx].getSections()) 336 currentPenalty += section.getPenalty() / current[idx].getSections().size(); 337 } 338 } 339 if (currentPenalty < bestPenalty) 340 return -1; 341 if (bestPenalty < currentPenalty) 342 return 1; 343 344 return 0; 345 } 346 347 @Override 348 public boolean canImprove(Assignment<Request, Enrollment> assignment, int maxIdx, Enrollment[] current, 349 Enrollment[] best) { 350 // 0. best number of assigned course requests (including alternativity & 351 // priority) 352 int currentAssignedCourseReq = 0, bestAssignedCourseReq = 0; 353 int currentAssignedRequests = 0, bestAssignedRequests = 0; 354 int currentAssignedPriority = 0, bestAssignedPriority = 0; 355 int currentAssignedAlternativity = 0, bestAssignedAlternativity = 0; 356 int alt = 0; 357 for (int idx = 0; idx < current.length; idx++) { 358 if (idx < maxIdx) { 359 if (current[idx] != null && current[idx].getAssignments() != null) { 360 currentAssignedRequests++; 361 if (current[idx].isCourseRequest()) 362 currentAssignedCourseReq++; 363 currentAssignedPriority += current[idx].getPriority() * current[idx].getPriority(); 364 currentAssignedAlternativity += (current[idx].getRequest().isAlternative() ? 1 : 0); 365 } else if (!isFreeTime(idx) && !getRequest(idx).isAlternative()) { 366 alt++; 367 } 368 } else { 369 if (!getRequest(idx).isAlternative()) { 370 currentAssignedRequests++; 371 if (!isFreeTime(idx)) 372 currentAssignedCourseReq++; 373 } else if (alt > 0) { 374 currentAssignedRequests++; 375 currentAssignedCourseReq++; 376 alt--; 377 currentAssignedAlternativity++; 378 } 379 } 380 if (best[idx] != null && best[idx].getAssignments() != null) { 381 bestAssignedRequests++; 382 if (best[idx].isCourseRequest()) 383 bestAssignedCourseReq++; 384 bestAssignedPriority += best[idx].getPriority() * best[idx].getPriority(); 385 bestAssignedAlternativity += (best[idx].getRequest().isAlternative() ? 1 : 0); 386 } 387 } 388 if (currentAssignedCourseReq > bestAssignedCourseReq) 389 return true; 390 if (bestAssignedCourseReq > currentAssignedCourseReq) 391 return false; 392 if (currentAssignedPriority < bestAssignedPriority) 393 return true; 394 if (bestAssignedPriority < currentAssignedPriority) 395 return false; 396 if (currentAssignedAlternativity < bestAssignedAlternativity) 397 return true; 398 if (bestAssignedAlternativity < currentAssignedAlternativity) 399 return false; 400 401 // 0.1. allowed, but not available sections 402 int notAvailable = 0; 403 for (int idx = 0; idx < current.length; idx++) { 404 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].getRequest() instanceof CourseRequest && best[idx].getReservation() != null && best[idx].getReservation().canAssignOverLimit()) { 405 for (Section section: best[idx].getSections()) { 406 if (section.getLimit() == 0) 407 notAvailable ++; 408 } 409 } 410 if (idx < maxIdx && current[idx] != null && current[idx].getAssignments() != null && current[idx].getRequest() instanceof CourseRequest && current[idx].getReservation() != null && current[idx].getReservation().canAssignOverLimit()) { 411 for (Section section: current[idx].getSections()) { 412 if (section.getLimit() == 0) 413 notAvailable --; 414 } 415 } 416 } 417 if (notAvailable > 0) { 418 return true; 419 } 420 421 // 0.5. avoid course time overlaps & unavailabilities 422 if (getModel().getTimeOverlaps() != null) { 423 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 424 for (int idx = 0; idx < current.length; idx++) { 425 if (best[idx] != null && best[idx].getRequest() instanceof CourseRequest) { 426 for (int x = 0; x < idx; x++) { 427 if (best[x] != null && best[x].getRequest() instanceof CourseRequest) 428 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 429 } 430 } 431 if (current[idx] != null && idx < maxIdx && current[idx].getRequest() instanceof CourseRequest) { 432 for (int x = 0; x < idx; x++) { 433 if (current[x] != null && current[x].getRequest() instanceof CourseRequest) 434 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 435 } 436 } 437 } 438 for (int idx = 0; idx < current.length; idx++) { 439 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 440 bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]); 441 } 442 if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 443 currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]); 444 } 445 } 446 if (currentTimeOverlaps < bestTimeOverlaps) 447 return true; 448 if (bestTimeOverlaps < currentTimeOverlaps) 449 return false; 450 } 451 452 // 1. maximize number of penalties 453 double bestPenalties = 0, currentPenalties = 0; 454 for (int idx = 0; idx < current.length; idx++) { 455 if (best[idx] != null) { 456 for (Section section : best[idx].getSections()) 457 bestPenalties += getModel().getOverExpected(assignment, best, idx, section, best[idx].getRequest()); 458 } 459 if (current[idx] != null && idx < maxIdx) { 460 for (Section section : current[idx].getSections()) 461 currentPenalties += getModel().getOverExpected(assignment, current, idx, section, current[idx].getRequest()); 462 } 463 } 464 if (currentPenalties < bestPenalties) 465 return true; 466 if (bestPenalties < currentPenalties) 467 return false; 468 469 // 2. best number of assigned requests (including free time requests) 470 if (currentAssignedRequests > bestAssignedRequests) 471 return true; 472 if (bestAssignedRequests > currentAssignedRequests) 473 return false; 474 475 // 3. maximize selection 476 int bestSelected = 0, currentSelected = 0; 477 for (int idx = 0; idx < current.length; idx++) { 478 if (best[idx] != null && best[idx].isCourseRequest()) { 479 Set<Section> preferred = getPreferredSections(best[idx].getRequest()); 480 if (preferred != null && !preferred.isEmpty()) { 481 for (Section section : best[idx].getSections()) 482 if (preferred.contains(section)) { 483 if (idx < maxIdx) 484 bestSelected++; 485 } else if (idx >= maxIdx) 486 bestSelected--; 487 } 488 } 489 if (current[idx] != null && idx < maxIdx && current[idx].isCourseRequest()) { 490 Set<Section> preferred = getPreferredSections(current[idx].getRequest()); 491 if (preferred != null && !preferred.isEmpty()) { 492 for (Section section : current[idx].getSections()) 493 if (preferred.contains(section)) 494 currentSelected++; 495 } 496 } 497 } 498 if (currentSelected > bestSelected) 499 return true; 500 if (bestSelected > currentSelected) 501 return false; 502 503 // 3.5 maximize preferences 504 double bestSelectedConfigs = 0, currentSelectedConfigs = 0; 505 double bestSelectedSections = 0, currentSelectedSections = 0; 506 for (int idx = 0; idx < current.length; idx++) { 507 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 508 bestSelectedSections += best[idx].percentSelectedSameSection(); 509 bestSelectedConfigs += best[idx].percentSelectedSameConfig(); 510 if (idx >= maxIdx) { 511 bestSelectedSections -= 1.0; 512 bestSelectedConfigs -= 1.0; 513 } 514 } 515 if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 516 currentSelectedSections += current[idx].percentSelectedSameSection(); 517 currentSelectedConfigs += current[idx].percentSelectedSameConfig(); 518 } 519 } 520 if (0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections > 0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections) return true; 521 if (0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections > 0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections) return false; 522 523 // 4. avoid time overlaps 524 if (getModel().getTimeOverlaps() != null) { 525 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 526 for (int idx = 0; idx < current.length; idx++) { 527 if (best[idx] != null) { 528 for (int x = 0; x < idx; x++) { 529 if (best[x] != null) 530 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 531 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 532 bestTimeOverlaps += getModel().getTimeOverlaps() 533 .nrConflicts( 534 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 535 best[idx]); 536 } 537 } 538 if (current[idx] != null && idx < maxIdx) { 539 for (int x = 0; x < idx; x++) { 540 if (current[x] != null) 541 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 542 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 543 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts( 544 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 545 current[idx]); 546 } 547 } 548 } 549 for (int idx = 0; idx < current.length; idx++) { 550 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 551 bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]); 552 } 553 if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 554 currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]); 555 } 556 } 557 if (currentTimeOverlaps < bestTimeOverlaps) 558 return true; 559 if (bestTimeOverlaps < currentTimeOverlaps) 560 return false; 561 } 562 563 // 5. avoid distance conflicts 564 if (getModel().getDistanceConflict() != null) { 565 int bestDistanceConf = 0, currentDistanceConf = 0; 566 for (int idx = 0; idx < current.length; idx++) { 567 if (best[idx] != null) { 568 bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[idx]); 569 for (int x = 0; x < idx; x++) { 570 if (best[x] != null) 571 bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[idx]); 572 } 573 } 574 if (current[idx] != null && idx < maxIdx) { 575 currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[idx]); 576 for (int x = 0; x < idx; x++) { 577 if (current[x] != null) 578 currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[x], 579 current[idx]); 580 } 581 } 582 } 583 if (currentDistanceConf < bestDistanceConf) 584 return true; 585 if (bestDistanceConf < currentDistanceConf) 586 return false; 587 } 588 589 // 6. avoid no-time sections 590 int bestNoTime = 0, currentNoTime = 0; 591 for (int idx = 0; idx < current.length; idx++) { 592 if (best[idx] != null) { 593 for (Section section : best[idx].getSections()) 594 if (section.getTime() == null) 595 bestNoTime++; 596 } 597 if (current[idx] != null && idx < maxIdx) { 598 for (Section section : current[idx].getSections()) 599 if (section.getTime() == null) 600 currentNoTime++; 601 } 602 } 603 if (currentNoTime < bestNoTime) 604 return true; 605 if (bestNoTime < currentNoTime) 606 return false; 607 608 // 7. balance sections 609 double bestUnavailableSize = 0.0, currentUnavailableSize = 0.0; 610 int bestAltSectionsWithLimit = 0, currentAltSectionsWithLimit = 0; 611 for (int idx = 0; idx < current.length; idx++) { 612 if (best[idx] != null) { 613 for (Section section : best[idx].getSections()) { 614 Subpart subpart = section.getSubpart(); 615 // skip unlimited and single section subparts 616 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 617 continue; 618 // average size 619 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 620 // section is below average 621 if (section.getLimit() < averageSize) 622 bestUnavailableSize += (averageSize - section.getLimit()) / averageSize; 623 bestAltSectionsWithLimit++; 624 } 625 } 626 if (current[idx] != null && idx < maxIdx) { 627 for (Section section : current[idx].getSections()) { 628 Subpart subpart = section.getSubpart(); 629 // skip unlimited and single section subparts 630 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 631 continue; 632 // average size 633 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 634 // section is below average 635 if (section.getLimit() < averageSize) 636 currentUnavailableSize += (averageSize - section.getLimit()) / averageSize; 637 currentAltSectionsWithLimit++; 638 } 639 } 640 } 641 double bestUnavailableSizeFraction = (bestUnavailableSize > 0 ? bestUnavailableSize / bestAltSectionsWithLimit 642 : 0.0); 643 double currentUnavailableSizeFraction = (currentUnavailableSize > 0 ? currentUnavailableSize 644 / currentAltSectionsWithLimit : 0.0); 645 if (currentUnavailableSizeFraction < bestUnavailableSizeFraction) 646 return true; 647 if (bestUnavailableSizeFraction < currentUnavailableSizeFraction) 648 return false; 649 650 // 8. average penalty sections 651 double bestPenalty = 0.0, currentPenalty = 0.0; 652 for (int idx = 0; idx < current.length; idx++) { 653 if (best[idx] != null) { 654 for (Section section : best[idx].getSections()) 655 bestPenalty += section.getPenalty() / best[idx].getSections().size(); 656 if (idx >= maxIdx && best[idx].isCourseRequest()) 657 bestPenalty -= ((CourseRequest) best[idx].getRequest()).getMinPenalty(); 658 } 659 if (current[idx] != null && idx < maxIdx) { 660 for (Section section : current[idx].getSections()) 661 currentPenalty += section.getPenalty() / current[idx].getSections().size(); 662 } 663 } 664 if (currentPenalty < bestPenalty) 665 return true; 666 if (bestPenalty < currentPenalty) 667 return false; 668 669 return true; 670 } 671}