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    }