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