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