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 }