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}