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         * True if the room is available during at least one period,
167         * @return true if there is an examination period at which the room is available
168         */
169        public boolean isAvailable() {
170            for (boolean a: iAvailable)
171                if (a) return true;
172            return false;
173        }
174    
175        /**
176         * Set whether the room is available (for examination timetabling) during
177         * the given period
178         * 
179         * @param period
180         *            a period
181         * @param available
182         *            true if an exam can be scheduled into this room at the given
183         *            period, false if otherwise
184         */
185        public void setAvailable(ExamPeriod period, boolean available) {
186            iAvailable[period.getIndex()] = available;
187        }
188    
189        public void setAvailable(int period, boolean available) {
190            iAvailable[period] = available;
191        }
192    
193        /** Return room penalty for given period */
194        public int getPenalty(ExamPeriod period) {
195            return iPenalty[period.getIndex()];
196        }
197    
198        public int getPenalty(int period) {
199            return iPenalty[period];
200        }
201    
202        /** Set room penalty for given period */
203        public void setPenalty(ExamPeriod period, int penalty) {
204            iPenalty[period.getIndex()] = penalty;
205        }
206    
207        public void setPenalty(int period, int penalty) {
208            iPenalty[period] = penalty;
209        }
210        
211        
212        public ExamRoomSharing getRoomSharing() {
213            return ((ExamModel)getModel()).getRoomSharing();
214        }
215    
216        /**
217         * Compute conflicts between the given assignment of an exam and all the
218         * current assignments (of this room)
219         */
220        @Override
221        public void computeConflicts(ExamPlacement p, Set<ExamPlacement> conflicts) {
222            if (!p.contains(this)) return;
223            
224            if (getRoomSharing() == null) {
225                for (ExamPlacement conflict: iTable[p.getPeriod().getIndex()])
226                    if (!conflict.variable().equals(p.variable()))
227                        conflicts.add(conflict);
228            } else {
229                getRoomSharing().computeConflicts(p, iTable[p.getPeriod().getIndex()], this, conflicts);
230            }
231        }
232    
233        /**
234         * Checks whether there is a conflict between the given assignment of an
235         * exam and all the current assignments (of this room)
236         */
237        @Override
238        public boolean inConflict(ExamPlacement p) {
239            if (!p.contains(this)) return false;
240            
241            if (getRoomSharing() == null) {
242                for (ExamPlacement conflict: iTable[p.getPeriod().getIndex()])
243                    if (!conflict.variable().equals(p.variable())) return true;
244                return false;
245            } else {
246                return getRoomSharing().inConflict(p, iTable[p.getPeriod().getIndex()], this);
247            }
248        }
249    
250        /**
251         * False if the given two assignments are using this room at the same period
252         */
253        @Override
254        public boolean isConsistent(ExamPlacement p1, ExamPlacement p2) {
255            return (p1.getPeriod() != p2.getPeriod() || !p1.contains(this) || !p2.contains(this));
256        }
257    
258        /**
259         * An exam was assigned, update room assignment table
260         */
261        @Override
262        public void assigned(long iteration, ExamPlacement p) {
263            if (p.contains(this) && !iTable[p.getPeriod().getIndex()].isEmpty()) {
264                HashSet<ExamPlacement> confs = new HashSet<ExamPlacement>();
265                computeConflicts(p, confs);
266                for (ExamPlacement conf: confs)
267                    conf.variable().unassign(iteration);
268                if (iConstraintListeners != null) {
269                    for (ConstraintListener<ExamPlacement> listener : iConstraintListeners)
270                        listener.constraintAfterAssigned(iteration, this, p, confs);
271                }
272            }
273        }
274    
275        /**
276         * An exam was assigned, update room assignment table
277         */
278        public void afterAssigned(long iteration, ExamPlacement p) {
279            if (p.contains(this))
280                iTable[p.getPeriod().getIndex()].add(p);
281        }
282    
283        /**
284         * An exam was unassigned, update room assignment table
285         */
286        @Override
287        public void unassigned(long iteration, ExamPlacement p) {
288            // super.unassigned(iteration, p);
289        }
290    
291        /**
292         * An exam was unassigned, update room assignment table
293         */
294        public void afterUnassigned(long iteration, ExamPlacement p) {
295            if (p.contains(this)) {
296                iTable[p.getPeriod().getIndex()].remove(p);
297            }
298        }
299    
300        /**
301         * Checks two rooms for equality
302         */
303        @Override
304        public boolean equals(Object o) {
305            if (o == null || !(o instanceof ExamRoom))
306                return false;
307            ExamRoom r = (ExamRoom) o;
308            return getId() == r.getId();
309        }
310    
311        /**
312         * Hash code
313         */
314        @Override
315        public int hashCode() {
316            return (int) (getId() ^ (getId() >>> 32));
317        }
318    
319        /**
320         * Room name
321         */
322        @Override
323        public String getName() {
324            return (hasName() ? iName : String.valueOf(getId()));
325        }
326    
327        /**
328         * Room name
329         */
330        public boolean hasName() {
331            return (iName != null && iName.length() > 0);
332        }
333    
334        /**
335         * Room unique id
336         */
337        @Override
338        public String toString() {
339            return getName();
340        }
341    
342        /**
343         * Compare two rooms (by unique id)
344         */
345        @Override
346        public int compareTo(Constraint<Exam, ExamPlacement> o) {
347            return toString().compareTo(o.toString());
348        }
349    }