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 int iHashCode; 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 iHashCode = getName().hashCode(); 070 } 071 072 /** 073 * Assigned period 074 */ 075 public ExamPeriod getPeriod() { 076 return iPeriodPlacement.getPeriod(); 077 } 078 079 /** 080 * Assigned period placement 081 */ 082 public ExamPeriodPlacement getPeriodPlacement() { 083 return iPeriodPlacement; 084 } 085 086 /** 087 * Assigned rooms (it is empty when {@link Exam#getMaxRooms()} is zero) 088 * 089 * @return list of {@link ExamRoomPlacement} 090 */ 091 public Set<ExamRoomPlacement> getRoomPlacements() { 092 return iRoomPlacements; 093 } 094 095 /** 096 * Distance between two placements, i.e., maximal distance between a room of 097 * this placement and a room of the given placement. Method 098 * {@link ExamRoom#getDistanceInMeters(ExamRoom)} is used to get a distance between 099 * two rooms. 100 */ 101 public double getDistanceInMeters(ExamPlacement other) { 102 if (getRoomPlacements().isEmpty() || other.getRoomPlacements().isEmpty()) 103 return 0; 104 double maxDistance = 0; 105 for (ExamRoomPlacement r1 : getRoomPlacements()) { 106 for (ExamRoomPlacement r2 : other.getRoomPlacements()) { 107 maxDistance = Math.max(maxDistance, r1.getDistanceInMeters(r2)); 108 } 109 } 110 return maxDistance; 111 } 112 113 /** 114 * Overall cost of using this placement. 115 */ 116 @Override 117 public double toDouble() { 118 double ret = 0.0; 119 for (Criterion<Exam, ExamPlacement> criterion: variable().getModel().getCriteria()) 120 ret += criterion.getWeightedValue(this, null); 121 return ret; 122 } 123 124 /** 125 * Overall cost of using this period. 126 */ 127 public double getTimeCost() { 128 double weight = 0.0; 129 for (Criterion<Exam, ExamPlacement> criterion: variable().getModel().getCriteria()) { 130 if (((ExamCriterion)criterion).isPeriodCriterion()) 131 weight += criterion.getWeight() * ((ExamCriterion)criterion).getPeriodValue(this); 132 } 133 return weight; 134 } 135 136 /** 137 * Overall cost of using this set or rooms. 138 */ 139 public double getRoomCost() { 140 double weight = 0.0; 141 for (Criterion<Exam, ExamPlacement> criterion: variable().getModel().getCriteria()) { 142 if (((ExamCriterion)criterion).isRoomCriterion()) 143 weight += criterion.getWeight() * ((ExamCriterion)criterion).getRoomValue(this); 144 } 145 return weight; 146 } 147 148 /** 149 * Room names separated with the given delimiter 150 */ 151 public String getRoomName(String delim) { 152 String roomName = ""; 153 for (Iterator<ExamRoomPlacement> i = getRoomPlacements().iterator(); i.hasNext();) { 154 ExamRoomPlacement r = i.next(); 155 roomName += r.getRoom().getName(); 156 if (i.hasNext()) 157 roomName += delim; 158 } 159 return roomName; 160 } 161 162 /** 163 * Assignment name (period / room(s)) 164 */ 165 @Override 166 public String getName() { 167 return getPeriod() + " " + getRoomName(","); 168 } 169 170 /** 171 * String representation -- returns a list of assignment costs 172 */ 173 @Override 174 public String toString() { 175 String ret = ""; 176 for (Criterion<Exam, ExamPlacement> criterion: variable().getModel().getCriteria()) { 177 String val = criterion.toString(); 178 if (!val.isEmpty()) 179 ret += (!ret.isEmpty() && !ret.endsWith(",") ? "," : "") + val; 180 } 181 return variable().getName() + " = " + getName() + " (" + new DecimalFormat("0.00").format(toDouble()) + "/" + ret + ")"; 182 } 183 184 /** 185 * Compare two assignments for equality 186 */ 187 @Override 188 public boolean equals(Object o) { 189 if (o == null || !(o instanceof ExamPlacement)) 190 return false; 191 ExamPlacement p = (ExamPlacement) o; 192 return p.variable().equals(variable()) && p.getPeriod().equals(getPeriod()) 193 && p.getRoomPlacements().equals(getRoomPlacements()); 194 } 195 196 /** 197 * Hash code 198 */ 199 @Override 200 public int 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 }