001 package net.sf.cpsolver.studentsct.model;
002
003 import java.text.DecimalFormat;
004 import java.util.Enumeration;
005 import java.util.Iterator;
006 import java.util.Set;
007
008 import net.sf.cpsolver.ifs.model.Value;
009 import net.sf.cpsolver.ifs.util.ToolBox;
010 import net.sf.cpsolver.studentsct.StudentSectioningModel;
011 import net.sf.cpsolver.studentsct.extension.DistanceConflict;
012
013 /**
014 * Representation of an enrollment of a student into a course. A student needs to
015 * be enrolled in a section of each subpart of a selected configuration. When
016 * parent-child relation is defined among sections, if a student is enrolled
017 * in a section that has a parent section defined, he/she has be enrolled in
018 * the parent section as well. Also, the selected sections cannot overlap in time.
019 * <br><br>
020 *
021 * @version
022 * StudentSct 1.1 (Student Sectioning)<br>
023 * Copyright (C) 2007 Tomáš Müller<br>
024 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
025 * Lazenska 391, 76314 Zlin, Czech Republic<br>
026 * <br>
027 * This library is free software; you can redistribute it and/or
028 * modify it under the terms of the GNU Lesser General Public
029 * License as published by the Free Software Foundation; either
030 * version 2.1 of the License, or (at your option) any later version.
031 * <br><br>
032 * This library is distributed in the hope that it will be useful,
033 * but 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.
036 * <br><br>
037 * You should have received a copy of the GNU Lesser General Public
038 * License along with this library; if not, write to the Free Software
039 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
040 */
041
042 public class Enrollment extends Value {
043 private static DecimalFormat sDF = new DecimalFormat("0.000");
044 private Request iRequest = null;
045 private Config iConfig = null;
046 private Set iAssignments = null;
047 private Double iCachedPenalty = null;
048 private Double iCachedDoubleValue = null;
049
050 public static double sPriorityWeight = 0.90;
051 public static double sAlterativeWeight = 1.0;
052 public static double sInitialWeight = 1.2;
053 public static double sSelectedWeight = 1.1;
054 public static double sWaitlistedWeight = 1.01;
055 public static double sMinWeight = 0.0001;
056 public static double sNormPenalty = 5.0;
057 public static double sDistConfWeight = 0.95;
058
059 /** Constructor
060 * @param request course / free time request
061 * @param value value (1.0 for primary course, 0.5 for the first alternative, etc.)
062 * @param config selected configuration
063 * @param assignments valid list of sections
064 */
065 public Enrollment(Request request, double value, Config config, Set assignments) {
066 super(request);
067 iRequest = request;
068 iConfig = config;
069 iAssignments = assignments;
070 iValue = value;
071 }
072
073 /** Student */
074 public Student getStudent() {
075 return iRequest.getStudent();
076 }
077
078 /** Request */
079 public Request getRequest() {
080 return iRequest;
081 }
082
083 /** True if the request is course request */
084 public boolean isCourseRequest() {
085 return iConfig!=null;
086 }
087
088 /** Offering of the course request */
089 public Offering getOffering() {
090 return (iConfig==null?null:iConfig.getOffering());
091 }
092
093 /** Config of the course request */
094 public Config getConfig() {
095 return iConfig;
096 }
097
098 /** List of assignments (selected sections) */
099 public Set getAssignments() {
100 return iAssignments;
101 }
102
103 /** True when this enrollment is overlapping with the given enrollment */
104 public boolean isOverlapping(Enrollment enrl) {
105 if (enrl==null) return false;
106 for (Iterator i=enrl.getAssignments().iterator();i.hasNext();) {
107 Assignment assignment = (Assignment)i.next();
108 if (assignment.isOverlapping(getAssignments())) return true;
109 }
110 return false;
111 }
112
113 /** Percent of sections that are wait-listed */
114 public double percentWaitlisted() {
115 if (!isCourseRequest()) return 0.0;
116 CourseRequest courseRequest = (CourseRequest)getRequest();
117 int nrWaitlisted = 0;
118 for (Iterator i=getAssignments().iterator();i.hasNext();) {
119 Section section = (Section)i.next();
120 if (courseRequest.isWaitlisted(section))
121 nrWaitlisted++;
122 }
123 return ((double)nrWaitlisted)/getAssignments().size();
124 }
125
126 /** Percent of sections that are selected */
127 public double percentSelected() {
128 if (!isCourseRequest()) return 0.0;
129 CourseRequest courseRequest = (CourseRequest)getRequest();
130 int nrSelected = 0;
131 for (Iterator i=getAssignments().iterator();i.hasNext();) {
132 Section section = (Section)i.next();
133 if (courseRequest.isSelected(section))
134 nrSelected++;
135 }
136 return ((double)nrSelected)/getAssignments().size();
137 }
138
139 /** Percent of sections that are initial */
140 public double percentInitial() {
141 if (!isCourseRequest()) return 0.0;
142 if (getRequest().getInitialAssignment()==null) return 0.0;
143 Enrollment inital = (Enrollment)getRequest().getInitialAssignment();
144 int nrInitial = 0;
145 for (Iterator i=getAssignments().iterator();i.hasNext();) {
146 Section section = (Section)i.next();
147 if (inital.getAssignments().contains(section))
148 nrInitial++;
149 }
150 return ((double)nrInitial)/getAssignments().size();
151 }
152
153 /** True if all the sections are wait-listed */
154 public boolean isWaitlisted() {
155 if (!isCourseRequest()) return false;
156 CourseRequest courseRequest = (CourseRequest)getRequest();
157 for (Iterator i=getAssignments().iterator();i.hasNext();) {
158 Section section = (Section)i.next();
159 if (!courseRequest.isWaitlisted(section)) return false;
160 }
161 return true;
162 }
163
164 /** True if all the sections are selected */
165 public boolean isSelected() {
166 if (!isCourseRequest()) return false;
167 CourseRequest courseRequest = (CourseRequest)getRequest();
168 for (Iterator i=getAssignments().iterator();i.hasNext();) {
169 Section section = (Section)i.next();
170 if (!courseRequest.isSelected(section)) return false;
171 }
172 return true;
173 }
174
175 /** Enrollment penalty -- sum of section penalties (see {@link Section#getPenalty()}) */
176 public double getPenalty() {
177 if (iCachedPenalty==null) {
178 double penalty = 0.0;
179 if (isCourseRequest()) {
180 for (Iterator i=getAssignments().iterator();i.hasNext();) {
181 Section section = (Section)i.next();
182 penalty += section.getPenalty();
183 }
184 }
185 iCachedPenalty = new Double(penalty/getAssignments().size());
186 }
187 return iCachedPenalty.doubleValue();
188 }
189
190 /** Normalized enrollment penalty -- to be used in {@link Enrollment#toDouble()} */
191 public static double normalizePenalty(double penalty) {
192 return sNormPenalty/(sNormPenalty+penalty);
193 }
194
195 /** Enrollment value */
196 public double toDouble() {
197 return toDouble(nrDistanceConflicts());
198 }
199
200 /** Enrollment value */
201 public double toDouble(double nrDistanceConflicts) {
202 if (iCachedDoubleValue==null) {
203 iCachedDoubleValue = new Double(
204 -iValue *
205 Math.pow(sPriorityWeight,getRequest().getPriority()) *
206 (getRequest().isAlternative()?sAlterativeWeight:1.0) *
207 Math.pow(sInitialWeight,percentInitial()) *
208 Math.pow(sSelectedWeight,percentSelected()) *
209 Math.pow(sWaitlistedWeight,percentWaitlisted()) *
210 //Math.max(sMinWeight,getRequest().getWeight()) *
211 (getStudent().isDummy()?Student.sDummyStudentWeight:1.0) *
212 normalizePenalty(getPenalty())
213 );
214 }
215 return iCachedDoubleValue.doubleValue() *
216 Math.pow(sDistConfWeight,nrDistanceConflicts);
217 }
218
219 /** Enrollment name */
220 public String getName() {
221 if (getRequest() instanceof CourseRequest) {
222 Course course = null;
223 CourseRequest courseRequest = (CourseRequest)getRequest();
224 for (Enumeration e=courseRequest.getCourses().elements();e.hasMoreElements();) {
225 Course c = (Course)e.nextElement();
226 if (c.getOffering().getConfigs().contains(getConfig())) {
227 course = c; break;
228 }
229 }
230 String ret = (course==null?getConfig()==null?"":getConfig().getName():course.getName());
231 for (Iterator i=getAssignments().iterator();i.hasNext();) {
232 Section assignment = (Section)i.next();
233 ret+="\n "+assignment.getLongName()+(i.hasNext()?",":"");
234 }
235 return ret;
236 } else if (getRequest() instanceof FreeTimeRequest) {
237 return "Free Time "+((FreeTimeRequest)getRequest()).getTime().getLongName();
238 } else {
239 String ret = "";
240 for (Iterator i=getAssignments().iterator();i.hasNext();) {
241 Assignment assignment = (Section)i.next();
242 ret+=assignment.toString()+(i.hasNext()?",":"");
243 if (i.hasNext()) ret+="\n ";
244 }
245 return ret;
246 }
247 }
248
249 public String toString() {
250 String ret = sDF.format(toDouble())+"/"+sDF.format(getRequest().getBound())+(getPenalty()==0.0?"":"/"+sDF.format(getPenalty()));
251 if (getRequest() instanceof CourseRequest) {
252 ret+=" ";
253 for (Iterator i=getAssignments().iterator();i.hasNext();) {
254 Assignment assignment = (Assignment)i.next();
255 ret+=assignment+(i.hasNext()?", ":"");
256 }
257 }
258 return ret;
259 }
260
261 public boolean equals(Object o) {
262 if (o==null || !(o instanceof Enrollment)) return false;
263 Enrollment e = (Enrollment)o;
264 if (!ToolBox.equals(getConfig(), e.getConfig())) return false;
265 if (!ToolBox.equals(getRequest(),e.getRequest())) return false;
266 if (!ToolBox.equals(getAssignments(),e.getAssignments())) return false;
267 return true;
268 }
269
270 /** Number of distance conflicts, in which this enrollment is involved. */
271 public double nrDistanceConflicts() {
272 if (!isCourseRequest()) return 0;
273 if (getRequest().getModel() instanceof StudentSectioningModel) {
274 DistanceConflict dc = ((StudentSectioningModel)getRequest().getModel()).getDistanceConflict();
275 if (dc==null) return 0;
276 return dc.nrAllConflicts(this);
277 } else return 0;
278 }
279 }