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    }