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 (lecture.isToIgnoreStudentConflictsWith(commitedLecture)) continue; 271 if (JenrlConstraint.isInConflict(commitedPlacement, placement, ((TimetableModel)placement.variable().getModel()).getDistanceMetric())) 272 ret.add(commitedPlacement); 273 } 274 return ret; 275 } 276 277 public int countConflictPlacements(Placement placement) { 278 Set<Placement> conflicts = conflictPlacements(placement); 279 double w = getOfferingWeight((placement.variable()).getConfiguration()); 280 return (int) Math.round(conflicts == null ? 0 : avg(w, 1.0) * conflicts.size()); 281 } 282 283 public double getJenrlWeight(Lecture l1, Lecture l2) { 284 return avg(getOfferingWeight(l1.getConfiguration()), getOfferingWeight(l2.getConfiguration())); 285 } 286 287 public double avg(double w1, double w2) { 288 return Math.sqrt(w1 * w2); 289 } 290 291 public String getAcademicArea() { 292 return iAcademicArea; 293 } 294 295 public void setAcademicArea(String acadArea) { 296 iAcademicArea = acadArea; 297 } 298 299 public String getAcademicClassification() { 300 return iAcademicClassification; 301 } 302 303 public void setAcademicClassification(String acadClasf) { 304 iAcademicClassification = acadClasf; 305 } 306 307 public String getMajor() { 308 return iMajor; 309 } 310 311 public void setMajor(String major) { 312 iMajor = major; 313 } 314 315 public String getCurriculum() { 316 return iCurriculum; 317 } 318 319 public void setCurriculum(String curriculum) { 320 iCurriculum = curriculum; 321 } 322 }