001package org.cpsolver.studentsct.extension; 002 003import java.util.HashMap; 004import java.util.HashSet; 005import java.util.Iterator; 006import java.util.Map; 007import java.util.Set; 008 009import org.apache.log4j.Logger; 010import org.cpsolver.coursett.Constants; 011import org.cpsolver.coursett.model.Placement; 012import org.cpsolver.coursett.model.RoomLocation; 013import org.cpsolver.coursett.model.TimeLocation; 014import org.cpsolver.ifs.assignment.Assignment; 015import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; 016import org.cpsolver.ifs.assignment.context.ExtensionWithContext; 017import org.cpsolver.ifs.model.ModelListener; 018import org.cpsolver.ifs.solver.Solver; 019import org.cpsolver.ifs.util.DataProperties; 020import org.cpsolver.ifs.util.DistanceMetric; 021import org.cpsolver.studentsct.StudentSectioningModel; 022import org.cpsolver.studentsct.StudentSectioningModel.StudentSectioningModelContext; 023import org.cpsolver.studentsct.model.CourseRequest; 024import org.cpsolver.studentsct.model.Enrollment; 025import org.cpsolver.studentsct.model.Request; 026import org.cpsolver.studentsct.model.Section; 027import org.cpsolver.studentsct.model.Student; 028 029 030/** 031 * This extension computes student distant conflicts. Two sections that are 032 * attended by the same student are considered in a distance conflict if they 033 * are back-to-back taught in locations that are two far away. This means that 034 * the (walking) distance in minutes between the two classes are longer than 035 * the break time of the earlier class. See {@link DistanceMetric} for more details. 036 * 037 * @see TimeLocation 038 * @see Placement 039 * 040 * <br> 041 * <br> 042 * 043 * @version StudentSct 1.3 (Student Sectioning)<br> 044 * Copyright (C) 2007 - 2014 Tomas Muller<br> 045 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 046 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 047 * <br> 048 * This library is free software; you can redistribute it and/or modify 049 * it under the terms of the GNU Lesser General Public License as 050 * published by the Free Software Foundation; either version 3 of the 051 * License, or (at your option) any later version. <br> 052 * <br> 053 * This library is distributed in the hope that it will be useful, but 054 * WITHOUT ANY WARRANTY; without even the implied warranty of 055 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 056 * Lesser General Public License for more details. <br> 057 * <br> 058 * You should have received a copy of the GNU Lesser General Public 059 * License along with this library; if not see 060 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 061 */ 062 063public class DistanceConflict extends ExtensionWithContext<Request, Enrollment, DistanceConflict.DistanceConflictContext> implements ModelListener<Request, Enrollment> { 064 private static Logger sLog = Logger.getLogger(DistanceConflict.class); 065 /** Debug flag */ 066 public static boolean sDebug = false; 067 private DistanceMetric iDistanceMetric = null; 068 069 /** 070 * Constructor. Beside of other thigs, this constructor also uses 071 * {@link StudentSectioningModel#setDistanceConflict(DistanceConflict)} to 072 * set the this instance to the model. 073 * 074 * @param solver 075 * constraint solver 076 * @param properties 077 * configuration 078 */ 079 public DistanceConflict(Solver<Request, Enrollment> solver, DataProperties properties) { 080 super(solver, properties); 081 if (solver != null) { 082 StudentSectioningModel model = (StudentSectioningModel) solver.currentSolution().getModel(); 083 iDistanceMetric = model.getDistanceMetric(); 084 model.setDistanceConflict(this); 085 } 086 if (iDistanceMetric == null) 087 iDistanceMetric = new DistanceMetric(properties); 088 } 089 090 /** 091 * Alternative constructor (for online student sectioning) 092 * @param metrics distance metrics 093 * @param properties configuration 094 */ 095 public DistanceConflict(DistanceMetric metrics, DataProperties properties) { 096 super(null, properties); 097 iDistanceMetric = metrics; 098 } 099 100 @Override 101 public String toString() { 102 return "DistanceConstraint"; 103 } 104 105 public DistanceMetric getDistanceMetric() { 106 return iDistanceMetric; 107 } 108 109 private Map<Long, Map<Long, Integer>> iDistanceCache = new HashMap<Long, Map<Long,Integer>>(); 110 protected synchronized int getDistanceInMinutes(RoomLocation r1, RoomLocation r2) { 111 if (r1.getId().compareTo(r2.getId()) > 0) return getDistanceInMinutes(r2, r1); 112 if (r1.getId().equals(r2.getId()) || r1.getIgnoreTooFar() || r2.getIgnoreTooFar()) 113 return 0; 114 if (r1.getPosX() == null || r1.getPosY() == null || r2.getPosX() == null || r2.getPosY() == null) 115 return iDistanceMetric.getMaxTravelDistanceInMinutes(); 116 Map<Long, Integer> other2distance = iDistanceCache.get(r1.getId()); 117 if (other2distance == null) { 118 other2distance = new HashMap<Long, Integer>(); 119 iDistanceCache.put(r1.getId(), other2distance); 120 } 121 Integer distance = other2distance.get(r2.getId()); 122 if (distance == null) { 123 distance = iDistanceMetric.getDistanceInMinutes(r1.getId(), r1.getPosX(), r1.getPosY(), r2.getId(), r2.getPosX(), r2.getPosY()); 124 other2distance.put(r2.getId(), distance); 125 } 126 return distance; 127 } 128 129 protected int getDistanceInMinutes(Placement p1, Placement p2) { 130 if (p1.isMultiRoom()) { 131 if (p2.isMultiRoom()) { 132 int dist = 0; 133 for (RoomLocation r1 : p1.getRoomLocations()) { 134 for (RoomLocation r2 : p2.getRoomLocations()) { 135 dist = Math.max(dist, getDistanceInMinutes(r1, r2)); 136 } 137 } 138 return dist; 139 } else { 140 if (p2.getRoomLocation() == null) 141 return 0; 142 int dist = 0; 143 for (RoomLocation r1 : p1.getRoomLocations()) { 144 dist = Math.max(dist, getDistanceInMinutes(r1, p2.getRoomLocation())); 145 } 146 return dist; 147 } 148 } else if (p2.isMultiRoom()) { 149 if (p1.getRoomLocation() == null) 150 return 0; 151 int dist = 0; 152 for (RoomLocation r2 : p2.getRoomLocations()) { 153 dist = Math.max(dist, getDistanceInMinutes(p1.getRoomLocation(), r2)); 154 } 155 return dist; 156 } else { 157 if (p1.getRoomLocation() == null || p2.getRoomLocation() == null) 158 return 0; 159 return getDistanceInMinutes(p1.getRoomLocation(), p2.getRoomLocation()); 160 } 161 } 162 163 /** 164 * Return true if the given two sections are in distance conflict. This 165 * means that the sections are back-to-back and that they are placed in 166 * locations that are two far. 167 * 168 * @param student a student 169 * @param s1 170 * a section 171 * @param s2 172 * a section 173 * @return true, if the given sections are in a distance conflict 174 */ 175 public boolean inConflict(Student student, Section s1, Section s2) { 176 if (s1.getPlacement() == null || s2.getPlacement() == null) 177 return false; 178 TimeLocation t1 = s1.getTime(); 179 TimeLocation t2 = s2.getTime(); 180 if (!t1.shareDays(t2) || !t1.shareWeeks(t2)) 181 return false; 182 int a1 = t1.getStartSlot(), a2 = t2.getStartSlot(); 183 if (student.isNeedShortDistances()) { 184 if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) { 185 if (a1 + t1.getNrSlotsPerMeeting() <= a2) { 186 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 187 if (dist > Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength())) 188 return true; 189 } else if (a2 + t2.getNrSlotsPerMeeting() <= a1) { 190 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 191 if (dist > Constants.SLOT_LENGTH_MIN * (a1 - a2 - t2.getLength())) 192 return true; 193 } 194 } else { 195 if (a1 + t1.getNrSlotsPerMeeting() == a2) { 196 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 197 if (dist > 0) return true; 198 } else if (a2 + t2.getNrSlotsPerMeeting() == a1) { 199 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 200 if (dist > 0) return true; 201 } 202 } 203 return false; 204 } 205 if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) { 206 if (a1 + t1.getNrSlotsPerMeeting() <= a2) { 207 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 208 if (dist > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength())) 209 return true; 210 } else if (a2 + t2.getNrSlotsPerMeeting() <= a1) { 211 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 212 if (dist > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a1 - a2 - t2.getLength())) 213 return true; 214 } 215 } else { 216 if (a1 + t1.getNrSlotsPerMeeting() == a2) { 217 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 218 if (dist > t1.getBreakTime()) 219 return true; 220 } else if (a2 + t2.getNrSlotsPerMeeting() == a1) { 221 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 222 if (dist > t2.getBreakTime()) 223 return true; 224 } 225 } 226 return false; 227 } 228 229 /** 230 * Return number of distance conflict of a (course) enrollment. It is the 231 * number of pairs of assignments of the enrollment that are in a distance 232 * conflict. 233 * 234 * @param e1 235 * an enrollment 236 * @return number of distance conflicts 237 */ 238 public int nrConflicts(Enrollment e1) { 239 if (!e1.isCourseRequest()) 240 return 0; 241 int cnt = 0; 242 for (Section s1 : e1.getSections()) { 243 for (Section s2 : e1.getSections()) { 244 if (s1.getId() < s2.getId() && inConflict(e1.getStudent(), s1, s2)) 245 cnt ++; 246 } 247 } 248 return cnt; 249 } 250 251 /** 252 * Return number of distance conflicts that are between two enrollments. It 253 * is the number of pairs of assignments of these enrollments that are in a 254 * distance conflict. 255 * 256 * @param e1 257 * an enrollment 258 * @param e2 259 * an enrollment 260 * @return number of distance conflict between given enrollments 261 */ 262 public int nrConflicts(Enrollment e1, Enrollment e2) { 263 if (!e1.isCourseRequest() || !e2.isCourseRequest() || !e1.getStudent().equals(e2.getStudent())) 264 return 0; 265 int cnt = 0; 266 for (Section s1 : e1.getSections()) { 267 for (Section s2 : e2.getSections()) { 268 if (inConflict(e1.getStudent(), s1, s2)) 269 cnt ++; 270 } 271 } 272 return cnt; 273 } 274 275 /** 276 * Return a set of distance conflicts ({@link Conflict} objects) of a 277 * (course) enrollment. 278 * 279 * @param e1 280 * an enrollment 281 * @return list of distance conflicts that are between assignment of the 282 * given enrollment 283 */ 284 public Set<Conflict> conflicts(Enrollment e1) { 285 Set<Conflict> ret = new HashSet<Conflict>(); 286 if (!e1.isCourseRequest()) 287 return ret; 288 for (Section s1 : e1.getSections()) { 289 for (Section s2 : e1.getSections()) { 290 if (s1.getId() < s2.getId() && inConflict(e1.getStudent(), s1, s2)) 291 ret.add(new Conflict(e1.getStudent(), e1, s1, e1, s2)); 292 } 293 } 294 return ret; 295 } 296 297 /** 298 * Return a set of distance conflicts ({@link Conflict} objects) between 299 * given (course) enrollments. 300 * 301 * @param e1 302 * an enrollment 303 * @param e2 304 * an enrollment 305 * @return list of distance conflicts that are between assignment of the 306 * given enrollments 307 */ 308 public Set<Conflict> conflicts(Enrollment e1, Enrollment e2) { 309 Set<Conflict> ret = new HashSet<Conflict>(); 310 if (!e1.isCourseRequest() || !e2.isCourseRequest() || !e1.getStudent().equals(e2.getStudent())) 311 return ret; 312 for (Section s1 : e1.getSections()) { 313 for (Section s2 : e2.getSections()) { 314 if (inConflict(e1.getStudent(), s1, s2)) 315 ret.add(new Conflict(e1.getStudent(), e1, s1, e2, s2)); 316 } 317 } 318 return ret; 319 } 320 321 /** 322 * The set of all conflicts ({@link Conflict} objects) of the given 323 * enrollment and other enrollments that are assignmed to the same student. 324 * @param assignment current assignment 325 * @param enrollment given enrollment 326 * @return set of all conflicts 327 */ 328 public Set<Conflict> allConflicts(Assignment<Request, Enrollment> assignment, Enrollment enrollment) { 329 Set<Conflict> ret = conflicts(enrollment); 330 if (!enrollment.isCourseRequest()) 331 return ret; 332 for (Request request : enrollment.getStudent().getRequests()) { 333 if (request.equals(enrollment.getRequest()) || assignment.getValue(request) == null) 334 continue; 335 ret.addAll(conflicts(enrollment, assignment.getValue(request))); 336 } 337 return ret; 338 } 339 340 /** Checks the counter counting all conflicts 341 * @param assignment current assignment 342 */ 343 public void checkAllConflicts(Assignment<Request, Enrollment> assignment) { 344 getContext(assignment).checkAllConflicts(assignment); 345 } 346 347 /** Actual number of all distance conflicts 348 * @param assignment current assignment 349 * @return cached number of all distance conflicts 350 **/ 351 public int getTotalNrConflicts(Assignment<Request, Enrollment> assignment) { 352 return getContext(assignment).getTotalNrConflicts(); 353 } 354 355 /** Actual number of all distance conflicts of students that need short distances 356 * @param assignment current assignment 357 * @return cached number of all distance conflicts 358 **/ 359 public int getTotalNrShortConflicts(Assignment<Request, Enrollment> assignment) { 360 return getContext(assignment).getTotalNrShortConflicts(); 361 } 362 363 /** 364 * Compute the actual number of all distance conflicts. Should be equal to 365 * {@link DistanceConflict#getTotalNrConflicts(Assignment)}. 366 * @param assignment current assignment 367 * @return computed number of all distance conflicts 368 */ 369 public int countTotalNrConflicts(Assignment<Request, Enrollment> assignment) { 370 int total = 0; 371 for (Request r1 : getModel().variables()) { 372 if (assignment.getValue(r1) == null || !(r1 instanceof CourseRequest)) 373 continue; 374 Enrollment e1 = assignment.getValue(r1); 375 total += nrConflicts(e1); 376 for (Request r2 : r1.getStudent().getRequests()) { 377 Enrollment e2 = assignment.getValue(r2); 378 if (e2 == null || r1.getId() >= r2.getId() || !(r2 instanceof CourseRequest)) 379 continue; 380 total += nrConflicts(e1, e2); 381 } 382 } 383 return total; 384 } 385 386 /** 387 * Compute a set of all distance conflicts ({@link Conflict} objects). 388 * @param assignment current assignment 389 * @return computed set of all distance conflicts 390 */ 391 public Set<Conflict> computeAllConflicts(Assignment<Request, Enrollment> assignment) { 392 Set<Conflict> ret = new HashSet<Conflict>(); 393 for (Request r1 : getModel().variables()) { 394 Enrollment e1 = assignment.getValue(r1); 395 if (e1 == null || !(r1 instanceof CourseRequest)) 396 continue; 397 ret.addAll(conflicts(e1)); 398 for (Request r2 : r1.getStudent().getRequests()) { 399 Enrollment e2 = assignment.getValue(r2); 400 if (e2 == null || r1.getId() >= r2.getId() || !(r2 instanceof CourseRequest)) 401 continue; 402 ret.addAll(conflicts(e1, e2)); 403 } 404 } 405 return ret; 406 } 407 408 /** 409 * Return a set of all distance conflicts ({@link Conflict} objects). 410 * @param assignment current assignment 411 * @return cached set of all distance conflicts 412 */ 413 public Set<Conflict> getAllConflicts(Assignment<Request, Enrollment> assignment) { 414 return getContext(assignment).getAllConflicts(); 415 } 416 417 /** 418 * Called before a value is assigned to a variable. 419 */ 420 @Override 421 public void beforeAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 422 getContext(assignment).beforeAssigned(assignment, iteration, value); 423 } 424 425 /** 426 * Called after a value is assigned to a variable. 427 */ 428 @Override 429 public void afterAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 430 getContext(assignment).afterAssigned(assignment, iteration, value); 431 } 432 433 /** 434 * Called after a value is unassigned from a variable. 435 */ 436 @Override 437 public void afterUnassigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 438 getContext(assignment).afterUnassigned(assignment, iteration, value); 439 } 440 441 /** A representation of a distance conflict */ 442 public static class Conflict { 443 private Student iStudent; 444 private Section iS1, iS2; 445 private Enrollment iE1, iE2; 446 private int iHashCode; 447 448 /** 449 * Constructor 450 * 451 * @param student 452 * related student 453 * @param e1 first enrollment 454 * @param s1 455 * first conflicting section 456 * @param e2 second enrollment 457 * @param s2 458 * second conflicting section 459 */ 460 public Conflict(Student student, Enrollment e1, Section s1, Enrollment e2, Section s2) { 461 iStudent = student; 462 if (s1.getId() < s2.getId()) { 463 iS1 = s1; 464 iS2 = s2; 465 iE1 = e1; 466 iE2 = e2; 467 } else { 468 iS1 = s2; 469 iS2 = s1; 470 iE1 = e2; 471 iE2 = e1; 472 } 473 iHashCode = (iStudent.getId() + ":" + iS1.getId() + ":" + iS2.getId()).hashCode(); 474 } 475 476 /** Related student 477 * @return student 478 **/ 479 public Student getStudent() { 480 return iStudent; 481 } 482 483 /** First section 484 * @return first section 485 **/ 486 public Section getS1() { 487 return iS1; 488 } 489 490 /** Second section 491 * @return second section 492 **/ 493 public Section getS2() { 494 return iS2; 495 } 496 497 /** First request 498 * @return first request 499 **/ 500 public Request getR1() { 501 return iE1.getRequest(); 502 } 503 504 /** Second request 505 * @return second request 506 **/ 507 public Request getR2() { 508 return iE2.getRequest(); 509 } 510 511 /** First enrollment 512 * @return first enrollment 513 **/ 514 public Enrollment getE1() { 515 return iE1; 516 } 517 518 /** Second enrollment 519 * @return second enrollment 520 **/ 521 public Enrollment getE2() { 522 return iE2; 523 } 524 525 @Override 526 public int hashCode() { 527 return iHashCode; 528 } 529 530 /** The distance between conflicting sections 531 * @param dm distance metrics 532 * @return distance in meters between conflicting sections 533 **/ 534 public double getDistance(DistanceMetric dm) { 535 return Placement.getDistanceInMeters(dm, getS1().getPlacement(), getS2().getPlacement()); 536 } 537 538 @Override 539 public boolean equals(Object o) { 540 if (o == null || !(o instanceof Conflict)) return false; 541 Conflict c = (Conflict) o; 542 return getStudent().equals(c.getStudent()) && getS1().equals(c.getS1()) && getS2().equals(c.getS2()); 543 } 544 545 @Override 546 public String toString() { 547 return getStudent() + ": " + getS1() + " -- " + getS2(); 548 } 549 } 550 551 public class DistanceConflictContext implements AssignmentConstraintContext<Request, Enrollment> { 552 private Set<Conflict> iAllConflicts = new HashSet<Conflict>(); 553 private Request iOldVariable = null; 554 private Enrollment iUnassignedValue = null; 555 556 public DistanceConflictContext(Assignment<Request, Enrollment> assignment) { 557 iAllConflicts = computeAllConflicts(assignment); 558 StudentSectioningModelContext cx = ((StudentSectioningModel)getModel()).getContext(assignment); 559 for (Conflict c: iAllConflicts) 560 cx.add(assignment, c); 561 } 562 563 /** 564 * Called before a value is assigned to a variable. 565 * @param assignment current assignment 566 * @param iteration current iteration 567 * @param value value to be assigned 568 */ 569 public void beforeAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 570 if (value != null) { 571 Enrollment old = assignment.getValue(value.variable()); 572 if (old != null) { 573 unassigned(assignment, old); 574 iUnassignedValue = old; 575 } 576 iOldVariable = value.variable(); 577 } 578 } 579 580 /** 581 * Called after a value is assigned to a variable. 582 * @param assignment current assignment 583 * @param iteration current iteration 584 * @param value value that was assigned 585 */ 586 public void afterAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 587 iOldVariable = null; 588 iUnassignedValue = null; 589 if (value != null) 590 assigned(assignment, value); 591 } 592 593 /** 594 * Called after a value is unassigned from a variable. 595 * @param assignment current assignment 596 * @param iteration current iteration 597 * @param value value to be unassigned 598 */ 599 public void afterUnassigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 600 if (value != null && !value.equals(iUnassignedValue)) 601 unassigned(assignment, value); 602 } 603 604 /** 605 * Called when a value is assigned to a variable. Internal number of 606 * distance conflicts is updated, see 607 * {@link DistanceConflict#getTotalNrConflicts(Assignment)}. 608 */ 609 @Override 610 public void assigned(Assignment<Request, Enrollment> assignment, Enrollment value) { 611 StudentSectioningModelContext cx = ((StudentSectioningModel)getModel()).getContext(assignment); 612 for (Conflict c: allConflicts(assignment, value)) { 613 if (iAllConflicts.add(c)) 614 cx.add(assignment, c); 615 } 616 if (sDebug) { 617 sLog.debug("A:" + value.variable() + " := " + value); 618 int inc = nrConflicts(value); 619 if (inc != 0) { 620 sLog.debug("-- DC+" + inc + " A: " + value.variable() + " := " + value); 621 for (Iterator<Conflict> i = allConflicts(assignment, value).iterator(); i.hasNext();) 622 sLog.debug(" -- " + i.next()); 623 } 624 } 625 } 626 627 /** 628 * Called when a value is unassigned from a variable. Internal number of 629 * distance conflicts is updated, see 630 * {@link DistanceConflict#getTotalNrConflicts(Assignment)}. 631 */ 632 @Override 633 public void unassigned(Assignment<Request, Enrollment> assignment, Enrollment value) { 634 if (value.variable().equals(iOldVariable)) 635 return; 636 StudentSectioningModelContext cx = ((StudentSectioningModel)getModel()).getContext(assignment); 637 for (Conflict c: allConflicts(assignment, value)) { 638 if (iAllConflicts.remove(c)) 639 cx.remove(assignment, c); 640 } 641 if (sDebug) { 642 sLog.debug("U:" + value.variable() + " := " + value); 643 int dec = nrAllConflicts(assignment, value); 644 if (dec != 0) { 645 sLog.debug("-- DC+" + dec + " U: " + value.variable() + " := " + value); 646 Set<Conflict> confs = allConflicts(assignment, value); 647 for (Iterator<Conflict> i = confs.iterator(); i.hasNext();) 648 sLog.debug(" -- " + i.next()); 649 } 650 } 651 } 652 653 /** Checks the counter counting all conflicts 654 * @param assignment current assignment 655 **/ 656 public void checkAllConflicts(Assignment<Request, Enrollment> assignment) { 657 Set<Conflict> allConfs = computeAllConflicts(assignment); 658 if (iAllConflicts.size() != allConfs.size()) { 659 sLog.error("Different number of conflicts " + iAllConflicts.size() + "!=" + allConfs.size()); 660 for (Iterator<Conflict> i = allConfs.iterator(); i.hasNext();) { 661 Conflict c = i.next(); 662 if (!iAllConflicts.contains(c)) 663 sLog.debug(" +add+ " + c); 664 } 665 for (Iterator<Conflict> i = iAllConflicts.iterator(); i.hasNext();) { 666 Conflict c = i.next(); 667 if (!allConfs.contains(c)) 668 sLog.debug(" -rem- " + c); 669 } 670 iAllConflicts = allConfs; 671 } 672 } 673 674 /** Actual number of all distance conflicts 675 * @return number of all distance conflicts 676 **/ 677 public int getTotalNrConflicts() { 678 return iAllConflicts.size(); 679 } 680 681 /** Actual number of all distance conflicts of students that need short distances 682 * @return number of all distance conflicts 683 **/ 684 public int getTotalNrShortConflicts() { 685 int ret = 0; 686 for (Conflict c: iAllConflicts) 687 if (c.getStudent().isNeedShortDistances()) ret ++; 688 return ret; 689 } 690 691 692 /** 693 * Return a set of all distance conflicts ({@link Conflict} objects). 694 * @return all distance conflicts 695 */ 696 public Set<Conflict> getAllConflicts() { 697 return iAllConflicts; 698 } 699 700 /** 701 * Total sum of all conflict of the given enrollment and other enrollments 702 * that are assigned to the same student. 703 * @param assignment current assignment 704 * @param enrollment given enrollment 705 * @return number of all conflict of the given enrollment 706 */ 707 public int nrAllConflicts(Assignment<Request, Enrollment> assignment, Enrollment enrollment) { 708 if (!enrollment.isCourseRequest()) 709 return 0; 710 int cnt = nrConflicts(enrollment); 711 Request old = iOldVariable; 712 for (Request request : enrollment.getStudent().getRequests()) { 713 if (request.equals(enrollment.getRequest()) || assignment.getValue(request) == null || request.equals(old)) 714 continue; 715 cnt += nrConflicts(enrollment, assignment.getValue(request)); 716 } 717 return cnt; 718 } 719 } 720 721 @Override 722 public DistanceConflictContext createAssignmentContext(Assignment<Request, Enrollment> assignment) { 723 return new DistanceConflictContext(assignment); 724 } 725}