001package org.cpsolver.exam.model; 002 003import java.util.ArrayList; 004import java.util.HashSet; 005import java.util.List; 006import java.util.Set; 007 008import org.cpsolver.ifs.assignment.Assignment; 009import org.cpsolver.ifs.model.Constraint; 010 011 012/** 013 * An instructor. <br> 014 * <br> 015 * 016 * @version ExamTT 1.3 (Examination Timetabling)<br> 017 * Copyright (C) 2008 - 2014 Tomáš Müller<br> 018 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 019 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 020 * <br> 021 * This library is free software; you can redistribute it and/or modify 022 * it under the terms of the GNU Lesser General Public License as 023 * published by the Free Software Foundation; either version 3 of the 024 * License, or (at your option) any later version. <br> 025 * <br> 026 * This library is distributed in the hope that it will be useful, but 027 * 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. <br> 030 * <br> 031 * You should have received a copy of the GNU Lesser General Public 032 * License along with this library; if not see 033 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 034 */ 035public class ExamInstructor extends Constraint<Exam, ExamPlacement> { 036 private String iName; 037 private boolean iAllowDirectConflicts = true; 038 private List<ExamOwner> iOwners = new ArrayList<ExamOwner>(); 039 private boolean[] iAvailable = null; 040 041 public ExamInstructor(ExamModel model, long id, String name) { 042 super(); 043 iAllowDirectConflicts = model.getProperties().getPropertyBoolean("Instructor.AllowDirectConflicts", iAllowDirectConflicts); 044 iId = id; 045 iName = name; 046 setModel(model); 047 } 048 049 /** 050 * True when direct instructor conflicts are not allowed. 051 * @return true if direct conflicts are allowed 052 */ 053 public boolean isAllowDirectConflicts() { 054 return iAllowDirectConflicts; 055 } 056 057 /** 058 * Set to true when direct instructor conflicts are not allowed. 059 * @param allowDirectConflicts true if direct conflicts are allowed 060 */ 061 public void setAllowDirectConflicts(boolean allowDirectConflicts) { 062 iAllowDirectConflicts = allowDirectConflicts; 063 } 064 065 /** 066 * Exam(s) enrolled by the instructor that are scheduled in the given period 067 * @param assignment current assignment 068 * @param period given period 069 * @return exams that are associated with this instructor and placed in the given period 070 */ 071 public Set<Exam> getExams(Assignment<Exam, ExamPlacement> assignment, ExamPeriod period) { 072 Set<Exam> exams = ((ExamModel)getModel()).getInstructorsOfPeriod(assignment, period).get(this); 073 return (exams != null ? exams : new HashSet<Exam>()); 074 // return getContext(assignment).getExams(period.getIndex()); 075 } 076 077 /** 078 * Exam(s) enrolled by the instructor that are scheduled in the given day 079 * @param assignment current assignment 080 * @param period given period 081 * @return exams that are associated with this instructor and placed in the day of the given period 082 */ 083 public Set<Exam> getExamsADay(Assignment<Exam, ExamPlacement> assignment, ExamPeriod period) { 084 Set<Exam> exams = ((ExamModel)getModel()).getInstructorsOfDay(assignment, period).get(this); 085 return (exams != null ? exams : new HashSet<Exam>()); 086 // return getContext(assignment).getExamsOfDay(period.getDay()); 087 } 088 089 /** 090 * Exam(s) enrolled by the instructor that are scheduled in the given day 091 * @param assignment current assignment 092 * @param day given day 093 * @return exams that are associated with this instructor and placed in the given day 094 */ 095 public Set<Exam> getExamsADay(Assignment<Exam, ExamPlacement> assignment, int day) { 096 Set<Exam> exams = ((ExamModel)getModel()).getInstructorsOfDay(assignment, day).get(this); 097 return (exams != null ? exams : new HashSet<Exam>()); 098 // return getContext(assignment).getExamsOfDay(day); 099 } 100 101 /** 102 * Compute conflicts between the given assignment of an exam and all the 103 * current assignments (of this instructor). Only not-allowed conflicts (see 104 * {@link ExamInstructor#isAllowDirectConflicts()}) are considered. 105 */ 106 @Override 107 public void computeConflicts(Assignment<Exam, ExamPlacement> assignment, ExamPlacement p, Set<ExamPlacement> conflicts) { 108 if (isAllowDirectConflicts()) 109 return; 110 Set<Exam> exams = ((ExamModel)getModel()).getInstructorsOfPeriod(assignment, p.getPeriod()).get(this); 111 if (exams != null) 112 for (Exam exam : exams) { 113 conflicts.add(assignment.getValue(exam)); 114 } 115 } 116 117 /** 118 * Check whether there is a conflict between the given assignment of an exam 119 * and all the current assignments (of this instructor). Only not-allowed 120 * conflicts (see {@link ExamInstructor#isAllowDirectConflicts()}) are 121 * considered. 122 */ 123 @Override 124 public boolean inConflict(Assignment<Exam, ExamPlacement> assignment, ExamPlacement p) { 125 if (isAllowDirectConflicts()) 126 return false; 127 Set<Exam> exams = ((ExamModel)getModel()).getInstructorsOfPeriod(assignment, p.getPeriod()).get(this); 128 return exams != null && !exams.isEmpty(); 129 } 130 131 /** 132 * True if the given exams can conflict (see 133 * {@link ExamInstructor#isAllowDirectConflicts()}), or if they are placed 134 * at different periods. 135 */ 136 @Override 137 public boolean isConsistent(ExamPlacement p1, ExamPlacement p2) { 138 if (isAllowDirectConflicts()) 139 return true; 140 return (p1.getPeriod() != p2.getPeriod()); 141 } 142 143 /** 144 * Compare two instructors for equality 145 */ 146 @Override 147 public boolean equals(Object o) { 148 if (o == null || !(o instanceof ExamInstructor)) 149 return false; 150 ExamInstructor s = (ExamInstructor) o; 151 return getId() == s.getId(); 152 } 153 154 /** 155 * Hash code 156 */ 157 @Override 158 public int hashCode() { 159 return (int) (getId() ^ (getId() >>> 32)); 160 } 161 162 /** 163 * Instructor name 164 */ 165 @Override 166 public String getName() { 167 return hasName() ? iName : String.valueOf(getId()); 168 } 169 170 /** 171 * Instructor name 172 * @return true if instructor name is set and not empty 173 */ 174 public boolean hasName() { 175 return (iName != null && iName.length() > 0); 176 } 177 178 /** 179 * Instructor name 180 */ 181 @Override 182 public String toString() { 183 return getName(); 184 } 185 186 /** 187 * Compare two instructors (by instructor ids) 188 * @param o another instructor 189 * @return comparison 190 */ 191 public int compareTo(ExamInstructor o) { 192 return toString().compareTo(o.toString()); 193 } 194 195 /** 196 * Courses and/or sections that this instructor is enrolled to 197 * 198 * @return list of {@link ExamOwner} 199 */ 200 public List<ExamOwner> getOwners() { 201 return iOwners; 202 } 203 204 @Override 205 public boolean isHard() { 206 return !isAllowDirectConflicts(); 207 } 208 209 /** 210 * True if the student is available (for examination timetabling) during the 211 * given period 212 * 213 * @param period 214 * a period 215 * @return true if a student can attend an exam at the given period, false 216 * if otherwise 217 */ 218 public boolean isAvailable(ExamPeriod period) { 219 return (iAvailable == null ? true : iAvailable[period.getIndex()]); 220 } 221 222 /** 223 * Set whether the student is available (for examination timetabling) during 224 * the given period 225 * 226 * @param period 227 * a period 228 * @param available 229 * true if a student can attend an exam at the given period, 230 * false if otherwise 231 */ 232 public void setAvailable(int period, boolean available) { 233 if (iAvailable == null) { 234 iAvailable = new boolean[((ExamModel)getModel()).getNrPeriods()]; 235 for (int i = 0; i < iAvailable.length; i++) 236 iAvailable[i] = true; 237 } 238 iAvailable[period] = available; 239 } 240 241 /** 242 * True if the given two exams can have a direct instructor conflict with this 243 * instructor, i.e., they can be placed at the same period. 244 * 245 * @param ex1 246 * an exam 247 * @param ex2 248 * an exam 249 * @return {@link ExamStudent#isAllowDirectConflicts()} and 250 * {@link Exam#isAllowDirectConflicts()} for both exams 251 */ 252 public boolean canConflict(Exam ex1, Exam ex2) { 253 return isAllowDirectConflicts() && ex1.isAllowDirectConflicts() && ex2.isAllowDirectConflicts(); 254 } 255 256 /* 257 @Override 258 public Context createAssignmentContext(Assignment<Exam, ExamPlacement> assignment) { 259 return new Context(assignment); 260 } 261 262 public class Context implements AssignmentConstraintContext<Exam, ExamPlacement> { 263 private Set<Exam>[] iTable; 264 private Set<Exam>[] iDayTable; 265 266 @SuppressWarnings("unchecked") 267 public Context(Assignment<Exam, ExamPlacement> assignment) { 268 ExamModel model = (ExamModel)getModel(); 269 iTable = new Set[model.getNrPeriods()]; 270 for (int i = 0; i < iTable.length; i++) 271 iTable[i] = new HashSet<Exam>(); 272 iDayTable = new Set[model.getNrDays()]; 273 for (int i = 0; i < iDayTable.length; i++) 274 iDayTable[i] = new HashSet<Exam>(); 275 for (Exam exam: variables()) { 276 ExamPlacement placement = assignment.getValue(exam); 277 if (placement != null) 278 assigned(assignment, placement); 279 } 280 } 281 282 @Override 283 public void assigned(Assignment<Exam, ExamPlacement> assignment, ExamPlacement placement) { 284 iTable[placement.getPeriod().getIndex()].add(placement.variable()); 285 iDayTable[placement.getPeriod().getDay()].add(placement.variable()); 286 } 287 288 @Override 289 public void unassigned(Assignment<Exam, ExamPlacement> assignment, ExamPlacement placement) { 290 iTable[placement.getPeriod().getIndex()].remove(placement.variable()); 291 iDayTable[placement.getPeriod().getDay()].remove(placement.variable()); 292 } 293 294 public Set<Exam> getExams(int period) { return iTable[period]; } 295 296 public Set<Exam> getExamsOfDay(int day) { return iDayTable[day]; } 297 } 298 */ 299}