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