001 package net.sf.cpsolver.studentsct.model;
002
003 import java.util.ArrayList;
004 import java.util.HashSet;
005 import java.util.List;
006 import java.util.Set;
007
008 import 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 Tomas Muller<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 */
038 public 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 }