001 package net.sf.cpsolver.exam.model;
002
003 import java.util.ArrayList;
004 import java.util.HashSet;
005 import java.util.List;
006 import java.util.Set;
007
008 import net.sf.cpsolver.ifs.model.Constraint;
009
010 /**
011 * A student. <br>
012 * <br>
013 *
014 * @version ExamTT 1.2 (Examination Timetabling)<br>
015 * Copyright (C) 2008 - 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 ExamStudent extends Constraint<Exam, ExamPlacement> {
034 private Set<Exam>[] iTable;
035 private Set<Exam>[] iDayTable;
036 private boolean iAllowDirectConflicts = true;
037 private List<ExamOwner> iOwners = new ArrayList<ExamOwner>();
038 private boolean[] iAvailable = null;
039
040 /**
041 * Constructor
042 *
043 * @param model
044 * examination timetabling model
045 * @param id
046 * student unique id
047 */
048 @SuppressWarnings("unchecked")
049 public ExamStudent(ExamModel model, long id) {
050 super();
051 iAllowDirectConflicts = model.getProperties().getPropertyBoolean("Student.AllowDirectConflicts",
052 iAllowDirectConflicts);
053 iAssignedVariables = null;
054 iId = id;
055 iTable = new Set[model.getNrPeriods()];
056 for (int i = 0; i < iTable.length; i++)
057 iTable[i] = new HashSet<Exam>();
058 iDayTable = new Set[model.getNrDays()];
059 for (int i = 0; i < iDayTable.length; i++)
060 iDayTable[i] = new HashSet<Exam>();
061 }
062
063 /**
064 * True if direct student conflicts are allowed for this student
065 */
066 public boolean isAllowDirectConflicts() {
067 return iAllowDirectConflicts;
068 }
069
070 /**
071 * Set whether direct student conflicts are allowed for this student
072 */
073 public void setAllowDirectConflicts(boolean allowDirectConflicts) {
074 iAllowDirectConflicts = allowDirectConflicts;
075 }
076
077 /**
078 * True if the given two exams can have a direct student conflict with this
079 * student, i.e., they can be placed at the same period.
080 *
081 * @param ex1
082 * an exam
083 * @param ex2
084 * an exam
085 * @return {@link ExamStudent#isAllowDirectConflicts()} and
086 * {@link Exam#isAllowDirectConflicts()} for both exams
087 */
088 public boolean canConflict(Exam ex1, Exam ex2) {
089 return isAllowDirectConflicts() && ex1.isAllowDirectConflicts() && ex2.isAllowDirectConflicts();
090 }
091
092 /**
093 * Exam(s) enrolled by the student that are scheduled in the given period
094 */
095 public Set<Exam> getExams(ExamPeriod period) {
096 return iTable[period.getIndex()];
097 }
098
099 /**
100 * Exam(s) enrolled by the student that are scheduled in the given day
101 */
102 public Set<Exam> getExamsADay(ExamPeriod period) {
103 return iDayTable[period.getDay()];
104 }
105
106 /**
107 * Exam(s) enrolled by the student that are scheduled in the given day
108 */
109 public Set<Exam> getExamsADay(int day) {
110 return iDayTable[day];
111 }
112
113 /**
114 * Compute conflicts between the given assignment of an exam and all the
115 * current assignments (of this student). Only not-allowed conflicts (see
116 * {@link ExamStudent#canConflict(Exam, Exam)} are considered.
117 */
118 @Override
119 public void computeConflicts(ExamPlacement p, Set<ExamPlacement> conflicts) {
120 Exam ex = p.variable();
121 for (Exam exam : iTable[p.getPeriod().getIndex()]) {
122 if (!canConflict(ex, exam))
123 conflicts.add(exam.getAssignment());
124 }
125 }
126
127 /**
128 * Check whether there is a conflict between the given assignment of an exam
129 * and all the current assignments (of this student). Only not-allowed
130 * conflicts (see {@link ExamStudent#canConflict(Exam, Exam)} are
131 * considered.
132 */
133 @Override
134 public boolean inConflict(ExamPlacement p) {
135 Exam ex = p.variable();
136 for (Exam exam : iTable[p.getPeriod().getIndex()]) {
137 if (!canConflict(ex, exam))
138 return true;
139 }
140 return false;
141 }
142
143 /**
144 * True if the given exams can conflict (see
145 * {@link ExamStudent#canConflict(Exam, Exam)}), or if they are placed at
146 * different periods.
147 */
148 @Override
149 public boolean isConsistent(ExamPlacement p1, ExamPlacement p2) {
150 return (p1.getPeriod() != p2.getPeriod() || canConflict(p1.variable(), p2.variable()));
151 }
152
153 /**
154 * An exam was assigned, update student assignment table
155 */
156 public void afterAssigned(long iteration, ExamPlacement value) {
157 ExamPlacement p = value;
158 iTable[p.getPeriod().getIndex()].add(value.variable());
159 iDayTable[p.getPeriod().getDay()].add(value.variable());
160 }
161
162 /**
163 * An exam was unassigned, update student assignment table
164 */
165 public void afterUnassigned(long iteration, ExamPlacement value) {
166 ExamPlacement p = value;
167 iTable[p.getPeriod().getIndex()].remove(value.variable());
168 iDayTable[p.getPeriod().getDay()].remove(value.variable());
169 }
170
171 /**
172 * Compare two student for equality
173 */
174 @Override
175 public boolean equals(Object o) {
176 if (o == null || !(o instanceof ExamStudent))
177 return false;
178 ExamStudent s = (ExamStudent) o;
179 return getId() == s.getId();
180 }
181
182 /**
183 * Hash code
184 */
185 @Override
186 public int hashCode() {
187 return (int) (getId() ^ (getId() >>> 32));
188 }
189
190 /**
191 * Student unique id
192 */
193 @Override
194 public String toString() {
195 return String.valueOf(getId());
196 }
197
198 /**
199 * Compare two students (by student ids)
200 */
201 @Override
202 public int compareTo(Constraint<Exam, ExamPlacement> o) {
203 return toString().compareTo(o.toString());
204 }
205
206 /**
207 * Constraint is hard if {@link ExamStudent#isAllowDirectConflicts()} is
208 * false.
209 */
210 @Override
211 public boolean isHard() {
212 return !isAllowDirectConflicts();
213 }
214
215 /**
216 * Courses and/or sections that this student is enrolled to
217 *
218 * @return list of {@link ExamOwner}
219 */
220 public List<ExamOwner> getOwners() {
221 return iOwners;
222 }
223
224 /**
225 * True if the student is available (for examination timetabling) during the
226 * given period
227 *
228 * @param period
229 * a period
230 * @return true if a student can attend an exam at the given period, false
231 * if otherwise
232 */
233 public boolean isAvailable(ExamPeriod period) {
234 return (iAvailable == null ? true : iAvailable[period.getIndex()]);
235 }
236
237 /**
238 * Set whether the student is available (for examination timetabling) during
239 * the given period
240 *
241 * @param period
242 * a period
243 * @param available
244 * true if a student can attend an exam at the given period,
245 * false if otherwise
246 */
247 public void setAvailable(int period, boolean available) {
248 if (iAvailable == null) {
249 iAvailable = new boolean[iTable.length];
250 for (int i = 0; i < iTable.length; i++)
251 iAvailable[i] = true;
252 }
253 iAvailable[period] = available;
254 }
255
256 }