001package org.cpsolver.studentsct.model; 002 003import java.util.ArrayList; 004import java.util.HashSet; 005import java.util.List; 006import java.util.Set; 007 008import org.cpsolver.ifs.assignment.Assignment; 009import org.cpsolver.ifs.model.Model; 010import org.cpsolver.studentsct.reservation.Reservation; 011 012 013 014/** 015 * Representation of an instructional offering. An offering contains id, name, 016 * the list of course offerings, and the list of possible configurations. See 017 * {@link Config} and {@link Course}. 018 * 019 * <br> 020 * <br> 021 * 022 * @version StudentSct 1.3 (Student Sectioning)<br> 023 * Copyright (C) 2007 - 2014 Tomas Muller<br> 024 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 025 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 026 * <br> 027 * This library is free software; you can redistribute it and/or modify 028 * it under the terms of the GNU Lesser General Public License as 029 * published by the Free Software Foundation; either version 3 of the 030 * License, or (at your option) any later version. <br> 031 * <br> 032 * This library is distributed in the hope that it will be useful, but 033 * WITHOUT ANY WARRANTY; without even the implied warranty of 034 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 035 * Lesser General Public License for more details. <br> 036 * <br> 037 * You should have received a copy of the GNU Lesser General Public 038 * License along with this library; if not see 039 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 040 */ 041public class Offering { 042 private long iId = -1; 043 private String iName = null; 044 private Model<Request, Enrollment> iModel = null; 045 private List<Config> iConfigs = new ArrayList<Config>(); 046 private List<Course> iCourses = new ArrayList<Course>(); 047 private List<Reservation> iReservations = new ArrayList<Reservation>(); 048 049 /** 050 * Constructor 051 * 052 * @param id 053 * instructional offering unique id 054 * @param name 055 * instructional offering name (this is usually the name of the 056 * controlling course) 057 */ 058 public Offering(long id, String name) { 059 iId = id; 060 iName = name; 061 } 062 063 /** Offering id 064 * @return instructional offering unique id 065 **/ 066 public long getId() { 067 return iId; 068 } 069 070 /** Offering name 071 * @return instructional offering name 072 **/ 073 public String getName() { 074 return iName; 075 } 076 077 /** Possible configurations 078 * @return instructional offering configurations 079 **/ 080 public List<Config> getConfigs() { 081 return iConfigs; 082 } 083 084 /** 085 * List of courses. One instructional offering can contain multiple courses 086 * (names under which it is offered) 087 * @return list of course offerings 088 */ 089 public List<Course> getCourses() { 090 return iCourses; 091 } 092 093 /** 094 * Return section of the given id, if it is part of one of this offering 095 * configurations. 096 * @param sectionId class unique id 097 * @return section of the given id 098 */ 099 public Section getSection(long sectionId) { 100 for (Config config : getConfigs()) { 101 for (Subpart subpart : config.getSubparts()) { 102 for (Section section : subpart.getSections()) { 103 if (section.getId() == sectionId) 104 return section; 105 } 106 } 107 } 108 return null; 109 } 110 111 /** Return course, under which the given student enrolls into this offering. 112 * @param student given student 113 * @return course of this offering requested by the student 114 **/ 115 public Course getCourse(Student student) { 116 if (getCourses().isEmpty()) 117 return null; 118 if (getCourses().size() == 1) 119 return getCourses().get(0); 120 for (Request request : student.getRequests()) { 121 if (request instanceof CourseRequest) { 122 for (Course course : ((CourseRequest) request).getCourses()) { 123 if (getCourses().contains(course)) 124 return course; 125 } 126 } 127 } 128 return getCourses().get(0); 129 } 130 131 /** Return set of instructional types, union over all configurations. 132 * @return set of instructional types 133 **/ 134 public Set<String> getInstructionalTypes() { 135 Set<String> instructionalTypes = new HashSet<String>(); 136 for (Config config : getConfigs()) { 137 for (Subpart subpart : config.getSubparts()) { 138 instructionalTypes.add(subpart.getInstructionalType()); 139 } 140 } 141 return instructionalTypes; 142 } 143 144 /** 145 * Return the list of all possible choices of the given instructional type 146 * for this offering. 147 * @param instructionalType instructional type 148 * @return set of choices of the given instructional type 149 */ 150 public Set<Choice> getChoices(String instructionalType) { 151 Set<Choice> choices = new HashSet<Choice>(); 152 for (Config config : getConfigs()) { 153 for (Subpart subpart : config.getSubparts()) { 154 if (!instructionalType.equals(subpart.getInstructionalType())) 155 continue; 156 choices.addAll(subpart.getChoices()); 157 } 158 } 159 return choices; 160 } 161 162 /** 163 * Return list of all subparts of the given isntructional type for this 164 * offering. 165 * @param instructionalType instructional type 166 * @return subpart of the given instructional type 167 */ 168 public Set<Subpart> getSubparts(String instructionalType) { 169 Set<Subpart> subparts = new HashSet<Subpart>(); 170 for (Config config : getConfigs()) { 171 for (Subpart subpart : config.getSubparts()) { 172 if (instructionalType.equals(subpart.getInstructionalType())) 173 subparts.add(subpart); 174 } 175 } 176 return subparts; 177 } 178 179 /** Minimal penalty from {@link Config#getMinPenalty()} 180 * @return minimal penalty 181 **/ 182 public double getMinPenalty() { 183 double min = Double.MAX_VALUE; 184 for (Config config : getConfigs()) { 185 min = Math.min(min, config.getMinPenalty()); 186 } 187 return (min == Double.MAX_VALUE ? 0.0 : min); 188 } 189 190 /** Maximal penalty from {@link Config#getMaxPenalty()} 191 * @return maximal penalty 192 **/ 193 public double getMaxPenalty() { 194 double max = Double.MIN_VALUE; 195 for (Config config : getConfigs()) { 196 max = Math.max(max, config.getMaxPenalty()); 197 } 198 return (max == Double.MIN_VALUE ? 0.0 : max); 199 } 200 201 @Override 202 public String toString() { 203 return iName; 204 } 205 206 /** Reservations associated with this offering 207 * @return reservations for this offering 208 **/ 209 public List<Reservation> getReservations() { return iReservations; } 210 211 /** True if there are reservations for this offering 212 * @return true if there is at least one reservation 213 **/ 214 public boolean hasReservations() { return !iReservations.isEmpty(); } 215 216 /** 217 * Total space in the offering that is not reserved by any reservation 218 * @return total unreserved space in the offering 219 **/ 220 public synchronized double getTotalUnreservedSpace() { 221 if (iTotalUnreservedSpace == null) 222 iTotalUnreservedSpace = getTotalUnreservedSpaceNoCache(); 223 return iTotalUnreservedSpace; 224 } 225 Double iTotalUnreservedSpace = null; 226 private double getTotalUnreservedSpaceNoCache() { 227 // compute overall available space 228 double available = 0.0; 229 for (Config config: getConfigs()) { 230 available += config.getLimit(); 231 // offering is unlimited -> there is unreserved space unless there is an unlimited reservation too 232 // (in which case there is no unreserved space) 233 if (config.getLimit() < 0) { 234 for (Reservation r: getReservations()) { 235 // ignore expired reservations 236 if (r.isExpired()) continue; 237 // there is an unlimited reservation -> no unreserved space 238 if (r.getLimit() < 0) return 0.0; 239 } 240 return Double.MAX_VALUE; 241 } 242 } 243 244 // compute maximal reserved space (out of the available space) 245 double reserved = 0; 246 for (Reservation r: getReservations()) { 247 // ignore expired reservations 248 if (r.isExpired()) continue; 249 // unlimited reservation -> no unreserved space 250 if (r.getLimit() < 0) return 0.0; 251 reserved += r.getLimit(); 252 } 253 254 return Math.max(0.0, available - reserved); 255 } 256 257 /** 258 * Available space in the offering that is not reserved by any reservation 259 * @param assignment current request 260 * @param excludeRequest excluding given request (if not null) 261 * @return remaining unreserved space in the offering 262 **/ 263 public double getUnreservedSpace(Assignment<Request, Enrollment> assignment, Request excludeRequest) { 264 // compute available space 265 double available = 0.0; 266 for (Config config: getConfigs()) { 267 available += config.getLimit() - config.getContext(assignment).getEnrollmentWeight(assignment, excludeRequest); 268 // offering is unlimited -> there is unreserved space unless there is an unlimited reservation too 269 // (in which case there is no unreserved space) 270 if (config.getLimit() < 0) { 271 for (Reservation r: getReservations()) { 272 // ignore expired reservations 273 if (r.isExpired()) continue; 274 // there is an unlimited reservation -> no unreserved space 275 if (r.getLimit() < 0) return 0.0; 276 } 277 return Double.MAX_VALUE; 278 } 279 } 280 281 // compute reserved space (out of the available space) 282 double reserved = 0; 283 for (Reservation r: getReservations()) { 284 // ignore expired reservations 285 if (r.isExpired()) continue; 286 // unlimited reservation -> no unreserved space 287 if (r.getLimit() < 0) return 0.0; 288 reserved += Math.max(0.0, r.getContext(assignment).getReservedAvailableSpace(assignment, excludeRequest)); 289 } 290 291 return available - reserved; 292 } 293 294 295 /** 296 * Clear reservation information that was cached on this offering or below 297 */ 298 public synchronized void clearReservationCache() { 299 for (Config c: getConfigs()) 300 c.clearReservationCache(); 301 for (Course c: getCourses()) 302 for (CourseRequest r: c.getRequests()) 303 r.clearReservationCache(); 304 iTotalUnreservedSpace = null; 305 } 306 307 @Override 308 public boolean equals(Object o) { 309 if (o == null || !(o instanceof Offering)) return false; 310 return getId() == ((Offering)o).getId(); 311 } 312 313 @Override 314 public int hashCode() { 315 return (int) (iId ^ (iId >>> 32)); 316 } 317 318 public Model<Request, Enrollment> getModel() { return iModel; } 319 public void setModel(Model<Request, Enrollment> model) { iModel = model; } 320}