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