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