001package net.sf.cpsolver.coursett.constraint; 002 003import java.util.ArrayList; 004import java.util.BitSet; 005import java.util.Enumeration; 006import java.util.HashSet; 007import java.util.List; 008import java.util.Set; 009 010import net.sf.cpsolver.coursett.Constants; 011import net.sf.cpsolver.coursett.criteria.BackToBackInstructorPreferences; 012import net.sf.cpsolver.coursett.model.Lecture; 013import net.sf.cpsolver.coursett.model.Placement; 014import net.sf.cpsolver.coursett.model.TimeLocation; 015import net.sf.cpsolver.coursett.model.TimetableModel; 016import net.sf.cpsolver.ifs.model.Constraint; 017import net.sf.cpsolver.ifs.util.DistanceMetric; 018 019/** 020 * Instructor constraint. <br> 021 * Classes with this instructor can not overlap in time. Also, for back-to-back 022 * classes, there is the following reasoning: 023 * <ul> 024 * <li>if the distance is equal or below 025 * {@link DistanceMetric#getInstructorNoPreferenceLimit()} .. no preference 026 * <li>if the distance is above 027 * {@link DistanceMetric#getInstructorNoPreferenceLimit()} and below 028 * {@link DistanceMetric#getInstructorDiscouragedLimit()} .. constraint is 029 * discouraged (soft, preference = 1) 030 * <li>if the distance is above 031 * {@link DistanceMetric#getInstructorDiscouragedLimit()} and below 032 * {@link DistanceMetric#getInstructorProhibitedLimit()} .. constraint is 033 * strongly discouraged (soft, preference = 2) 034 * <li>if the distance is above 035 * {@link DistanceMetric#getInstructorProhibitedLimit()} .. constraint is 036 * prohibited (hard) 037 * </ul> 038 * <br> 039 * When {@link InstructorConstraint#isIgnoreDistances()} is set to true, the 040 * constraint never prohibits two back-to-back classes (but it still tries to 041 * minimize the above back-to-back preferences). 042 * 043 * @version CourseTT 1.2 (University Course Timetabling)<br> 044 * Copyright (C) 2006 - 2010 Tomáš Müller<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 InstructorConstraint extends Constraint<Lecture, Placement> { 064 065 public int iPreference = 0; 066 067 /** 068 * table iResource[slot] = lecture using this resource placed in the given 069 * time slot (null if empty) 070 */ 071 protected List<Placement>[] iResource; 072 private Long iResourceId; 073 private String iName; 074 private String iPuid; 075 private List<Placement> iUnavailabilities = null; 076 private boolean iIgnoreDistances = false; 077 private Long iType = null; 078 079 /** 080 * Constructor 081 * 082 * @param id 083 * instructor id 084 * @param name 085 * instructor name 086 */ 087 @SuppressWarnings("unchecked") 088 public InstructorConstraint(Long id, String puid, String name, boolean ignDist) { 089 iResourceId = id; 090 iName = name; 091 iPuid = puid; 092 iIgnoreDistances = ignDist; 093 iResource = new List[Constants.SLOTS_PER_DAY * Constants.DAY_CODES.length]; 094 for (int i = 0; i < iResource.length; i++) 095 iResource[i] = new ArrayList<Placement>(3); 096 } 097 098 public List<Placement> getPlacements(int slot, Placement placement) { 099 return getPlacements(slot, placement.getTimeLocation().getWeekCode()); 100 } 101 102 public List<Placement> getPlacements(int slot, BitSet weekCode) { 103 List<Placement> placements = new ArrayList<Placement>(iResource[slot].size()); 104 for (Placement p : iResource[slot]) { 105 if (p.getTimeLocation().shareWeeks(weekCode)) 106 placements.add(p); 107 } 108 return placements; 109 } 110 111 public Placement getPlacement(int slot, int day) { 112 for (Placement p : iResource[slot]) { 113 if (p.getTimeLocation().hasDay(day)) 114 return p; 115 } 116 return null; 117 } 118 119 public void setNotAvailable(Placement placement) { 120 if (iUnavailabilities == null) 121 iUnavailabilities = new ArrayList<Placement>(); 122 iUnavailabilities.add(placement); 123 for (Lecture lecture: variables()) 124 lecture.clearValueCache(); 125 } 126 127 public boolean isAvailable(Lecture lecture, TimeLocation time) { 128 if (iUnavailabilities == null) return true; 129 for (Placement c: iUnavailabilities) { 130 if (c.getTimeLocation().hasIntersection(time) && !lecture.canShareRoom(c.variable())) return false; 131 } 132 return true; 133 } 134 135 private DistanceMetric getDistanceMetric() { 136 return ((TimetableModel)getModel()).getDistanceMetric(); 137 } 138 139 public boolean isAvailable(Lecture lecture, Placement placement) { 140 if (iUnavailabilities == null) return true; 141 TimeLocation t1 = placement.getTimeLocation(); 142 for (Placement c: iUnavailabilities) { 143 if (c.getTimeLocation().hasIntersection(placement.getTimeLocation()) && (!lecture.canShareRoom(c.variable()) || !placement.sameRooms(c))) 144 return false; 145 if (!iIgnoreDistances) { 146 TimeLocation t2 = c.getTimeLocation(); 147 if (t1.shareDays(t2) && t1.shareWeeks(t2)) { 148 if (t1.getStartSlot() + t1.getLength() == t2.getStartSlot() || t2.getStartSlot() + t2.getLength() == t1.getStartSlot()) { 149 if (Placement.getDistanceInMeters(getDistanceMetric(), placement, c) > getDistanceMetric().getInstructorProhibitedLimit()) 150 return false; 151 } else if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) { 152 if (t1.getStartSlot() + t1.getLength() < t2.getStartSlot()) { 153 if (Placement.getDistanceInMinutes(getDistanceMetric(), placement, c) > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t2.getStartSlot() - t1.getStartSlot() - t1.getLength())) 154 return false; 155 } else if (t2.getStartSlot() + t2.getLength() < t1.getStartSlot()) { 156 if (Placement.getDistanceInMinutes(getDistanceMetric(), placement, c) > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t1.getStartSlot() - t2.getStartSlot() - t2.getLength())) 157 return false; 158 } 159 } 160 } 161 } 162 } 163 return true; 164 } 165 166 public List<Placement> getUnavailabilities() { 167 return iUnavailabilities; 168 } 169 170 @Deprecated 171 @SuppressWarnings("unchecked") 172 public List<Placement>[] getAvailableArray() { 173 if (iUnavailabilities == null) return null; 174 List<Placement>[] available = new List[Constants.SLOTS_PER_DAY * Constants.DAY_CODES.length]; 175 for (int i = 0; i < iResource.length; i++) 176 available[i] = null; 177 for (Placement p: iUnavailabilities) { 178 for (Enumeration<Integer> e = p.getTimeLocation().getSlots(); e.hasMoreElements();) { 179 int slot = e.nextElement(); 180 if (available[slot] == null) 181 available[slot] = new ArrayList<Placement>(1); 182 available[slot].add(p); 183 } 184 } 185 return available; 186 } 187 188 /** Back-to-back preference of two placements (3 means prohibited) */ 189 public int getDistancePreference(Placement p1, Placement p2) { 190 TimeLocation t1 = p1.getTimeLocation(); 191 TimeLocation t2 = p2.getTimeLocation(); 192 if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) 193 return Constants.sPreferenceLevelNeutral; 194 if (t1.getStartSlot() + t1.getLength() == t2.getStartSlot() || t2.getStartSlot() + t2.getLength() == t1.getStartSlot()) { 195 double distance = Placement.getDistanceInMeters(getDistanceMetric(), p1, p2); 196 if (distance <= getDistanceMetric().getInstructorNoPreferenceLimit()) 197 return Constants.sPreferenceLevelNeutral; 198 if (distance <= getDistanceMetric().getInstructorDiscouragedLimit()) 199 return Constants.sPreferenceLevelDiscouraged; 200 if (iIgnoreDistances || distance <= getDistanceMetric().getInstructorProhibitedLimit()) 201 return Constants.sPreferenceLevelStronglyDiscouraged; 202 return Constants.sPreferenceLevelProhibited; 203 } else if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) { 204 if (t1.getStartSlot() + t1.getLength() < t2.getStartSlot()) { 205 int distanceInMinutes = Placement.getDistanceInMinutes(getDistanceMetric(), p1, p2); 206 if (distanceInMinutes > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t2.getStartSlot() - t1.getStartSlot() - t1.getLength())) // too far apart 207 return (iIgnoreDistances ? Constants.sPreferenceLevelStronglyDiscouraged : Constants.sPreferenceLevelProhibited); 208 if (distanceInMinutes >= getDistanceMetric().getInstructorLongTravelInMinutes()) // long travel 209 return Constants.sPreferenceLevelStronglyDiscouraged; 210 if (distanceInMinutes > Constants.SLOT_LENGTH_MIN * (t2.getStartSlot() - t1.getStartSlot() - t1.getLength())) // too far if no break time 211 return Constants.sPreferenceLevelDiscouraged; 212 } else if (t2.getStartSlot() + t2.getLength() < t1.getStartSlot()) { 213 int distanceInMinutes = Placement.getDistanceInMinutes(getDistanceMetric(), p1, p2); 214 if (distanceInMinutes > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t1.getStartSlot() - t2.getStartSlot() - t2.getLength())) // too far apart 215 return (iIgnoreDistances ? Constants.sPreferenceLevelStronglyDiscouraged : Constants.sPreferenceLevelProhibited); 216 if (distanceInMinutes >= getDistanceMetric().getInstructorLongTravelInMinutes()) // long travel 217 return Constants.sPreferenceLevelStronglyDiscouraged; 218 if (distanceInMinutes > Constants.SLOT_LENGTH_MIN * (t1.getStartSlot() - t2.getStartSlot() - t2.getLength())) // too far if no break time 219 return Constants.sPreferenceLevelDiscouraged; 220 } 221 } 222 return Constants.sPreferenceLevelNeutral; 223 } 224 225 /** Resource id */ 226 public Long getResourceId() { 227 return iResourceId; 228 } 229 230 /** Resource name */ 231 @Override 232 public String getName() { 233 return iName; 234 } 235 236 @Override 237 public void computeConflicts(Placement placement, Set<Placement> conflicts) { 238 Lecture lecture = placement.variable(); 239 BitSet weekCode = placement.getTimeLocation().getWeekCode(); 240 241 for (Enumeration<Integer> e = placement.getTimeLocation().getSlots(); e.hasMoreElements();) { 242 int slot = e.nextElement(); 243 for (Placement p : iResource[slot]) { 244 if (!p.equals(lecture.getAssignment()) && p.getTimeLocation().shareWeeks(weekCode)) { 245 if (p.canShareRooms(placement) && p.sameRooms(placement)) 246 continue; 247 conflicts.add(p); 248 } 249 } 250 } 251 if (!iIgnoreDistances) { 252 for (Enumeration<Integer> e = placement.getTimeLocation().getStartSlots(); e.hasMoreElements();) { 253 int startSlot = e.nextElement(); 254 255 int prevSlot = startSlot - 1; 256 if (prevSlot >= 0 && (prevSlot / Constants.SLOTS_PER_DAY) == (startSlot / Constants.SLOTS_PER_DAY)) { 257 for (Placement c : getPlacements(prevSlot, placement)) { 258 if (lecture.equals(c.variable())) continue; 259 if (c.canShareRooms(placement) && c.sameRooms(placement)) continue; 260 if (Placement.getDistanceInMeters(getDistanceMetric(), placement, c) > getDistanceMetric().getInstructorProhibitedLimit()) 261 conflicts.add(c); 262 } 263 } 264 int nextSlot = startSlot + placement.getTimeLocation().getLength(); 265 if ((nextSlot / Constants.SLOTS_PER_DAY) == (startSlot / Constants.SLOTS_PER_DAY)) { 266 for (Placement c : getPlacements(nextSlot, placement)) { 267 if (lecture.equals(c.variable())) continue; 268 if (c.canShareRooms(placement) && c.sameRooms(placement)) continue; 269 if (Placement.getDistanceInMeters(getDistanceMetric(), placement, c) > getDistanceMetric().getInstructorProhibitedLimit()) 270 conflicts.add(c); 271 } 272 } 273 274 if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) { 275 TimeLocation t1 = placement.getTimeLocation(); 276 for (Lecture other: assignedVariables()) { 277 if (other.getAssignment() == null || other.equals(placement.variable())) continue; 278 TimeLocation t2 = other.getAssignment().getTimeLocation(); 279 if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) continue; 280 if (t1.getStartSlot() + t1.getLength() < t2.getStartSlot()) { 281 if (Placement.getDistanceInMinutes(getDistanceMetric(), placement, other.getAssignment()) > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t2.getStartSlot() - t1.getStartSlot() - t1.getLength())) 282 conflicts.add(other.getAssignment()); 283 } else if (t2.getStartSlot() + t2.getLength() < t1.getStartSlot()) { 284 if (Placement.getDistanceInMinutes(getDistanceMetric(), placement, other.getAssignment()) > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t1.getStartSlot() - t2.getStartSlot() - t2.getLength())) 285 conflicts.add(other.getAssignment()); 286 } 287 } 288 } 289 } 290 } 291 } 292 293 @Override 294 public boolean inConflict(Placement placement) { 295 Lecture lecture = placement.variable(); 296 BitSet weekCode = placement.getTimeLocation().getWeekCode(); 297 for (Enumeration<Integer> e = placement.getTimeLocation().getSlots(); e.hasMoreElements();) { 298 int slot = e.nextElement(); 299 for (Placement p : iResource[slot]) { 300 if (!p.equals(lecture.getAssignment()) && p.getTimeLocation().shareWeeks(weekCode)) { 301 if (p.canShareRooms(placement) && p.sameRooms(placement)) 302 continue; 303 return true; 304 } 305 } 306 } 307 if (!iIgnoreDistances) { 308 for (Enumeration<Integer> e = placement.getTimeLocation().getStartSlots(); e.hasMoreElements();) { 309 int startSlot = e.nextElement(); 310 311 int prevSlot = startSlot - 1; 312 if (prevSlot >= 0 && (prevSlot / Constants.SLOTS_PER_DAY) == (startSlot / Constants.SLOTS_PER_DAY)) { 313 for (Placement c : getPlacements(prevSlot, placement)) { 314 if (lecture.equals(c.variable())) continue; 315 if (c.canShareRooms(placement) && c.sameRooms(placement)) continue; 316 if (Placement.getDistanceInMeters(getDistanceMetric(), placement, c) > getDistanceMetric().getInstructorProhibitedLimit()) 317 return true; 318 } 319 } 320 int nextSlot = startSlot + placement.getTimeLocation().getLength(); 321 if ((nextSlot / Constants.SLOTS_PER_DAY) == (startSlot / Constants.SLOTS_PER_DAY)) { 322 for (Placement c : getPlacements(nextSlot, placement)) { 323 if (lecture.equals(c.variable())) continue; 324 if (c.canShareRooms(placement) && c.sameRooms(placement)) continue; 325 if (Placement.getDistanceInMeters(getDistanceMetric(), placement, c) > getDistanceMetric().getInstructorProhibitedLimit()) 326 return true; 327 } 328 } 329 330 if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) { 331 TimeLocation t1 = placement.getTimeLocation(); 332 for (Lecture other: assignedVariables()) { 333 if (other.getAssignment() == null || other.equals(placement.variable())) continue; 334 TimeLocation t2 = other.getAssignment().getTimeLocation(); 335 if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) continue; 336 if (t1.getStartSlot() + t1.getLength() < t2.getStartSlot()) { 337 if (Placement.getDistanceInMinutes(getDistanceMetric(), placement, other.getAssignment()) > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t2.getStartSlot() - t1.getStartSlot() - t1.getLength())) 338 return true; 339 } else if (t2.getStartSlot() + t2.getLength() < t1.getStartSlot()) { 340 if (Placement.getDistanceInMinutes(getDistanceMetric(), placement, other.getAssignment()) > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t1.getStartSlot() - t2.getStartSlot() - t2.getLength())) 341 return true; 342 } 343 } 344 } 345 } 346 } 347 return false; 348 } 349 350 @Override 351 public boolean isConsistent(Placement p1, Placement p2) { 352 if (p1.canShareRooms(p2) && p1.sameRooms(p2)) 353 return true; 354 if (p1.getTimeLocation().hasIntersection(p2.getTimeLocation())) 355 return false; 356 return getDistancePreference(p1, p2) != Constants.sPreferenceLevelProhibited; 357 } 358 359 @Override 360 public void assigned(long iteration, Placement placement) { 361 super.assigned(iteration, placement); 362 // iPreference += getPreference(placement); 363 for (Enumeration<Integer> e = placement.getTimeLocation().getSlots(); e.hasMoreElements();) { 364 int slot = e.nextElement(); 365 iResource[slot].add(placement); 366 } 367 getModel().getCriterion(BackToBackInstructorPreferences.class).inc(-iPreference); 368 iPreference = countPreference(); 369 getModel().getCriterion(BackToBackInstructorPreferences.class).inc(iPreference); 370 } 371 372 @Override 373 public void unassigned(long iteration, Placement placement) { 374 super.unassigned(iteration, placement); 375 // iPreference -= getPreference(placement); 376 for (Enumeration<Integer> e = placement.getTimeLocation().getSlots(); e.hasMoreElements();) { 377 int slot = e.nextElement(); 378 iResource[slot].remove(placement); 379 } 380 getModel().getCriterion(BackToBackInstructorPreferences.class).inc(-iPreference); 381 iPreference = countPreference(); 382 getModel().getCriterion(BackToBackInstructorPreferences.class).inc(iPreference); 383 } 384 385 /** 386 * Lookup table getResource()[slot] -> lecture using this resource placed in 387 * the given time slot (null if empty) 388 */ 389 public List<Placement> getResource(int slot) { 390 return iResource[slot]; 391 } 392 393 public Placement[] getResourceOfWeek(int startDay) { 394 Placement[] ret = new Placement[iResource.length]; 395 for (int i = 0; i < iResource.length; i++) { 396 ret[i] = getPlacement(i, startDay + (i / Constants.SLOTS_PER_DAY)); 397 } 398 return ret; 399 } 400 401 /** Number of useless slots for this resource */ 402 public int countUselessSlots() { 403 int ret = 0; 404 for (int d = 0; d < Constants.DAY_CODES.length; d++) { 405 for (int s = 1; s < Constants.SLOTS_PER_DAY - 1; s++) { 406 int slot = d * Constants.SLOTS_PER_DAY + s; 407 if (iResource[slot - 1] != null && iResource[slot] == null && iResource[slot + 1] != null) 408 ret++; 409 } 410 } 411 return ret; 412 } 413 414 /** Resource usage usage */ 415 protected void printUsage(StringBuffer sb) { 416 for (int slot = 0; slot < iResource.length; slot++) { 417 for (Placement p : iResource[slot]) { 418 int day = slot / Constants.SLOTS_PER_DAY; 419 int time = slot * Constants.SLOT_LENGTH_MIN + Constants.FIRST_SLOT_TIME_MIN; 420 int h = time / 60; 421 int m = time % 60; 422 String d = Constants.DAY_NAMES_SHORT[day]; 423 int slots = p.getTimeLocation().getLength(); 424 time += (30 * slots); 425 int h2 = time / 60; 426 int m2 = time % 60; 427 sb.append(sb.length() == 0 ? "" : ",\n ").append( 428 "[" + d + (h > 12 ? h - 12 : h) + ":" + (m < 10 ? "0" : "") + m + (h >= 12 ? "p" : "a") + "-" 429 + (h2 > 12 ? h2 - 12 : h2) + ":" + (m2 < 10 ? "0" : "") + m2 + (h2 >= 12 ? "p" : "a") 430 + "]=").append(p.variable().getName()); 431 slot += slots - 1; 432 // sb.append(sb.length()==0?"":", ").append("s"+(slot+1)+"=").append(((Lecture)getResource()[slot]).getName()); 433 } 434 } 435 } 436 437 @Override 438 public String toString() { 439 return "Instructor " + getName(); 440 } 441 442 /** Back-to-back preference of the given placement */ 443 public int getPreference(Placement value) { 444 Lecture lecture = value.variable(); 445 Placement placement = value; 446 int pref = 0; 447 HashSet<Placement> checked = new HashSet<Placement>(); 448 449 for (Enumeration<Integer> e = placement.getTimeLocation().getStartSlots(); e.hasMoreElements();) { 450 int startSlot = e.nextElement(); 451 452 int prevSlot = startSlot - 1; 453 if (prevSlot >= 0 && (prevSlot / Constants.SLOTS_PER_DAY) == (startSlot / Constants.SLOTS_PER_DAY)) { 454 for (Placement c : getPlacements(prevSlot, placement)) { 455 if (lecture.equals(c.variable()) || !checked.add(c)) continue; 456 double dist = Placement.getDistanceInMeters(getDistanceMetric(), placement, c); 457 if (dist > getDistanceMetric().getInstructorNoPreferenceLimit() && dist <= getDistanceMetric().getInstructorDiscouragedLimit()) 458 pref += Constants.sPreferenceLevelDiscouraged; 459 if (dist > getDistanceMetric().getInstructorDiscouragedLimit() 460 && (dist <= getDistanceMetric().getInstructorProhibitedLimit() || iIgnoreDistances)) 461 pref += Constants.sPreferenceLevelStronglyDiscouraged; 462 if (!iIgnoreDistances && dist > getDistanceMetric().getInstructorProhibitedLimit()) 463 pref += Constants.sPreferenceLevelProhibited; 464 } 465 } 466 int nextSlot = startSlot + placement.getTimeLocation().getLength(); 467 if ((nextSlot / Constants.SLOTS_PER_DAY) == (startSlot / Constants.SLOTS_PER_DAY)) { 468 for (Placement c : getPlacements(nextSlot, placement)) { 469 if (lecture.equals(c.variable()) || !checked.add(c)) continue; 470 double dist = Placement.getDistanceInMeters(getDistanceMetric(), placement, c); 471 if (dist > getDistanceMetric().getInstructorNoPreferenceLimit() && dist <= getDistanceMetric().getInstructorDiscouragedLimit()) 472 pref += Constants.sPreferenceLevelDiscouraged; 473 if (dist > getDistanceMetric().getInstructorDiscouragedLimit() 474 && (dist <= getDistanceMetric().getInstructorProhibitedLimit() || iIgnoreDistances)) 475 pref += Constants.sPreferenceLevelStronglyDiscouraged; 476 if (!iIgnoreDistances && dist > getDistanceMetric().getInstructorProhibitedLimit()) 477 pref = Constants.sPreferenceLevelProhibited; 478 } 479 } 480 481 if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) { 482 TimeLocation t1 = placement.getTimeLocation(); 483 Placement before = null, after = null; 484 for (Lecture other: assignedVariables()) { 485 if (other.getAssignment() == null || other.equals(placement.variable())) continue; 486 TimeLocation t2 = other.getAssignment().getTimeLocation(); 487 if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) continue; 488 if (t1.getStartSlot() + t1.getLength() < t2.getStartSlot()) { 489 int distanceInMinutes = Placement.getDistanceInMinutes(getDistanceMetric(), placement, other.getAssignment()); 490 if (distanceInMinutes > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t2.getStartSlot() - t1.getStartSlot() - t1.getLength())) 491 pref += (iIgnoreDistances ? Constants.sPreferenceLevelStronglyDiscouraged : Constants.sPreferenceLevelProhibited); 492 else if (distanceInMinutes > Constants.SLOT_LENGTH_MIN * (t2.getStartSlot() - t1.getStartSlot() - t1.getLength())) 493 pref += Constants.sPreferenceLevelDiscouraged; 494 } else if (t2.getStartSlot() + t2.getLength() < t1.getStartSlot()) { 495 int distanceInMinutes = Placement.getDistanceInMinutes(getDistanceMetric(), placement, other.getAssignment()); 496 if (distanceInMinutes > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t1.getStartSlot() - t2.getStartSlot() - t2.getLength())) 497 pref += (iIgnoreDistances ? Constants.sPreferenceLevelStronglyDiscouraged : Constants.sPreferenceLevelProhibited); 498 else if (distanceInMinutes > Constants.SLOT_LENGTH_MIN * (t1.getStartSlot() - t2.getStartSlot() - t2.getLength())) 499 pref += Constants.sPreferenceLevelDiscouraged; 500 } 501 if (t1.getStartSlot() + t1.getLength() <= t2.getStartSlot()) { 502 if (after == null || t2.getStartSlot() < after.getTimeLocation().getStartSlot()) 503 after = other.getAssignment(); 504 } else if (t2.getStartSlot() + t2.getLength() <= t1.getStartSlot()) { 505 if (before == null || before.getTimeLocation().getStartSlot() < t2.getStartSlot()) 506 before = other.getAssignment(); 507 } 508 } 509 if (iUnavailabilities != null) { 510 for (Placement c: iUnavailabilities) { 511 TimeLocation t2 = c.getTimeLocation(); 512 if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) continue; 513 if (t1.getStartSlot() + t1.getLength() <= t2.getStartSlot()) { 514 if (after == null || t2.getStartSlot() < after.getTimeLocation().getStartSlot()) 515 after = c; 516 } else if (t2.getStartSlot() + t2.getLength() <= t1.getStartSlot()) { 517 if (before == null || before.getTimeLocation().getStartSlot() < t2.getStartSlot()) 518 before = c; 519 } 520 } 521 } 522 if (before != null && Placement.getDistanceInMinutes(getDistanceMetric(), before, placement) > getDistanceMetric().getInstructorLongTravelInMinutes()) 523 pref += Constants.sPreferenceLevelStronglyDiscouraged; 524 if (after != null && Placement.getDistanceInMinutes(getDistanceMetric(), after, placement) > getDistanceMetric().getInstructorLongTravelInMinutes()) 525 pref += Constants.sPreferenceLevelStronglyDiscouraged; 526 if (before != null && after != null && Placement.getDistanceInMinutes(getDistanceMetric(), before, after) > getDistanceMetric().getInstructorLongTravelInMinutes()) 527 pref -= Constants.sPreferenceLevelStronglyDiscouraged; 528 } 529 } 530 return pref; 531 } 532 533 public int getPreferenceCombination(Placement value) { 534 Lecture lecture = value.variable(); 535 Placement placement = value; 536 int pref = 0; 537 HashSet<Placement> checked = new HashSet<Placement>(); 538 539 for (Enumeration<Integer> e = placement.getTimeLocation().getStartSlots(); e.hasMoreElements();) { 540 int startSlot = e.nextElement(); 541 542 int prevSlot = startSlot - 1; 543 if (prevSlot >= 0 && (prevSlot / Constants.SLOTS_PER_DAY) == (startSlot / Constants.SLOTS_PER_DAY)) { 544 for (Placement c : getPlacements(prevSlot, placement)) { 545 if (lecture.equals(c.variable()) || !checked.add(c)) continue; 546 double dist = Placement.getDistanceInMeters(getDistanceMetric(), placement, c); 547 if (dist > getDistanceMetric().getInstructorNoPreferenceLimit() && dist <= getDistanceMetric().getInstructorDiscouragedLimit()) 548 pref = Math.max(pref, Constants.sPreferenceLevelDiscouraged); 549 if (dist > getDistanceMetric().getInstructorDiscouragedLimit() 550 && (dist <= getDistanceMetric().getInstructorProhibitedLimit() || iIgnoreDistances)) 551 pref = Math.max(pref, Constants.sPreferenceLevelStronglyDiscouraged); 552 if (!iIgnoreDistances && dist > getDistanceMetric().getInstructorProhibitedLimit()) 553 pref = Math.max(pref, Constants.sPreferenceLevelProhibited); 554 } 555 } 556 int nextSlot = startSlot + placement.getTimeLocation().getLength(); 557 if ((nextSlot / Constants.SLOTS_PER_DAY) == (startSlot / Constants.SLOTS_PER_DAY)) { 558 for (Placement c : getPlacements(nextSlot, placement)) { 559 if (lecture.equals(c.variable()) || !checked.add(c)) continue; 560 double dist = Placement.getDistanceInMeters(getDistanceMetric(), placement, c); 561 if (dist > getDistanceMetric().getInstructorNoPreferenceLimit() && dist <= getDistanceMetric().getInstructorDiscouragedLimit()) 562 pref = Math.max(pref, Constants.sPreferenceLevelDiscouraged); 563 if (dist > getDistanceMetric().getInstructorDiscouragedLimit() 564 && (dist <= getDistanceMetric().getInstructorProhibitedLimit() || iIgnoreDistances)) 565 pref = Math.max(pref, Constants.sPreferenceLevelStronglyDiscouraged); 566 if (!iIgnoreDistances && dist > getDistanceMetric().getInstructorProhibitedLimit()) 567 pref = Constants.sPreferenceLevelProhibited; 568 } 569 } 570 571 if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) { 572 TimeLocation t1 = placement.getTimeLocation(); 573 Placement before = null, after = null; 574 for (Lecture other: assignedVariables()) { 575 if (other.getAssignment() == null || other.equals(placement.variable())) continue; 576 TimeLocation t2 = other.getAssignment().getTimeLocation(); 577 if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) continue; 578 if (t1.getStartSlot() + t1.getLength() < t2.getStartSlot()) { 579 int distanceInMinutes = Placement.getDistanceInMinutes(getDistanceMetric(), placement, other.getAssignment()); 580 if (distanceInMinutes > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t2.getStartSlot() - t1.getStartSlot() - t1.getLength())) 581 pref = Math.max(pref, (iIgnoreDistances ? Constants.sPreferenceLevelStronglyDiscouraged : Constants.sPreferenceLevelProhibited)); 582 else if (distanceInMinutes > Constants.SLOT_LENGTH_MIN * (t2.getStartSlot() - t1.getStartSlot() - t1.getLength())) 583 pref = Math.max(pref, Constants.sPreferenceLevelDiscouraged); 584 } else if (t2.getStartSlot() + t2.getLength() < t1.getStartSlot()) { 585 int distanceInMinutes = Placement.getDistanceInMinutes(getDistanceMetric(), placement, other.getAssignment()); 586 if (distanceInMinutes > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t1.getStartSlot() - t2.getStartSlot() - t2.getLength())) 587 pref = Math.max(pref, (iIgnoreDistances ? Constants.sPreferenceLevelStronglyDiscouraged : Constants.sPreferenceLevelProhibited)); 588 else if (distanceInMinutes > Constants.SLOT_LENGTH_MIN * (t1.getStartSlot() - t2.getStartSlot() - t2.getLength())) 589 pref = Math.max(pref, Constants.sPreferenceLevelDiscouraged); 590 } 591 if (t1.getStartSlot() + t1.getLength() <= t2.getStartSlot()) { 592 if (after == null || t2.getStartSlot() < after.getTimeLocation().getStartSlot()) 593 after = other.getAssignment(); 594 } else if (t2.getStartSlot() + t2.getLength() <= t1.getStartSlot()) { 595 if (before == null || before.getTimeLocation().getStartSlot() < t2.getStartSlot()) 596 before = other.getAssignment(); 597 } 598 } 599 if (iUnavailabilities != null) { 600 for (Placement c: iUnavailabilities) { 601 TimeLocation t2 = c.getTimeLocation(); 602 if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) continue; 603 if (t1.getStartSlot() + t1.getLength() <= t2.getStartSlot()) { 604 if (after == null || t2.getStartSlot() < after.getTimeLocation().getStartSlot()) 605 after = c; 606 } else if (t2.getStartSlot() + t2.getLength() <= t1.getStartSlot()) { 607 if (before == null || before.getTimeLocation().getStartSlot() < t2.getStartSlot()) 608 before = c; 609 } 610 } 611 } 612 int tooLongTravel = 0; 613 if (before != null && Placement.getDistanceInMinutes(getDistanceMetric(), before, placement) > getDistanceMetric().getInstructorLongTravelInMinutes()) 614 tooLongTravel++; 615 if (after != null && Placement.getDistanceInMinutes(getDistanceMetric(), after, placement) > getDistanceMetric().getInstructorLongTravelInMinutes()) 616 tooLongTravel++; 617 if (tooLongTravel > 0) 618 pref += Math.max(pref, Constants.sPreferenceLevelStronglyDiscouraged); 619 } 620 } 621 return pref; 622 } 623 624 /** Overall back-to-back preference of this instructor */ 625 public int getPreference() { 626 /* 627 * if (iPreference!=countPreference()) {System.err.println( 628 * "InstructorConstraint.getPreference() is not working properly"); } 629 */ 630 return iPreference; 631 } 632 633 public int countPreference() { 634 int pref = 0; 635 HashSet<Placement> checked = new HashSet<Placement>(); 636 637 for (int slot = 1; slot < iResource.length; slot++) { 638 if ((slot % Constants.SLOTS_PER_DAY) == 0) continue; 639 for (Placement placement : iResource[slot]) { 640 for (Placement c : getPlacements(slot - 1, placement)) { 641 if (placement.variable().equals(c.variable()) || !checked.add(c)) continue; 642 double dist = Placement.getDistanceInMeters(getDistanceMetric(), c, placement); 643 if (dist > getDistanceMetric().getInstructorNoPreferenceLimit() && dist <= getDistanceMetric().getInstructorDiscouragedLimit()) 644 pref += Constants.sPreferenceLevelDiscouraged; 645 if (dist > getDistanceMetric().getInstructorDiscouragedLimit()) 646 pref += Constants.sPreferenceLevelStronglyDiscouraged; 647 } 648 } 649 } 650 651 if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) { 652 for (Lecture p1: assignedVariables()) { 653 TimeLocation t1 = (p1.getAssignment() == null ? null : p1.getAssignment().getTimeLocation()); 654 if (t1 == null) continue; 655 Placement before = null; 656 for (Lecture p2: assignedVariables()) { 657 if (p2.getAssignment() == null || p2.equals(p1)) continue; 658 TimeLocation t2 = p2.getAssignment().getTimeLocation(); 659 if (t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) continue; 660 if (t2.getStartSlot() + t2.getLength() < t1.getStartSlot()) { 661 int distanceInMinutes = Placement.getDistanceInMinutes(getDistanceMetric(), p1.getAssignment(), p2.getAssignment()); 662 if (distanceInMinutes > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (t1.getStartSlot() - t2.getStartSlot() - t2.getLength())) 663 pref += (iIgnoreDistances ? Constants.sPreferenceLevelStronglyDiscouraged : Constants.sPreferenceLevelProhibited); 664 else if (distanceInMinutes > Constants.SLOT_LENGTH_MIN * (t1.getStartSlot() - t2.getStartSlot() - t2.getLength())) 665 pref += Constants.sPreferenceLevelDiscouraged; 666 } 667 if (t2.getStartSlot() + t2.getLength() <= t1.getStartSlot()) { 668 if (before == null || before.getTimeLocation().getStartSlot() < t2.getStartSlot()) 669 before = p2.getAssignment(); 670 } 671 } 672 if (iUnavailabilities != null) { 673 for (Placement c: iUnavailabilities) { 674 TimeLocation t2 = c.getTimeLocation(); 675 if (t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) continue; 676 if (t2.getStartSlot() + t2.getLength() <= t1.getStartSlot()) { 677 if (before == null || before.getTimeLocation().getStartSlot() < t2.getStartSlot()) 678 before = c; 679 } 680 } 681 } 682 if (before != null && Placement.getDistanceInMinutes(getDistanceMetric(), before, p1.getAssignment()) > getDistanceMetric().getInstructorLongTravelInMinutes()) 683 pref += Constants.sPreferenceLevelStronglyDiscouraged; 684 } 685 } 686 687 return pref; 688 } 689 690 /** Worst back-to-back preference of this instructor */ 691 public int getWorstPreference() { 692 return Constants.sPreferenceLevelStronglyDiscouraged * (variables().size() - 1); 693 } 694 695 public String getPuid() { 696 return iPuid; 697 } 698 699 public boolean isIgnoreDistances() { 700 return iIgnoreDistances; 701 } 702 703 public void setIgnoreDistances(boolean ignDist) { 704 iIgnoreDistances = ignDist; 705 } 706 707 public Long getType() { 708 return iType; 709 } 710 711 public void setType(Long type) { 712 iType = type; 713 } 714}