    001package net.sf.cpsolver.exam.model;
    002
    003import java.util.ArrayList;
    004import java.util.HashSet;
    005import java.util.List;
    006import java.util.Set;
    007
    008import net.sf.cpsolver.ifs.model.Constraint;
    009import net.sf.cpsolver.ifs.model.ConstraintListener;
    010import 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 Tomáš Müller<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 */
    035public 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}