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    }