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