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