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