001package net.sf.cpsolver.exam.model; 002 003import java.text.DecimalFormat; 004import java.util.HashSet; 005import java.util.Iterator; 006import java.util.Set; 007 008import net.sf.cpsolver.exam.criteria.ExamCriterion; 009import net.sf.cpsolver.ifs.criteria.Criterion; 010import 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 Tomáš Müller<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 */ 046public 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}