001 package net.sf.cpsolver.coursett.model; 002 003 import java.util.ArrayList; 004 import java.util.List; 005 006 import net.sf.cpsolver.coursett.Constants; 007 import net.sf.cpsolver.coursett.constraint.GroupConstraint; 008 import net.sf.cpsolver.coursett.constraint.InstructorConstraint; 009 import net.sf.cpsolver.coursett.constraint.SpreadConstraint; 010 import net.sf.cpsolver.coursett.preference.PreferenceCombination; 011 import net.sf.cpsolver.ifs.criteria.Criterion; 012 import net.sf.cpsolver.ifs.model.Value; 013 import net.sf.cpsolver.ifs.util.DistanceMetric; 014 import net.sf.cpsolver.ifs.util.ToolBox; 015 016 /** 017 * Placement (value). <br> 018 * <br> 019 * It combines room and time location 020 * 021 * @version CourseTT 1.2 (University Course Timetabling)<br> 022 * Copyright (C) 2006 - 2010 Tomas Muller<br> 023 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 024 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 025 * <br> 026 * This library is free software; you can redistribute it and/or modify 027 * it under the terms of the GNU Lesser General Public License as 028 * published by the Free Software Foundation; either version 3 of the 029 * License, or (at your option) any later version. <br> 030 * <br> 031 * This library is distributed in the hope that it will be useful, but 032 * WITHOUT ANY WARRANTY; without even the implied warranty of 033 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 034 * Lesser General Public License for more details. <br> 035 * <br> 036 * You should have received a copy of the GNU Lesser General Public 037 * License along with this library; if not see 038 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 039 */ 040 041 public class Placement extends Value<Lecture, Placement> { 042 private TimeLocation iTimeLocation; 043 private RoomLocation iRoomLocation; 044 private List<RoomLocation> iRoomLocations = null; 045 private Long iAssignmentId = null; 046 047 private int iHashCode = 0; 048 049 /** 050 * Constructor 051 * 052 * @param lecture 053 * lecture 054 * @param timeLocation 055 * time location 056 * @param roomLocation 057 * room location 058 */ 059 public Placement(Lecture lecture, TimeLocation timeLocation, RoomLocation roomLocation) { 060 super(lecture); 061 iTimeLocation = timeLocation; 062 iRoomLocation = roomLocation; 063 if (iRoomLocation == null) { 064 iRoomLocations = new ArrayList<RoomLocation>(0); 065 } 066 iHashCode = getName().hashCode(); 067 } 068 069 public Placement(Lecture lecture, TimeLocation timeLocation, java.util.List<RoomLocation> roomLocations) { 070 super(lecture); 071 iTimeLocation = timeLocation; 072 iRoomLocation = (roomLocations.isEmpty() ? null : (RoomLocation) roomLocations.get(0)); 073 if (roomLocations.size() != 1) { 074 iRoomLocations = new ArrayList<RoomLocation>(roomLocations); 075 } 076 iHashCode = getName().hashCode(); 077 } 078 079 /** Time location */ 080 public TimeLocation getTimeLocation() { 081 return iTimeLocation; 082 } 083 084 /** Room location */ 085 public RoomLocation getRoomLocation() { 086 return iRoomLocation; 087 } 088 089 /** Room locations (multi-room placement) */ 090 public List<RoomLocation> getRoomLocations() { 091 return iRoomLocations; 092 } 093 094 public List<Long> getBuildingIds() { 095 if (isMultiRoom()) { 096 List<Long> ret = new ArrayList<Long>(iRoomLocations.size()); 097 for (RoomLocation r : iRoomLocations) { 098 ret.add(r.getBuildingId()); 099 } 100 return ret; 101 } else { 102 List<Long> ret = new ArrayList<Long>(1); 103 ret.add(iRoomLocation.getBuildingId()); 104 return ret; 105 } 106 } 107 108 public List<Long> getRoomIds() { 109 if (isMultiRoom()) { 110 List<Long> ret = new ArrayList<Long>(iRoomLocations.size()); 111 for (RoomLocation r : iRoomLocations) { 112 ret.add(r.getId()); 113 } 114 return ret; 115 } else { 116 List<Long> ret = new ArrayList<Long>(1); 117 ret.add(iRoomLocation.getId()); 118 return ret; 119 } 120 } 121 122 public List<String> getRoomNames() { 123 if (isMultiRoom()) { 124 List<String> ret = new ArrayList<String>(iRoomLocations.size()); 125 for (RoomLocation r : iRoomLocations) { 126 ret.add(r.getName()); 127 } 128 return ret; 129 } else { 130 List<String> ret = new ArrayList<String>(1); 131 if (iRoomLocation != null) 132 ret.add(iRoomLocation.getName()); 133 return ret; 134 } 135 } 136 137 public List<Integer> getRoomPrefs() { 138 if (isMultiRoom()) { 139 List<Integer> ret = new ArrayList<Integer>(iRoomLocations.size()); 140 for (RoomLocation r : iRoomLocations) { 141 ret.add(r.getPreference()); 142 } 143 return ret; 144 } else { 145 List<Integer> ret = new ArrayList<Integer>(1); 146 if (iRoomLocation != null) 147 ret.add(iRoomLocation.getPreference()); 148 return ret; 149 } 150 } 151 152 public boolean isMultiRoom() { 153 return (iRoomLocations != null && iRoomLocations.size() != 1); 154 } 155 156 public RoomLocation getRoomLocation(Long roomId) { 157 if (isMultiRoom()) { 158 for (RoomLocation r : iRoomLocations) { 159 if (r.getId().equals(roomId)) 160 return r; 161 } 162 } else if (iRoomLocation != null && iRoomLocation.getId().equals(roomId)) 163 return iRoomLocation; 164 return null; 165 } 166 167 public boolean hasRoomLocation(Long roomId) { 168 if (isMultiRoom()) { 169 for (RoomLocation r : iRoomLocations) { 170 if (r.getId().equals(roomId)) 171 return true; 172 } 173 return false; 174 } else 175 return iRoomLocation != null && iRoomLocation.getId().equals(roomId); 176 } 177 178 public String getRoomName(String delim) { 179 if (isMultiRoom()) { 180 StringBuffer sb = new StringBuffer(); 181 for (RoomLocation r : iRoomLocations) { 182 if (sb.length() > 0) 183 sb.append(delim); 184 sb.append(r.getName()); 185 } 186 return sb.toString(); 187 } else { 188 return (getRoomLocation() == null ? "" : getRoomLocation().getName()); 189 } 190 } 191 192 @Override 193 public String getName() { 194 Lecture lecture = variable(); 195 return getTimeLocation().getName() + " " + getRoomName(", ") 196 + (lecture != null && lecture.getInstructorName() != null ? " " + lecture.getInstructorName() : ""); 197 } 198 199 public String getLongName() { 200 Lecture lecture = variable(); 201 if (isMultiRoom()) { 202 StringBuffer sb = new StringBuffer(); 203 for (RoomLocation r : iRoomLocations) { 204 if (sb.length() > 0) 205 sb.append(", "); 206 sb.append(r.getName()); 207 } 208 return getTimeLocation().getLongName() + " " + sb 209 + (lecture.getInstructorName() != null ? " " + lecture.getInstructorName() : ""); 210 } else 211 return getTimeLocation().getLongName() 212 + (getRoomLocation() == null ? "" : " " + getRoomLocation().getName()) 213 + (lecture.getInstructorName() != null ? " " + lecture.getInstructorName() : ""); 214 } 215 216 public boolean sameRooms(Placement placement) { 217 if (placement.isMultiRoom() != isMultiRoom()) 218 return false; 219 if (isMultiRoom()) { 220 if (placement.getRoomLocations().size() != getRoomLocations().size()) 221 return false; 222 return placement.getRoomLocations().containsAll(getRoomLocations()); 223 } else { 224 if (placement.getRoomLocation() == null) 225 return getRoomLocation() == null; 226 return placement.getRoomLocation().equals(getRoomLocation()); 227 } 228 } 229 230 public boolean shareRooms(Placement placement) { 231 if (isMultiRoom()) { 232 if (placement.isMultiRoom()) { 233 for (RoomLocation rl : getRoomLocations()) { 234 if (rl.getRoomConstraint() == null || !rl.getRoomConstraint().getConstraint()) 235 continue; 236 if (placement.getRoomLocations().contains(rl)) 237 return true; 238 } 239 return false; 240 } else { 241 return getRoomLocations().contains(placement.getRoomLocation()); 242 } 243 } else { 244 if (getRoomLocation().getRoomConstraint() == null || !getRoomLocation().getRoomConstraint().getConstraint()) 245 return false; 246 if (placement.isMultiRoom()) { 247 return placement.getRoomLocations().contains(getRoomLocation()); 248 } else { 249 return getRoomLocation().equals(placement.getRoomLocation()); 250 } 251 } 252 } 253 254 public int nrDifferentRooms(Placement placement) { 255 if (isMultiRoom()) { 256 int ret = 0; 257 for (RoomLocation r : getRoomLocations()) { 258 if (!placement.getRoomLocations().contains(r)) 259 ret++; 260 } 261 return ret; 262 } else { 263 return (placement.getRoomLocation().equals(getRoomLocation()) ? 0 : 1); 264 } 265 } 266 267 public int nrDifferentBuildings(Placement placement) { 268 if (isMultiRoom()) { 269 int ret = 0; 270 for (RoomLocation r : getRoomLocations()) { 271 boolean contains = false; 272 for (RoomLocation q : placement.getRoomLocations()) { 273 if (ToolBox.equals(r.getBuildingId(), q.getBuildingId())) 274 contains = true; 275 } 276 if (!contains) 277 ret++; 278 } 279 return ret; 280 } else { 281 return (ToolBox.equals(placement.getRoomLocation().getBuildingId(), getRoomLocation().getBuildingId()) ? 0 282 : 1); 283 } 284 } 285 286 public int sumRoomPreference() { 287 if (isMultiRoom()) { 288 int ret = 0; 289 for (RoomLocation r : getRoomLocations()) { 290 ret += r.getPreference(); 291 } 292 return ret; 293 } else { 294 return getRoomLocation().getPreference(); 295 } 296 } 297 298 public int getRoomPreference() { 299 if (isMultiRoom()) { 300 PreferenceCombination p = PreferenceCombination.getDefault(); 301 for (RoomLocation r : getRoomLocations()) { 302 p.addPreferenceInt(r.getPreference()); 303 } 304 return p.getPreferenceInt(); 305 } else { 306 return getRoomLocation().getPreference(); 307 } 308 } 309 310 public int getRoomSize() { 311 if (isMultiRoom()) { 312 if (getRoomLocations().isEmpty()) return 0; 313 int roomSize = Integer.MAX_VALUE; 314 for (RoomLocation r : getRoomLocations()) { 315 roomSize = Math.min(roomSize, r.getRoomSize()); 316 } 317 return roomSize; 318 } else { 319 return getRoomLocation().getRoomSize(); 320 } 321 } 322 323 public boolean isHard() { 324 if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(getTimeLocation().getPreference()))) 325 return true; 326 if (getRoomLocation() != null && Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(getRoomLocation().getPreference()))) 327 return true; 328 Lecture lecture = variable(); 329 for (GroupConstraint gc : lecture.hardGroupSoftConstraints()) { 330 if (gc.isSatisfied()) 331 continue; 332 if (Constants.sPreferenceProhibited.equals(gc.getPrologPreference())) 333 return true; 334 if (Constants.sPreferenceRequired.equals(gc.getPrologPreference())) 335 return true; 336 } 337 return false; 338 } 339 340 public boolean sameTime(Placement placement) { 341 return placement.getTimeLocation().equals(getTimeLocation()); 342 } 343 344 @Override 345 public boolean equals(Object object) { 346 if (object == null || !(object instanceof Placement)) 347 return false; 348 Placement placement = (Placement) object; 349 if (placement.getId() == getId()) 350 return true; // quick check 351 Lecture lecture = placement.variable(); 352 Lecture thisLecture = variable(); 353 if (lecture != null && thisLecture != null && !lecture.getClassId().equals(thisLecture.getClassId())) 354 return false; 355 if (!sameRooms(placement)) 356 return false; 357 if (!sameTime(placement)) 358 return false; 359 return true; 360 } 361 362 @Override 363 public int hashCode() { 364 return iHashCode; 365 } 366 367 @Override 368 public String toString() { 369 return variable().getName() + " " + getName(); 370 } 371 372 /** Distance between two placements */ 373 public static double getDistanceInMeters(DistanceMetric m, Placement p1, Placement p2) { 374 if (p1.isMultiRoom()) { 375 if (p2.isMultiRoom()) { 376 double dist = 0.0; 377 for (RoomLocation r1 : p1.getRoomLocations()) { 378 for (RoomLocation r2 : p2.getRoomLocations()) { 379 dist = Math.max(dist, r1.getDistanceInMeters(m, r2)); 380 } 381 } 382 return dist; 383 } else { 384 if (p2.getRoomLocation() == null) 385 return 0.0; 386 double dist = 0.0; 387 for (RoomLocation r1 : p1.getRoomLocations()) { 388 dist = Math.max(dist, r1.getDistanceInMeters(m, p2.getRoomLocation())); 389 } 390 return dist; 391 } 392 } else if (p2.isMultiRoom()) { 393 if (p1.getRoomLocation() == null) 394 return 0.0; 395 double dist = 0.0; 396 for (RoomLocation r2 : p2.getRoomLocations()) { 397 dist = Math.max(dist, p1.getRoomLocation().getDistanceInMeters(m, r2)); 398 } 399 return dist; 400 } else { 401 if (p1.getRoomLocation() == null || p2.getRoomLocation() == null) 402 return 0.0; 403 return p1.getRoomLocation().getDistanceInMeters(m, p2.getRoomLocation()); 404 } 405 } 406 407 /** Distance between two placements */ 408 public static int getDistanceInMinutes(DistanceMetric m, Placement p1, Placement p2) { 409 if (p1.isMultiRoom()) { 410 if (p2.isMultiRoom()) { 411 int dist = 0; 412 for (RoomLocation r1 : p1.getRoomLocations()) { 413 for (RoomLocation r2 : p2.getRoomLocations()) { 414 dist = Math.max(dist, r1.getDistanceInMinutes(m, r2)); 415 } 416 } 417 return dist; 418 } else { 419 if (p2.getRoomLocation() == null) 420 return 0; 421 int dist = 0; 422 for (RoomLocation r1 : p1.getRoomLocations()) { 423 dist = Math.max(dist, r1.getDistanceInMinutes(m, p2.getRoomLocation())); 424 } 425 return dist; 426 } 427 } else if (p2.isMultiRoom()) { 428 if (p1.getRoomLocation() == null) 429 return 0; 430 int dist = 0; 431 for (RoomLocation r2 : p2.getRoomLocations()) { 432 dist = Math.max(dist, p1.getRoomLocation().getDistanceInMinutes(m, r2)); 433 } 434 return dist; 435 } else { 436 if (p1.getRoomLocation() == null || p2.getRoomLocation() == null) 437 return 0; 438 return p1.getRoomLocation().getDistanceInMinutes(m, p2.getRoomLocation()); 439 } 440 } 441 442 public int getCommitedConflicts() { 443 int ret = 0; 444 Lecture lecture = variable(); 445 for (Student student : lecture.students()) { 446 ret += student.countConflictPlacements(this); 447 } 448 return ret; 449 } 450 451 public Long getAssignmentId() { 452 return iAssignmentId; 453 } 454 455 public void setAssignmentId(Long assignmentId) { 456 iAssignmentId = assignmentId; 457 } 458 459 public boolean canShareRooms(Placement other) { 460 return (variable()).canShareRoom(other.variable()); 461 } 462 463 public boolean isValid() { 464 Lecture lecture = variable(); 465 if (!lecture.isValid(this)) 466 return false; 467 for (InstructorConstraint ic : lecture.getInstructorConstraints()) { 468 if (!ic.isAvailable(lecture, this)) 469 return false; 470 } 471 if (lecture.getNrRooms() > 0) { 472 if (isMultiRoom()) { 473 for (RoomLocation roomLocation : getRoomLocations()) { 474 if (roomLocation.getRoomConstraint() != null 475 && !roomLocation.getRoomConstraint().isAvailable(lecture, getTimeLocation(), 476 lecture.getScheduler())) 477 return false; 478 } 479 } else { 480 if (getRoomLocation().getRoomConstraint() != null 481 && !getRoomLocation().getRoomConstraint().isAvailable(lecture, getTimeLocation(), 482 lecture.getScheduler())) 483 return false; 484 } 485 } 486 return true; 487 } 488 489 public String getNotValidReason() { 490 Lecture lecture = variable(); 491 String reason = lecture.getNotValidReason(this); 492 if (reason != null) 493 return reason; 494 for (InstructorConstraint ic : lecture.getInstructorConstraints()) { 495 if (!ic.isAvailable(lecture, this)) { 496 if (!ic.isAvailable(lecture, getTimeLocation())) 497 return "instructor " + ic.getName() + " not available at " + getTimeLocation().getLongName(); 498 else 499 return "placement " + getTimeLocation().getLongName() + " " + getRoomName(", ") 500 + " is too far for instructor " + ic.getName(); 501 } 502 } 503 if (lecture.getNrRooms() > 0) { 504 if (isMultiRoom()) { 505 for (RoomLocation roomLocation : getRoomLocations()) { 506 if (roomLocation.getRoomConstraint() != null 507 && !roomLocation.getRoomConstraint().isAvailable(lecture, getTimeLocation(), 508 lecture.getScheduler())) 509 return "room " + roomLocation.getName() + " not available at " 510 + getTimeLocation().getLongName(); 511 } 512 } else { 513 if (getRoomLocation().getRoomConstraint() != null 514 && !getRoomLocation().getRoomConstraint().isAvailable(lecture, getTimeLocation(), 515 lecture.getScheduler())) 516 return "room " + getRoomLocation().getName() + " not available at " 517 + getTimeLocation().getLongName(); 518 } 519 } 520 return reason; 521 } 522 523 public int getNrRooms() { 524 if (iRoomLocations != null) 525 return iRoomLocations.size(); 526 return (iRoomLocation == null ? 0 : 1); 527 } 528 529 public int getSpreadPenalty() { 530 int spread = 0; 531 for (SpreadConstraint sc : variable().getSpreadConstraints()) { 532 spread += sc.getPenalty(this); 533 } 534 return spread; 535 } 536 537 public int getMaxSpreadPenalty() { 538 int spread = 0; 539 for (SpreadConstraint sc : variable().getSpreadConstraints()) { 540 spread += sc.getMaxPenalty(this); 541 } 542 return spread; 543 } 544 545 @Override 546 public double toDouble() { 547 double ret = 0.0; 548 for (Criterion<Lecture, Placement> criterion: variable().getModel().getCriteria()) 549 ret += criterion.getWeightedValue(this, null); 550 return ret; 551 } 552 553 private transient Object iAssignment = null; 554 555 public Object getAssignment() { 556 return iAssignment; 557 } 558 559 public void setAssignment(Object assignment) { 560 iAssignment = assignment; 561 } 562 }