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