001 package net.sf.cpsolver.exam.model;
002
003 import java.util.HashSet;
004 import java.util.Iterator;
005 import java.util.Set;
006 import java.util.Vector;
007
008 import net.sf.cpsolver.ifs.model.Constraint;
009 import net.sf.cpsolver.ifs.model.Value;
010 /**
011 * A student.
012 * <br><br>
013 *
014 * @version
015 * ExamTT 1.1 (Examination Timetabling)<br>
016 * Copyright (C) 2008 Tomáš Müller<br>
017 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
018 * Lazenska 391, 76314 Zlin, Czech Republic<br>
019 * <br>
020 * This library is free software; you can redistribute it and/or
021 * modify it under the terms of the GNU Lesser General Public
022 * License as published by the Free Software Foundation; either
023 * version 2.1 of the License, or (at your option) any later version.
024 * <br><br>
025 * This library is distributed in the hope that it will be useful,
026 * but 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.
029 * <br><br>
030 * You should have received a copy of the GNU Lesser General Public
031 * License along with this library; if not, write to the Free Software
032 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
033 */
034 public class ExamStudent extends Constraint implements Comparable {
035 private HashSet[] iTable;
036 private HashSet[] iDayTable;
037 private boolean iAllowDirectConflicts = true;
038 private Vector iOwners = new Vector();
039 private boolean[] iAvailable = null;
040
041 /**
042 * Constructor
043 * @param model examination timetabling model
044 * @param id student unique id
045 */
046 public ExamStudent(ExamModel model, long id) {
047 super();
048 iAllowDirectConflicts = model.getProperties().getPropertyBoolean("Student.AllowDirectConflicts", iAllowDirectConflicts);
049 iAssignedVariables = null;
050 iId = id;
051 iTable = new HashSet[model.getNrPeriods()];
052 for (int i=0;i<iTable.length;i++) iTable[i]=new HashSet();
053 iDayTable = new HashSet[model.getNrDays()];
054 for (int i=0;i<iDayTable.length;i++) iDayTable[i]=new HashSet();
055 }
056
057 /**
058 * True if direct student conflicts are allowed for this student
059 */
060 public boolean isAllowDirectConflicts() {
061 return iAllowDirectConflicts;
062 }
063
064 /**
065 * Set whether direct student conflicts are allowed for this student
066 */
067 public void setAllowDirectConflicts(boolean allowDirectConflicts) {
068 iAllowDirectConflicts = allowDirectConflicts;
069 }
070 /**
071 * True if the given two exams can have a direct student conflict with this
072 * student, i.e., they can be placed at the same period.
073 * @param ex1 an exam
074 * @param ex2 an exam
075 * @return {@link ExamStudent#isAllowDirectConflicts()} and {@link Exam#isAllowDirectConflicts()} for
076 * both exams
077 */
078 public boolean canConflict(Exam ex1, Exam ex2) {
079 return isAllowDirectConflicts() && ex1.isAllowDirectConflicts() && ex2.isAllowDirectConflicts();
080 }
081
082 /**
083 * Exam(s) enrolled by the student that are scheduled in the given period
084 */
085 public Set getExams(ExamPeriod period) { return iTable[period.getIndex()]; }
086 /**
087 * Exam(s) enrolled by the student that are scheduled in the given day
088 */
089 public Set getExamsADay(ExamPeriod period) { return iDayTable[period.getDay()]; }
090 /**
091 * Exam(s) enrolled by the student that are scheduled in the given day
092 */
093 public Set getExamsADay(int day) { return iDayTable[day]; }
094
095 /**
096 * Compute conflicts between the given assignment of an exam and all the current assignments (of this student).
097 * Only not-allowed conflicts (see {@link ExamStudent#canConflict(Exam, Exam)} are considered.
098 */
099 public void computeConflicts(Value value, Set conflicts) {
100 ExamPlacement p = (ExamPlacement)value;
101 Exam ex = (Exam)p.variable();
102 for (Iterator i=iTable[p.getPeriod().getIndex()].iterator();i.hasNext();) {
103 Exam exam = (Exam)i.next();
104 if (!canConflict(ex,exam)) conflicts.add(exam.getAssignment());
105 }
106 }
107
108 /**
109 * Check whether there is a conflict between the given assignment of an exam and all the current assignments (of this student).
110 * Only not-allowed conflicts (see {@link ExamStudent#canConflict(Exam, Exam)} are considered.
111 */
112 public boolean inConflict(Value value) {
113 ExamPlacement p = (ExamPlacement)value;
114 Exam ex = (Exam)p.variable();
115 for (Iterator i=iTable[p.getPeriod().getIndex()].iterator();i.hasNext();) {
116 Exam exam = (Exam)i.next();
117 if (!canConflict(ex,exam)) return true;
118 }
119 return false;
120 }
121
122 /**
123 * True if the given exams can conflict (see {@link ExamStudent#canConflict(Exam, Exam)}),
124 * or if they are placed at different periods.
125 */
126 public boolean isConsistent(Value value1, Value value2) {
127 ExamPlacement p1 = (ExamPlacement)value1;
128 ExamPlacement p2 = (ExamPlacement)value2;
129 return (p1.getPeriod()!=p2.getPeriod() || canConflict((Exam)p1.variable(), (Exam)p2.variable()));
130 }
131
132 /**
133 * An exam was assigned, update student assignment table
134 */
135 public void afterAssigned(long iteration, Value value) {
136 ExamPlacement p = (ExamPlacement)value;
137 iTable[p.getPeriod().getIndex()].add(value.variable());
138 iDayTable[p.getPeriod().getDay()].add(value.variable());
139 }
140
141 /**
142 * An exam was unassigned, update student assignment table
143 */
144 public void afterUnassigned(long iteration, Value value) {
145 ExamPlacement p = (ExamPlacement)value;
146 iTable[p.getPeriod().getIndex()].remove(value.variable());
147 iDayTable[p.getPeriod().getDay()].remove(value.variable());
148 }
149
150 /**
151 * Compare two student for equality
152 */
153 public boolean equals(Object o) {
154 if (o==null || !(o instanceof ExamStudent)) return false;
155 ExamStudent s = (ExamStudent)o;
156 return getId()==s.getId();
157 }
158
159 /**
160 * Hash code
161 */
162 public int hashCode() {
163 return (int)(getId() ^ (getId() >>> 32));
164 }
165
166 /**
167 * Student unique id
168 */
169 public String toString() {
170 return String.valueOf(getId());
171 }
172
173 /**
174 * Compare two students (by student ids)
175 */
176 public int compareTo(Object o) {
177 return toString().compareTo(o.toString());
178 }
179
180 /**
181 * Constraint is hard if {@link ExamStudent#isAllowDirectConflicts()} is false.
182 */
183 public boolean isHard() {
184 return !isAllowDirectConflicts();
185 }
186
187 /**
188 * Courses and/or sections that this student is enrolled to
189 * @return list of {@link ExamOwner}
190 */
191 public Vector getOwners() {
192 return iOwners;
193 }
194
195 /**
196 * True if the student is available (for examination timetabling) during the given period
197 * @param period a period
198 * @return true if a student can attend an exam at the given period, false if otherwise
199 */
200 public boolean isAvailable(ExamPeriod period) {
201 return (iAvailable==null?true:iAvailable[period.getIndex()]);
202 }
203
204 /**
205 * Set whether the student is available (for examination timetabling) during the given period
206 * @param period a period
207 * @param available true if a student can attend an exam at the given period, false if otherwise
208 */
209 public void setAvailable(int period, boolean available) {
210 if (iAvailable==null) {
211 iAvailable = new boolean[iTable.length];
212 for (int i=0;i<iTable.length;i++) iAvailable[i]=true;
213 }
214 iAvailable[period]=available;
215 }
216
217 }