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}