001    package net.sf.cpsolver.exam.model;
002    
003    import java.util.Enumeration;
004    import java.util.HashSet;
005    import java.util.Set;
006    
007    import net.sf.cpsolver.ifs.model.Constraint;
008    import net.sf.cpsolver.ifs.model.ConstraintListener;
009    import net.sf.cpsolver.ifs.model.Value;
010    
011    /**
012     * A room. Only one exam can use a room at a time (period). 
013     * <br><br>
014     * 
015     * @version
016     * ExamTT 1.1 (Examination Timetabling)<br>
017     * Copyright (C) 2008 Tomáš Müller<br>
018     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
019     * Lazenska 391, 76314 Zlin, Czech Republic<br>
020     * <br>
021     * This library is free software; you can redistribute it and/or
022     * modify it under the terms of the GNU Lesser General Public
023     * License as published by the Free Software Foundation; either
024     * version 2.1 of the License, or (at your option) any later version.
025     * <br><br>
026     * This library is distributed in the hope that it will be useful,
027     * but 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.
030     * <br><br>
031     * You should have received a copy of the GNU Lesser General Public
032     * License along with this library; if not, write to the Free Software
033     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
034     */
035    public class ExamRoom extends Constraint implements Comparable {
036        private ExamPlacement[] iTable;
037        private boolean[] iAvailable;
038        private int[] iPenalty;
039        private String iName;
040        private int iSize, iAltSize;
041        private int iCoordX, iCoordY;
042        
043        /**
044         * Constructor
045         * @param model examination timetabling model
046         * @param id unique id
047         * @param size room (normal) seating capacity
048         * @param altSize room alternating seating capacity (to be used when {@link Exam#hasAltSeating()} is true)
049         * @param coordX x coordinate
050         * @param coordY y coordinate
051         */
052        public ExamRoom(ExamModel model, long id, String name, int size, int altSize, int coordX, int coordY) {
053            super();
054            iAssignedVariables = null;
055            iId = id;
056            iName = name;
057            iCoordX = coordX; iCoordY = coordY; 
058            iSize = size; iAltSize = altSize;
059            iTable = new ExamPlacement[model.getNrPeriods()];
060            iAvailable = new boolean[model.getNrPeriods()];
061            iPenalty = new int[model.getNrPeriods()];
062            for (int i=0;i<iTable.length;i++) {
063                iTable[i]=null;
064                iAvailable[i]=true;
065                iPenalty[i]=0;
066            }
067        }
068        
069        /**
070         * Distance between two rooms. It is computed as Euclidean distance using the room coordinates,
071         * 1 unit equals to 10 meters.
072         * @param other another room
073         * @return distance between this and the given room
074         */
075        public int getDistance(ExamRoom other) {
076            if (getCoordX()<0 || getCoordY()<0 || other.getCoordX()<0 || other.getCoordY()<0) return 10000;
077            int dx = getCoordX()-other.getCoordX();
078            int dy = getCoordY()-other.getCoordY();
079            return (int)Math.sqrt(dx*dx+dy*dy);
080        }
081        
082        /**
083         * Normal seating capacity (to be used when {@link Exam#hasAltSeating()} is false)
084         */
085        public int getSize() { return iSize; }
086        /**
087         * Alternating seating capacity (to be used when {@link Exam#hasAltSeating()} is true)
088         */
089        public int getAltSize() { return iAltSize; }
090        /**
091         * X coordinate
092         */
093        public int getCoordX() { return iCoordX; }
094        /**
095         * Y coordinate
096         */
097        public int getCoordY() { return iCoordY; }
098        /**
099         * An exam placed at the given period
100         * @param period a period
101         * @return a placement of an exam in this room at the given period, null if unused 
102         */
103        public ExamPlacement getPlacement(ExamPeriod period) { return iTable[period.getIndex()]; }
104        /**
105         * True if the room is available (for examination timetabling) during the given period
106         * @param period a period
107         * @return true if an exam can be scheduled into this room at the given period, false if otherwise
108         */
109        public boolean isAvailable(ExamPeriod period) { return iAvailable[period.getIndex()]; }
110        public boolean isAvailable(int period) { return iAvailable[period]; }
111        /**
112         * Set whether the room is available (for examination timetabling) during the given period
113         * @param period a period
114         * @param available true if an exam can be scheduled into this room at the given period, false if otherwise
115         */
116        public void setAvailable(ExamPeriod period, boolean available) { iAvailable[period.getIndex()]=available; }
117        public void setAvailable(int period, boolean available) { iAvailable[period]=available; }
118        
119        /** Return room penalty for given period */
120        public int getPenalty(ExamPeriod period) { return iPenalty[period.getIndex()]; }
121        public int getPenalty(int period) { return iPenalty[period]; }
122        /** Set room penalty for given period */
123        public void setPenalty(ExamPeriod period, int penalty) { iPenalty[period.getIndex()]=penalty; }
124        public void setPenalty(int period, int penalty) { iPenalty[period]=penalty; }
125        
126        /**
127         * Compute conflicts between the given assignment of an exam and all the current assignments (of this room)
128         */
129        public void computeConflicts(Value value, Set conflicts) {
130            ExamPlacement p = (ExamPlacement)value;
131            if (!p.contains(this)) return; 
132            if (iTable[p.getPeriod().getIndex()]!=null && !iTable[p.getPeriod().getIndex()].variable().equals(value.variable()))
133                conflicts.add(iTable[p.getPeriod().getIndex()]);
134        }
135        
136        /**
137         * Checks whether there is a conflict between the given assignment of an exam and all the current assignments (of this room)
138         */
139        public boolean inConflict(Value value) {
140            ExamPlacement p = (ExamPlacement)value;
141            if (!p.contains(this)) return false; 
142            return iTable[p.getPeriod().getIndex()]!=null && !iTable[p.getPeriod().getIndex()].variable().equals(value.variable());
143        }
144        
145        /**
146         * False if the given two assignments are using this room at the same period 
147         */
148        public boolean isConsistent(Value value1, Value value2) {
149            ExamPlacement p1 = (ExamPlacement)value1;
150            ExamPlacement p2 = (ExamPlacement)value2;
151            return (p1.getPeriod()!=p2.getPeriod() || !p1.contains(this) || !p2.contains(this));
152        }
153        
154        /**
155         * An exam was assigned, update room assignment table
156         */
157        public void assigned(long iteration, Value value) {
158            ExamPlacement p = (ExamPlacement)value;
159            if (p.contains(this) && iTable[p.getPeriod().getIndex()]!=null) {
160                if (iConstraintListeners!=null) {
161                    HashSet confs = new HashSet();
162                    confs.add(iTable[p.getPeriod().getIndex()]);
163                    for (Enumeration e=iConstraintListeners.elements();e.hasMoreElements();)
164                        ((ConstraintListener)e.nextElement()).constraintAfterAssigned(iteration, this, value, confs);
165                }
166                iTable[p.getPeriod().getIndex()].variable().unassign(iteration);
167            }
168        }
169        
170        /**
171         * An exam was assigned, update room assignment table
172         */
173        public void afterAssigned(long iteration, Value value) {
174            ExamPlacement p = (ExamPlacement)value;
175            if (p.contains(this))
176                iTable[p.getPeriod().getIndex()] = p;
177        }
178        
179        /**
180         * An exam was unassigned, update room assignment table
181         */
182        public void unassigned(long iteration, Value value) {
183          //super.unassigned(iteration, value);
184        }
185    
186        /**
187         * An exam was unassigned, update room assignment table
188         */
189        public void afterUnassigned(long iteration, Value value) {
190            //super.unassigned(iteration, value);
191            ExamPlacement p = (ExamPlacement)value;
192            if (p.contains(this)) {
193                iTable[p.getPeriod().getIndex()] = null;
194            }
195        }
196        
197        /**
198         * Checks two rooms for equality
199         */
200        public boolean equals(Object o) {
201            if (o==null || !(o instanceof ExamRoom)) return false;
202            ExamRoom r = (ExamRoom)o;
203            return getId()==r.getId();
204        }
205        
206        /**
207         * Hash code
208         */
209        public int hashCode() {
210            return (int)(getId() ^ (getId() >>> 32));
211        }
212        
213        /**
214         * Room name
215         */
216        public String getName() {
217            return (hasName()?iName:String.valueOf(getId()));
218        }
219        
220        /**
221         * Room name
222         */
223        public boolean hasName() {
224            return (iName!=null && iName.length()>0);
225        }
226        
227        /**
228         * Room unique id
229         */
230        public String toString() {
231            return getName();
232        }
233        
234        /**
235         * Compare two rooms (by unique id)
236         */
237        public int compareTo(Object o) {
238            return toString().compareTo(o.toString());
239        }
240    }