001 package net.sf.cpsolver.coursett.model; 002 003 import java.util.Collection; 004 import java.util.Enumeration; 005 import java.util.HashSet; 006 import java.util.Hashtable; 007 import java.util.Iterator; 008 import java.util.Set; 009 010 import net.sf.cpsolver.coursett.constraint.JenrlConstraint; 011 012 /** 013 * Student. 014 * 015 * @version 016 * CourseTT 1.1 (University Course Timetabling)<br> 017 * Copyright (C) 2006 Tomáš Müller<br> 018 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 019 * Lazenska 391, 76314 Zlin, Czech Republic<br> 020 * <br> 021 * This library is free software; you can redistribute it and/or 022 * modify it under the terms of the GNU Lesser General Public 023 * License as published by the Free Software Foundation; either 024 * version 2.1 of the License, or (at your option) any later version. 025 * <br><br> 026 * This library is distributed in the hope that it will be useful, 027 * but 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. 030 * <br><br> 031 * You should have received a copy of the GNU Lesser General Public 032 * License along with this library; if not, write to the Free Software 033 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 034 */ 035 public class Student implements Comparable { 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 Hashtable iOfferings = new Hashtable(); 040 Set iLectures = new HashSet(); 041 Set iConfigurations = new HashSet(); 042 Hashtable iCanNotEnrollSections = null; 043 Hashtable iDistanceCache = null; 044 HashSet iCommitedPlacements = null; 045 046 public Student(Long studentId) { 047 iStudentId = studentId; 048 } 049 050 public void addOffering(Long offeringId, double weight) { 051 iOfferings.put(offeringId, new Double(weight)); 052 } 053 public Hashtable getOfferingsMap() { 054 return iOfferings; 055 } 056 public Set getOfferings() { 057 return iOfferings.keySet(); 058 } 059 public boolean hasOffering(Long offeringId) { 060 return iOfferings.containsKey(offeringId); 061 } 062 public double getOfferingWeight(Configuration configuration) { 063 if (configuration==null) return 1.0; 064 return getOfferingWeight(configuration.getOfferingId()); 065 } 066 067 public double getOfferingWeight(Long offeringId) { 068 Double weight = (Double)iOfferings.get(offeringId); 069 return (weight==null?0.0:weight.doubleValue()); 070 } 071 072 public boolean canEnroll(Lecture lecture) { 073 if (iCanNotEnrollSections!=null) { 074 HashSet canNotEnrollLectures = (HashSet)iCanNotEnrollSections.get(lecture.getConfiguration().getOfferingId()); 075 return canEnroll(canNotEnrollLectures, lecture, true); 076 } 077 return true; 078 } 079 080 private boolean canEnroll(HashSet canNotEnrollLectures, Lecture lecture, boolean checkParents) { 081 if (canNotEnrollLectures==null) return true; 082 if (canNotEnrollLectures.contains(lecture)) return false; 083 if (checkParents) { 084 Lecture parent = lecture.getParent(); 085 while (parent!=null) { 086 if (canNotEnrollLectures.contains(parent)) return false; 087 parent = parent.getParent(); 088 } 089 } 090 if (lecture.hasAnyChildren()) { 091 for (Enumeration e=lecture.getChildrenSubpartIds();e.hasMoreElements();) { 092 Long subpartId = (Long)e.nextElement(); 093 boolean canEnrollChild = false; 094 for (Enumeration f=lecture.getChildren(subpartId).elements();f.hasMoreElements();) { 095 Lecture childLecture = (Lecture)f.nextElement(); 096 if (canEnroll(canNotEnrollLectures, childLecture, false)) { 097 canEnrollChild = true; break; 098 } 099 } 100 if (!canEnrollChild) return false; 101 } 102 } 103 return true; 104 } 105 106 public void addCanNotEnroll(Lecture lecture) { 107 if (iCanNotEnrollSections==null) 108 iCanNotEnrollSections = new Hashtable(); 109 if (lecture.getConfiguration()==null) { 110 sLogger.warn("Student.addCanNotEnroll("+lecture+") -- given lecture has no configuration associated with."); 111 return; 112 } 113 HashSet canNotEnrollLectures = (HashSet)iCanNotEnrollSections.get(lecture.getConfiguration().getOfferingId()); 114 if (canNotEnrollLectures==null) { 115 canNotEnrollLectures = new HashSet(); 116 iCanNotEnrollSections.put(lecture.getConfiguration().getOfferingId(),canNotEnrollLectures); 117 } 118 canNotEnrollLectures.add(lecture); 119 } 120 121 public void addCanNotEnroll(Long offeringId, Collection lectures) { 122 if (lectures==null || lectures.isEmpty()) return; 123 if (iCanNotEnrollSections==null) 124 iCanNotEnrollSections = new Hashtable(); 125 HashSet canNotEnrollLectures = (HashSet)iCanNotEnrollSections.get(offeringId); 126 if (canNotEnrollLectures==null) { 127 canNotEnrollLectures = new HashSet(); 128 iCanNotEnrollSections.put(offeringId,canNotEnrollLectures); 129 } 130 canNotEnrollLectures.addAll(lectures); 131 } 132 133 public Hashtable canNotEnrollSections() { 134 return iCanNotEnrollSections; 135 } 136 137 public void addLecture(Lecture lecture) { iLectures.add(lecture); } 138 public void removeLecture(Lecture lecture) { iLectures.remove(lecture); } 139 public Set getLectures() { return iLectures; } 140 141 public void addConfiguration(Configuration config) { iConfigurations.add(config); } 142 public void removeConfiguration(Configuration config) { iConfigurations.remove(config); } 143 public Set getConfigurations() { return iConfigurations; } 144 145 public Long getId() { return iStudentId; } 146 147 public double getDistance(Student student) { 148 Double dist = (USE_DISTANCE_CACHE && iDistanceCache!=null?(Double)iDistanceCache.get(student):null); 149 if (dist==null) { 150 int same = 0; 151 for (Iterator i=getOfferings().iterator();i.hasNext();) { 152 if (student.getOfferings().contains(i.next())) same++; 153 } 154 double all = student.getOfferings().size() + getOfferings().size(); 155 double dif = all - 2.0*same; 156 dist = new Double(dif/all); 157 if (USE_DISTANCE_CACHE) { 158 if (iDistanceCache == null) iDistanceCache = new Hashtable(); 159 iDistanceCache.put(student,dist); 160 } 161 } 162 return dist.doubleValue(); 163 } 164 165 public void clearDistanceCache() { 166 if (USE_DISTANCE_CACHE && iDistanceCache!=null) iDistanceCache.clear(); 167 } 168 169 public String toString() { return String.valueOf(getId()); }//+"/"+getOfferings(); } 170 public int hashCode() { return getId().hashCode(); } 171 public int compareTo(Object o) { 172 return getId().compareTo(((Student)o).getId()); 173 } 174 public boolean equals(Object o) { 175 if (o==null || !(o instanceof Student)) return false; 176 return getId().equals(((Student)o).getId()); 177 } 178 179 public void addCommitedPlacement(Placement placement) { 180 if (iCommitedPlacements==null) 181 iCommitedPlacements = new HashSet(); 182 iCommitedPlacements.add(placement); 183 } 184 185 public Set getCommitedPlacements() { return iCommitedPlacements; } 186 187 public Set conflictPlacements(Placement placement) { 188 if (iCommitedPlacements==null) return null; 189 Set ret = new HashSet(); 190 Lecture lecture = (Lecture)placement.variable(); 191 for (Iterator i=iCommitedPlacements.iterator();i.hasNext();) { 192 Placement commitedPlacement = (Placement)i.next(); 193 Lecture commitedLecture = (Lecture)commitedPlacement.variable(); 194 if (lecture.getSchedulingSubpartId()!=null && lecture.getSchedulingSubpartId().equals(commitedLecture.getSchedulingSubpartId())) continue; 195 if (JenrlConstraint.isInConflict(commitedPlacement, placement)) 196 ret.add(commitedPlacement); 197 } 198 return ret; 199 } 200 201 public int countConflictPlacements(Placement placement) { 202 Set conflicts = conflictPlacements(placement); 203 double w = getOfferingWeight(((Lecture)placement.variable()).getConfiguration()); 204 return (int)Math.round(conflicts==null?0:avg(w,1.0)*conflicts.size()); 205 } 206 207 public double getJenrlWeight(Lecture l1, Lecture l2) { 208 return avg(getOfferingWeight(l1.getConfiguration()),getOfferingWeight(l2.getConfiguration())); 209 } 210 211 public double avg(double w1, double w2) { 212 return Math.sqrt(w1*w2); 213 } 214 }