001 package net.sf.cpsolver.exam.model; 002 003 import java.util.ArrayList; 004 import java.util.HashSet; 005 import java.util.List; 006 import java.util.Set; 007 008 import net.sf.cpsolver.ifs.model.Constraint; 009 import net.sf.cpsolver.ifs.model.ConstraintListener; 010 import net.sf.cpsolver.ifs.util.DistanceMetric; 011 012 /** 013 * A room. Only one exam can use a room at a time (period). <br> 014 * <br> 015 * 016 * @version ExamTT 1.2 (Examination Timetabling)<br> 017 * Copyright (C) 2008 - 2010 Tomas Muller<br> 018 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 019 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 020 * <br> 021 * This library is free software; you can redistribute it and/or modify 022 * it under the terms of the GNU Lesser General Public License as 023 * published by the Free Software Foundation; either version 3 of the 024 * License, or (at your option) any later version. <br> 025 * <br> 026 * This library is distributed in the hope that it will be useful, but 027 * WITHOUT ANY WARRANTY; without even the implied warranty of 028 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 029 * Lesser General Public License for more details. <br> 030 * <br> 031 * You should have received a copy of the GNU Lesser General Public 032 * License along with this library; if not see 033 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 034 */ 035 public class ExamRoom extends Constraint<Exam, ExamPlacement> { 036 private List<ExamPlacement>[] iTable; 037 private boolean[] iAvailable; 038 private int[] iPenalty; 039 private String iName; 040 private int iSize, iAltSize; 041 private Double iCoordX, iCoordY; 042 043 /** 044 * Constructor 045 * 046 * @param model 047 * examination timetabling model 048 * @param id 049 * unique id 050 * @param size 051 * room (normal) seating capacity 052 * @param altSize 053 * room alternating seating capacity (to be used when 054 * {@link Exam#hasAltSeating()} is true) 055 * @param coordX 056 * x coordinate 057 * @param coordY 058 * y coordinate 059 */ 060 @SuppressWarnings("unchecked") 061 public ExamRoom(ExamModel model, long id, String name, int size, int altSize, Double coordX, Double coordY) { 062 super(); 063 iAssignedVariables = null; 064 iId = id; 065 iName = name; 066 iCoordX = coordX; 067 iCoordY = coordY; 068 iSize = size; 069 iAltSize = altSize; 070 iTable = new List[model.getNrPeriods()]; 071 iAvailable = new boolean[model.getNrPeriods()]; 072 iPenalty = new int[model.getNrPeriods()]; 073 for (int i = 0; i < iTable.length; i++) { 074 iTable[i] = new ArrayList<ExamPlacement>(); 075 iAvailable[i] = true; 076 iPenalty[i] = 0; 077 } 078 } 079 080 /** 081 * Distance between two rooms. See {@link DistanceMetric} 082 * 083 * @param other 084 * another room 085 * @return distance between this and the given room 086 */ 087 public double getDistanceInMeters(ExamRoom other) { 088 return ((ExamModel)getModel()).getDistanceMetric().getDistanceInMeters(getId(), getCoordX(), getCoordY(), other.getId(), other.getCoordX(), other.getCoordY()); 089 } 090 091 /** 092 * Normal seating capacity (to be used when {@link Exam#hasAltSeating()} is 093 * false) 094 */ 095 public int getSize() { 096 return iSize; 097 } 098 099 /** 100 * Alternating seating capacity (to be used when 101 * {@link Exam#hasAltSeating()} is true) 102 */ 103 public int getAltSize() { 104 return iAltSize; 105 } 106 107 /** 108 * X coordinate 109 */ 110 public Double getCoordX() { 111 return iCoordX; 112 } 113 114 /** 115 * Y coordinate 116 */ 117 public Double getCoordY() { 118 return iCoordY; 119 } 120 121 /** 122 * An exam placed at the given period. 123 * 124 * @param period 125 * a period 126 * @return a placement of an exam in this room at the given period, null if 127 * unused 128 * @deprecated If room sharing is allowed, this method only returns first exam. Use {@link ExamRoom#getPlacements(ExamPeriod)} instead. 129 */ 130 @Deprecated 131 public ExamPlacement getPlacement(ExamPeriod period) { 132 return (iTable[period.getIndex()].isEmpty() ? null : iTable[period.getIndex()].iterator().next()); 133 } 134 135 /** 136 * Exams placed at the given period 137 * 138 * @param period 139 * a period 140 * @return a placement of an exam in this room at the given period, null if 141 * unused (multiple placements can be returned if the room is shared between 142 * two or more exams) 143 */ 144 public List<ExamPlacement> getPlacements(ExamPeriod period) { 145 return iTable[period.getIndex()]; 146 } 147 148 /** 149 * True if the room is available (for examination timetabling) during the 150 * given period 151 * 152 * @param period 153 * a period 154 * @return true if an exam can be scheduled into this room at the given 155 * period, false if otherwise 156 */ 157 public boolean isAvailable(ExamPeriod period) { 158 return iAvailable[period.getIndex()]; 159 } 160 161 public boolean isAvailable(int period) { 162 return iAvailable[period]; 163 } 164 165 /** 166 * Set whether the room is available (for examination timetabling) during 167 * the given period 168 * 169 * @param period 170 * a period 171 * @param available 172 * true if an exam can be scheduled into this room at the given 173 * period, false if otherwise 174 */ 175 public void setAvailable(ExamPeriod period, boolean available) { 176 iAvailable[period.getIndex()] = available; 177 } 178 179 public void setAvailable(int period, boolean available) { 180 iAvailable[period] = available; 181 } 182 183 /** Return room penalty for given period */ 184 public int getPenalty(ExamPeriod period) { 185 return iPenalty[period.getIndex()]; 186 } 187 188 public int getPenalty(int period) { 189 return iPenalty[period]; 190 } 191 192 /** Set room penalty for given period */ 193 public void setPenalty(ExamPeriod period, int penalty) { 194 iPenalty[period.getIndex()] = penalty; 195 } 196 197 public void setPenalty(int period, int penalty) { 198 iPenalty[period] = penalty; 199 } 200 201 202 public ExamRoomSharing getRoomSharing() { 203 return ((ExamModel)getModel()).getRoomSharing(); 204 } 205 206 /** 207 * Compute conflicts between the given assignment of an exam and all the 208 * current assignments (of this room) 209 */ 210 @Override 211 public void computeConflicts(ExamPlacement p, Set<ExamPlacement> conflicts) { 212 if (!p.contains(this)) return; 213 214 if (getRoomSharing() == null) { 215 for (ExamPlacement conflict: iTable[p.getPeriod().getIndex()]) 216 if (!conflict.variable().equals(p.variable())) 217 conflicts.add(conflict); 218 } else { 219 getRoomSharing().computeConflicts(p, iTable[p.getPeriod().getIndex()], this, conflicts); 220 } 221 } 222 223 /** 224 * Checks whether there is a conflict between the given assignment of an 225 * exam and all the current assignments (of this room) 226 */ 227 @Override 228 public boolean inConflict(ExamPlacement p) { 229 if (!p.contains(this)) return false; 230 231 if (getRoomSharing() == null) { 232 for (ExamPlacement conflict: iTable[p.getPeriod().getIndex()]) 233 if (!conflict.variable().equals(p.variable())) return true; 234 return false; 235 } else { 236 return getRoomSharing().inConflict(p, iTable[p.getPeriod().getIndex()], this); 237 } 238 } 239 240 /** 241 * False if the given two assignments are using this room at the same period 242 */ 243 @Override 244 public boolean isConsistent(ExamPlacement p1, ExamPlacement p2) { 245 return (p1.getPeriod() != p2.getPeriod() || !p1.contains(this) || !p2.contains(this)); 246 } 247 248 /** 249 * An exam was assigned, update room assignment table 250 */ 251 @Override 252 public void assigned(long iteration, ExamPlacement p) { 253 if (p.contains(this) && !iTable[p.getPeriod().getIndex()].isEmpty()) { 254 HashSet<ExamPlacement> confs = new HashSet<ExamPlacement>(); 255 computeConflicts(p, confs); 256 for (ExamPlacement conf: confs) 257 conf.variable().unassign(iteration); 258 if (iConstraintListeners != null) { 259 for (ConstraintListener<ExamPlacement> listener : iConstraintListeners) 260 listener.constraintAfterAssigned(iteration, this, p, confs); 261 } 262 } 263 } 264 265 /** 266 * An exam was assigned, update room assignment table 267 */ 268 public void afterAssigned(long iteration, ExamPlacement p) { 269 if (p.contains(this)) 270 iTable[p.getPeriod().getIndex()].add(p); 271 } 272 273 /** 274 * An exam was unassigned, update room assignment table 275 */ 276 @Override 277 public void unassigned(long iteration, ExamPlacement p) { 278 // super.unassigned(iteration, p); 279 } 280 281 /** 282 * An exam was unassigned, update room assignment table 283 */ 284 public void afterUnassigned(long iteration, ExamPlacement p) { 285 if (p.contains(this)) { 286 iTable[p.getPeriod().getIndex()].remove(p); 287 } 288 } 289 290 /** 291 * Checks two rooms for equality 292 */ 293 @Override 294 public boolean equals(Object o) { 295 if (o == null || !(o instanceof ExamRoom)) 296 return false; 297 ExamRoom r = (ExamRoom) o; 298 return getId() == r.getId(); 299 } 300 301 /** 302 * Hash code 303 */ 304 @Override 305 public int hashCode() { 306 return (int) (getId() ^ (getId() >>> 32)); 307 } 308 309 /** 310 * Room name 311 */ 312 @Override 313 public String getName() { 314 return (hasName() ? iName : String.valueOf(getId())); 315 } 316 317 /** 318 * Room name 319 */ 320 public boolean hasName() { 321 return (iName != null && iName.length() > 0); 322 } 323 324 /** 325 * Room unique id 326 */ 327 @Override 328 public String toString() { 329 return getName(); 330 } 331 332 /** 333 * Compare two rooms (by unique id) 334 */ 335 @Override 336 public int compareTo(Constraint<Exam, ExamPlacement> o) { 337 return toString().compareTo(o.toString()); 338 } 339 }