001    package net.sf.cpsolver.coursett.model;
002    
003    import java.util.Collection;
004    import java.util.HashSet;
005    import java.util.HashMap;
006    import java.util.Map;
007    import java.util.Set;
008    
009    import net.sf.cpsolver.coursett.constraint.JenrlConstraint;
010    
011    /**
012     * Student.
013     * 
014     * @version CourseTT 1.2 (University Course Timetabling)<br>
015     *          Copyright (C) 2006 - 2010 Tomas Muller<br>
016     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
017     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
018     * <br>
019     *          This library is free software; you can redistribute it and/or modify
020     *          it under the terms of the GNU Lesser General Public License as
021     *          published by the Free Software Foundation; either version 3 of the
022     *          License, or (at your option) any later version. <br>
023     * <br>
024     *          This library is distributed in the hope that it will be useful, but
025     *          WITHOUT ANY WARRANTY; without even the implied warranty of
026     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
027     *          Lesser General Public License for more details. <br>
028     * <br>
029     *          You should have received a copy of the GNU Lesser General Public
030     *          License along with this library; if not see
031     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
032     */
033    public class Student implements Comparable<Student> {
034        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Student.class);
035        public static boolean USE_DISTANCE_CACHE = false;
036        Long iStudentId = null;
037        HashMap<Long, Double> iOfferings = new HashMap<Long, Double>();
038        Set<Lecture> iLectures = new HashSet<Lecture>();
039        Set<Configuration> iConfigurations = new HashSet<Configuration>();
040        HashMap<Long, Set<Lecture>> iCanNotEnrollSections = null;
041        HashMap<Student, Double> iDistanceCache = null;
042        HashSet<Placement> iCommitedPlacements = null;
043        private String iAcademicArea = null, iAcademicClassification = null, iMajor = null, iCurriculum = null;
044        HashMap<Long, Double> iOfferingPriority = new HashMap<Long, Double>();
045    
046        public Student(Long studentId) {
047            iStudentId = studentId;
048        }
049    
050        public void addOffering(Long offeringId, double weight, Double priority) {
051            iOfferings.put(offeringId, weight);
052            if (priority != null) iOfferingPriority.put(offeringId, priority);
053        }
054        
055        public void addOffering(Long offeringId, double weight) {
056            addOffering(offeringId, weight, null);
057        }
058    
059        public Map<Long, Double> getOfferingsMap() {
060            return iOfferings;
061        }
062    
063        public Set<Long> getOfferings() {
064            return iOfferings.keySet();
065        }
066    
067        public boolean hasOffering(Long offeringId) {
068            return iOfferings.containsKey(offeringId);
069        }
070        
071        /**
072         * Priority of an offering (for the student). Null if not used, or between
073         * zero (no priority) and one (highest priority)
074         */
075        public Double getPriority(Long offeringId) {
076            return offeringId == null ? null : iOfferingPriority.get(offeringId);
077        }
078        
079        public Double getPriority(Configuration configuration) {
080            return configuration == null ? null : getPriority(configuration.getOfferingId());
081        }
082        
083        public Double getPriority(Lecture lecture) {
084            return lecture == null ? null : getPriority(lecture.getConfiguration());
085        }
086        
087        public Double getConflictingPriorty(Lecture l1, Lecture l2) {
088            // Conflicting priority is the lower of the two priorities
089            Double p1 = getPriority(l1);
090            Double p2 = getPriority(l2);
091            return p1 == null ? null : p2 == null ? null : Math.min(p1, p2);
092        }
093    
094        public double getOfferingWeight(Configuration configuration) {
095            if (configuration == null)
096                return 1.0;
097            return getOfferingWeight(configuration.getOfferingId());
098        }
099    
100        public double getOfferingWeight(Long offeringId) {
101            Double weight = iOfferings.get(offeringId);
102            return (weight == null ? 0.0 : weight.doubleValue());
103        }
104    
105        public boolean canEnroll(Lecture lecture) {
106            if (iCanNotEnrollSections != null) {
107                Set<Lecture> canNotEnrollLectures = iCanNotEnrollSections.get(lecture.getConfiguration().getOfferingId());
108                return canEnroll(canNotEnrollLectures, lecture, true);
109            }
110            return true;
111        }
112    
113        private boolean canEnroll(Set<Lecture> canNotEnrollLectures, Lecture lecture, boolean checkParents) {
114            if (canNotEnrollLectures == null)
115                return true;
116            if (canNotEnrollLectures.contains(lecture))
117                return false;
118            if (checkParents) {
119                Lecture parent = lecture.getParent();
120                while (parent != null) {
121                    if (canNotEnrollLectures.contains(parent))
122                        return false;
123                    parent = parent.getParent();
124                }
125            }
126            if (lecture.hasAnyChildren()) {
127                for (Long subpartId: lecture.getChildrenSubpartIds()) {
128                    boolean canEnrollChild = false;
129                    for (Lecture childLecture : lecture.getChildren(subpartId)) {
130                        if (canEnroll(canNotEnrollLectures, childLecture, false)) {
131                            canEnrollChild = true;
132                            break;
133                        }
134                    }
135                    if (!canEnrollChild)
136                        return false;
137                }
138            }
139            return true;
140        }
141    
142        public void addCanNotEnroll(Lecture lecture) {
143            if (iCanNotEnrollSections == null)
144                iCanNotEnrollSections = new HashMap<Long, Set<Lecture>>();
145            if (lecture.getConfiguration() == null) {
146                sLogger.warn("Student.addCanNotEnroll(" + lecture
147                        + ") -- given lecture has no configuration associated with.");
148                return;
149            }
150            Set<Lecture> canNotEnrollLectures = iCanNotEnrollSections.get(lecture.getConfiguration().getOfferingId());
151            if (canNotEnrollLectures == null) {
152                canNotEnrollLectures = new HashSet<Lecture>();
153                iCanNotEnrollSections.put(lecture.getConfiguration().getOfferingId(), canNotEnrollLectures);
154            }
155            canNotEnrollLectures.add(lecture);
156        }
157    
158        public void addCanNotEnroll(Long offeringId, Collection<Lecture> lectures) {
159            if (lectures == null || lectures.isEmpty())
160                return;
161            if (iCanNotEnrollSections == null)
162                iCanNotEnrollSections = new HashMap<Long, Set<Lecture>>();
163            Set<Lecture> canNotEnrollLectures = iCanNotEnrollSections.get(offeringId);
164            if (canNotEnrollLectures == null) {
165                canNotEnrollLectures = new HashSet<Lecture>();
166                iCanNotEnrollSections.put(offeringId, canNotEnrollLectures);
167            }
168            canNotEnrollLectures.addAll(lectures);
169        }
170    
171        public Map<Long, Set<Lecture>> canNotEnrollSections() {
172            return iCanNotEnrollSections;
173        }
174    
175        public void addLecture(Lecture lecture) {
176            iLectures.add(lecture);
177        }
178    
179        public void removeLecture(Lecture lecture) {
180            iLectures.remove(lecture);
181        }
182    
183        public Set<Lecture> getLectures() {
184            return iLectures;
185        }
186    
187        public void addConfiguration(Configuration config) {
188            iConfigurations.add(config);
189        }
190    
191        public void removeConfiguration(Configuration config) {
192            iConfigurations.remove(config);
193        }
194    
195        public Set<Configuration> getConfigurations() {
196            return iConfigurations;
197        }
198    
199        public Long getId() {
200            return iStudentId;
201        }
202    
203        public double getDistance(Student student) {
204            Double dist = (USE_DISTANCE_CACHE && iDistanceCache != null ? iDistanceCache.get(student) : null);
205            if (dist == null) {
206                int same = 0;
207                for (Long o : getOfferings()) {
208                    if (student.getOfferings().contains(o))
209                        same++;
210                }
211                double all = student.getOfferings().size() + getOfferings().size();
212                double dif = all - 2.0 * same;
213                dist = new Double(dif / all);
214                if (USE_DISTANCE_CACHE) {
215                    if (iDistanceCache == null)
216                        iDistanceCache = new HashMap<Student, Double>();
217                    iDistanceCache.put(student, dist);
218                }
219            }
220            return dist.doubleValue();
221        }
222    
223        public void clearDistanceCache() {
224            if (USE_DISTANCE_CACHE && iDistanceCache != null)
225                iDistanceCache.clear();
226        }
227    
228        @Override
229        public String toString() {
230            return String.valueOf(getId());
231        }
232    
233        @Override
234        public int hashCode() {
235            return getId().hashCode();
236        }
237    
238        @Override
239        public int compareTo(Student s) {
240            return getId().compareTo(s.getId());
241        }
242    
243        @Override
244        public boolean equals(Object o) {
245            if (o == null || !(o instanceof Student))
246                return false;
247            return getId().equals(((Student) o).getId());
248        }
249    
250        public void addCommitedPlacement(Placement placement) {
251            if (iCommitedPlacements == null)
252                iCommitedPlacements = new HashSet<Placement>();
253            iCommitedPlacements.add(placement);
254        }
255    
256        public Set<Placement> getCommitedPlacements() {
257            return iCommitedPlacements;
258        }
259    
260        public Set<Placement> conflictPlacements(Placement placement) {
261            if (iCommitedPlacements == null)
262                return null;
263            Set<Placement> ret = new HashSet<Placement>();
264            Lecture lecture = placement.variable();
265            for (Placement commitedPlacement : iCommitedPlacements) {
266                Lecture commitedLecture = commitedPlacement.variable();
267                if (lecture.getSchedulingSubpartId() != null
268                        && lecture.getSchedulingSubpartId().equals(commitedLecture.getSchedulingSubpartId()))
269                    continue;
270                if (JenrlConstraint.isInConflict(commitedPlacement, placement, ((TimetableModel)placement.variable().getModel()).getDistanceMetric()))
271                    ret.add(commitedPlacement);
272            }
273            return ret;
274        }
275    
276        public int countConflictPlacements(Placement placement) {
277            Set<Placement> conflicts = conflictPlacements(placement);
278            double w = getOfferingWeight((placement.variable()).getConfiguration());
279            return (int) Math.round(conflicts == null ? 0 : avg(w, 1.0) * conflicts.size());
280        }
281    
282        public double getJenrlWeight(Lecture l1, Lecture l2) {
283            return avg(getOfferingWeight(l1.getConfiguration()), getOfferingWeight(l2.getConfiguration()));
284        }
285    
286        public double avg(double w1, double w2) {
287            return Math.sqrt(w1 * w2);
288        }
289        
290        public String getAcademicArea() {
291            return iAcademicArea;
292        }
293        
294        public void setAcademicArea(String acadArea) {
295            iAcademicArea = acadArea;
296        }
297        
298        public String getAcademicClassification() {
299            return iAcademicClassification;
300        }
301        
302        public void setAcademicClassification(String acadClasf) {
303            iAcademicClassification = acadClasf;
304        }
305        
306        public String getMajor() {
307            return iMajor;
308        }
309        
310        public void setMajor(String major) {
311            iMajor = major;
312        }
313        
314        public String getCurriculum() {
315            return iCurriculum;
316        }
317        
318        public void setCurriculum(String curriculum) {
319            iCurriculum = curriculum;
320        }
321    }