    001package org.cpsolver.exam.model;
    002
    003import java.text.DecimalFormat;
    004import java.util.HashSet;
    005import java.util.Iterator;
    006import java.util.Set;
    007
    008import org.cpsolver.exam.criteria.ExamCriterion;
    009import org.cpsolver.ifs.assignment.Assignment;
    010import org.cpsolver.ifs.criteria.Criterion;
    011import org.cpsolver.ifs.model.Value;
    012
    013
    014/**
    015 * Representation of an exam placement (problem value), i.e., assignment of an
    016 * exam to period and room(s). Each placement has defined a period and a set of
    017 * rooms. The exam as well as the rooms have to be available during the given
    018 * period (see {@link Exam#getPeriodPlacements()} and
    019 * {@link Exam#getRoomPlacements()}). The total size of rooms have to be equal
    020 * or greater than the number of students enrolled in the exam
    021 * {@link Exam#getSize()}, using either {@link ExamRoom#getSize()} or
    022 * {@link ExamRoom#getAltSize()}, depending on {@link Exam#hasAltSeating()}.
    023 * Also, the number of rooms has to be smaller or equal to
    024 * {@link Exam#getMaxRooms()}. If {@link Exam#getMaxRooms()} is zero, the exam
    025 * is only to be assigned to period (the set of rooms is empty). <br>
    026 * <br>
    027 * <br>
    028 *
    029 * @version ExamTT 1.3 (Examination Timetabling)<br>
    030 *          Copyright (C) 2008 - 2014 Tomáš Müller<br>
    031 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    032 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    033 * <br>
    034 *          This library is free software; you can redistribute it and/or modify
    035 *          it under the terms of the GNU Lesser General Public License as
    036 *          published by the Free Software Foundation; either version 3 of the
    037 *          License, or (at your option) any later version. <br>
    038 * <br>
    039 *          This library is distributed in the hope that it will be useful, but
    040 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    041 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    042 *          Lesser General Public License for more details. <br>
    043 * <br>
    044 *          You should have received a copy of the GNU Lesser General Public
    045 *          License along with this library; if not see
    046 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
    047 */
    048public class ExamPlacement extends Value<Exam, ExamPlacement> {
    049    private ExamPeriodPlacement iPeriodPlacement;
    050    private Set<ExamRoomPlacement> iRoomPlacements;
    051
    052    private Integer iHashCode = null;
    053
    054    /**
    055     * Constructor
    056     *
    057     * @param exam
    058     *            an exam
    059     * @param periodPlacement
    060     *            period placement
    061     * @param roomPlacements
    062     *            a set of room placements {@link ExamRoomPlacement}
    063     */
    064    public ExamPlacement(Exam exam, ExamPeriodPlacement periodPlacement, Set<ExamRoomPlacement> roomPlacements) {
    065        super(exam);
    066        iPeriodPlacement = periodPlacement;
    067        if (roomPlacements == null)
    068            iRoomPlacements = new HashSet<ExamRoomPlacement>();
    069        else
    070            iRoomPlacements = roomPlacements;
    071    }
    072
    073    /**
    074     * Assigned period
    075     * @return assigned period
    076     */
    077    public ExamPeriod getPeriod() {
    078        return iPeriodPlacement.getPeriod();
    079    }
    080
    081    /**
    082     * Assigned period placement
    083     * @return assigned period placement
    084     */
    085    public ExamPeriodPlacement getPeriodPlacement() {
    086        return iPeriodPlacement;
    087    }
    088
    089    /**
    090     * Assigned rooms (it is empty when {@link Exam#getMaxRooms()} is zero)
    091     *
    092     * @return list of {@link ExamRoomPlacement}
    093     */
    094    public Set<ExamRoomPlacement> getRoomPlacements() {
    095        return iRoomPlacements;
    096    }
    097
    098    /**
    099     * Distance between two placements, i.e., maximal distance between a room of
    100     * this placement and a room of the given placement. Method
    101     * {@link ExamRoom#getDistanceInMeters(ExamRoom)} is used to get a distance between
    102     * two rooms.
    103     * @param other other placement
    104     * @return maximal distance to the other placement
    105     */
    106    public double getDistanceInMeters(ExamPlacement other) {
    107        if (getRoomPlacements().isEmpty() || other.getRoomPlacements().isEmpty())
    108            return 0;
    109        double maxDistance = 0;
    110        for (ExamRoomPlacement r1 : getRoomPlacements()) {
    111            for (ExamRoomPlacement r2 : other.getRoomPlacements()) {
    112                maxDistance = Math.max(maxDistance, r1.getDistanceInMeters(r2));
    113            }
    114        }
    115        return maxDistance;
    116    }
    117
    118    /**
    119     * Overall cost of using this placement.
    120     */
    121    @Override
    122    public double toDouble(Assignment<Exam, ExamPlacement> assignment) {
    123        double ret = 0.0;
    124        for (Criterion<Exam, ExamPlacement> criterion: variable().getModel().getCriteria())
    125            ret += criterion.getWeightedValue(assignment, this, null);
    126        return ret;
    127    }
    128
    129    /**
    130     * Overall cost of using this period.
    131     * @param assignment current assignment
    132     * @return sum of all criteria weighted {@link Criterion#getWeight()} period values {@link ExamCriterion#getPeriodValue(Assignment, ExamPlacement)}
    133     */
    134    public double getTimeCost(Assignment<Exam, ExamPlacement> assignment) {
    135        double weight = 0.0;
    136        for (Criterion<Exam, ExamPlacement> criterion: variable().getModel().getCriteria()) {
    137            if (((ExamCriterion)criterion).isPeriodCriterion())
    138                weight += criterion.getWeight() * ((ExamCriterion)criterion).getPeriodValue(assignment, this);
    139        }
    140        return weight;
    141    }
    142
    143    /**
    144     * Overall cost of using this set or rooms.
    145     * @param assignment current assignment
    146     * @return sum of all criteria weighted {@link Criterion#getWeight()} room values {@link ExamCriterion#getRoomValue(Assignment, ExamPlacement)}
    147     */
    148    public double getRoomCost(Assignment<Exam, ExamPlacement> assignment) {
    149        double weight = 0.0;
    150        for (Criterion<Exam, ExamPlacement> criterion: variable().getModel().getCriteria()) {
    151            if (((ExamCriterion)criterion).isRoomCriterion())
    152                weight += criterion.getWeight() * ((ExamCriterion)criterion).getRoomValue(assignment, this);
    153        }
    154        return weight;
    155    }
    156
    157    /**
    158     * Room names separated with the given delimiter
    159     * @param delim delimiter
    160     * @return delimiter separated list of room names
    161     */
    162    public String getRoomName(String delim) {
    163        String roomName = "";
    164        for (Iterator<ExamRoomPlacement> i = getRoomPlacements().iterator(); i.hasNext();) {
    165            ExamRoomPlacement r = i.next();
    166            roomName += r.getRoom().getName();
    167            if (i.hasNext())
    168                roomName += delim;
    169        }
    170        return roomName;
    171    }
    172
    173    /**
    174     * Assignment name (period / room(s))
    175     */
    176    @Override
    177    public String getName() {
    178        return getPeriod() + " " + getRoomName(",");
    179    }
    180
    181    /**
    182     * String representation -- returns a list of assignment costs
    183     * @param assignment current assignment
    184     * @return debug string
    185     */
    186    public String toString(Assignment<Exam, ExamPlacement> assignment) {
    187        String ret = "";
    188        for (Criterion<Exam, ExamPlacement> criterion: variable().getModel().getCriteria()) {
    189            String val = ((ExamCriterion)criterion).toString(assignment);
    190            if (!val.isEmpty())
    191                ret += (!ret.isEmpty() && !ret.endsWith(",") ? "," : "") + val;
    192        }
    193        return variable().getName() + " = " + getName() + " (" + new DecimalFormat("0.00").format(toDouble(assignment)) + "/" + ret + ")";
    194    }
    195
    196   /**
    197    * String representation -- returns a list of assignment costs
    198    */
    199   @Override
    200   public String toString() {
    201        return variable().getName() + " = " + getName();
    202    }
    203
    204    /**
    205     * Compare two assignments for equality
    206     */
    207    @Override
    208    public boolean equals(Object o) {
    209        if (o == null || !(o instanceof ExamPlacement))
    210            return false;
    211        ExamPlacement p = (ExamPlacement) o;
    212        return p.variable().equals(variable()) && p.getPeriod().equals(getPeriod())
    213                && p.getRoomPlacements().equals(getRoomPlacements());
    214    }
    215
    216    /**
    217     * Hash code
    218     */
    219    @Override
    220    public int hashCode() {
    221        if (iHashCode == null) iHashCode = getName().hashCode();
    222        return iHashCode;
    223    }
    224
    225    /**
    226     * True if given room is between {@link ExamPlacement#getRoomPlacements()}
    227     * @param room a room
    228     * @return true if this placement contains the given room
    229     */
    230    public boolean contains(ExamRoom room) {
    231        return getRoomPlacements().contains(new ExamRoomPlacement(room));
    232    }
    233}