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 * A student. <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 ExamStudent extends Constraint<Exam, ExamPlacement> { 037 private boolean iAllowDirectConflicts = true; 038 private List<ExamOwner> iOwners = new ArrayList<ExamOwner>(); 039 private boolean[] iAvailable = null; 040 041 /** 042 * Constructor 043 * 044 * @param model 045 * examination timetabling model 046 * @param id 047 * student unique id 048 */ 049 public ExamStudent(ExamModel model, long id) { 050 super(); 051 iAllowDirectConflicts = model.getProperties().getPropertyBoolean("Student.AllowDirectConflicts", iAllowDirectConflicts); 052 iId = id; 053 setModel(model); 054 } 055 056 /** 057 * True if direct student conflicts are allowed for this student 058 * @return direct conflicts are allowed 059 */ 060 public boolean isAllowDirectConflicts() { 061 return iAllowDirectConflicts; 062 } 063 064 /** 065 * Set whether direct student conflicts are allowed for this student 066 * @param allowDirectConflicts direct conflicts are allowed 067 */ 068 public void setAllowDirectConflicts(boolean allowDirectConflicts) { 069 iAllowDirectConflicts = allowDirectConflicts; 070 } 071 072 /** 073 * True if the given two exams can have a direct student conflict with this 074 * student, i.e., they can be placed at the same period. 075 * 076 * @param ex1 077 * an exam 078 * @param ex2 079 * an exam 080 * @return {@link ExamStudent#isAllowDirectConflicts()} and 081 * {@link Exam#isAllowDirectConflicts()} for both exams 082 */ 083 public boolean canConflict(Exam ex1, Exam ex2) { 084 return isAllowDirectConflicts() && ex1.isAllowDirectConflicts() && ex2.isAllowDirectConflicts(); 085 } 086 087 /** 088 * Exam(s) enrolled by the student that are scheduled in the given period 089 * @param assignment current assignment 090 * @param period given period 091 * @return set of exams that this student is enrolled into and that are placed in the given period 092 */ 093 public Set<Exam> getExams(Assignment<Exam, ExamPlacement> assignment, ExamPeriod period) { 094 Set<Exam> exams = ((ExamModel)getModel()).getStudentsOfPeriod(assignment, period).get(this); 095 return (exams != null ? exams : new HashSet<Exam>()); 096 // return getContext(assignment).getExams(period.getIndex()); 097 } 098 099 /** 100 * Exam(s) enrolled by the student that are scheduled in the given day 101 * @param assignment current assignment 102 * @param period given period 103 * @return set of exams that this student is enrolled into and that are placed in the day of the given period 104 */ 105 public Set<Exam> getExamsADay(Assignment<Exam, ExamPlacement> assignment, ExamPeriod period) { 106 Set<Exam> exams = ((ExamModel)getModel()).getStudentsOfDay(assignment, period).get(this); 107 return (exams != null ? exams : new HashSet<Exam>()); 108 // return getContext(assignment).getExamsOfDay(period.getDay()); 109 } 110 111 /** 112 * Exam(s) enrolled by the student that are scheduled in the given day 113 * @param assignment current assignment 114 * @param day given day 115 * @return set of exams that this student is enrolled into and that are placed in the given day 116 */ 117 public Set<Exam> getExamsADay(Assignment<Exam, ExamPlacement> assignment, int day) { 118 Set<Exam> exams = ((ExamModel)getModel()).getStudentsOfDay(assignment, day).get(this); 119 return (exams != null ? exams : new HashSet<Exam>()); 120 // return getContext(assignment).getExamsOfDay(day); 121 } 122 123 /** 124 * Compute conflicts between the given assignment of an exam and all the 125 * current assignments (of this student). Only not-allowed conflicts (see 126 * {@link ExamStudent#canConflict(Exam, Exam)} are considered. 127 */ 128 @Override 129 public void computeConflicts(Assignment<Exam, ExamPlacement> assignment, ExamPlacement p, Set<ExamPlacement> conflicts) { 130 Exam ex = p.variable(); 131 Set<Exam> exams = ((ExamModel)getModel()).getStudentsOfPeriod(assignment, p.getPeriod()).get(this); 132 if (exams != null) 133 for (Exam exam : exams) { 134 if (!canConflict(ex, exam)) 135 conflicts.add(assignment.getValue(exam)); 136 } 137 } 138 139 /** 140 * Check whether there is a conflict between the given assignment of an exam 141 * and all the current assignments (of this student). Only not-allowed 142 * conflicts (see {@link ExamStudent#canConflict(Exam, Exam)} are 143 * considered. 144 */ 145 @Override 146 public boolean inConflict(Assignment<Exam, ExamPlacement> assignment, ExamPlacement p) { 147 Exam ex = p.variable(); 148 Set<Exam> exams = ((ExamModel)getModel()).getStudentsOfPeriod(assignment, p.getPeriod()).get(this); 149 if (exams != null) 150 for (Exam exam : exams) { 151 if (!canConflict(ex, exam)) 152 return true; 153 } 154 return false; 155 } 156 157 /** 158 * True if the given exams can conflict (see 159 * {@link ExamStudent#canConflict(Exam, Exam)}), or if they are placed at 160 * different periods. 161 */ 162 @Override 163 public boolean isConsistent(ExamPlacement p1, ExamPlacement p2) { 164 return (p1.getPeriod() != p2.getPeriod() || canConflict(p1.variable(), p2.variable())); 165 } 166 167 /** 168 * Compare two student for equality 169 */ 170 @Override 171 public boolean equals(Object o) { 172 if (o == null || !(o instanceof ExamStudent)) 173 return false; 174 ExamStudent s = (ExamStudent) o; 175 return getId() == s.getId(); 176 } 177 178 /** 179 * Hash code 180 */ 181 @Override 182 public int hashCode() { 183 return (int) (getId() ^ (getId() >>> 32)); 184 } 185 186 /** 187 * Student unique id 188 */ 189 @Override 190 public String toString() { 191 return String.valueOf(getId()); 192 } 193 194 /** 195 * Compare two students (by student ids) 196 */ 197 @Override 198 public int compareTo(Constraint<Exam, ExamPlacement> o) { 199 return toString().compareTo(o.toString()); 200 } 201 202 /** 203 * Constraint is hard if {@link ExamStudent#isAllowDirectConflicts()} is 204 * false. 205 */ 206 @Override 207 public boolean isHard() { 208 return !isAllowDirectConflicts(); 209 } 210 211 /** 212 * Courses and/or sections that this student is enrolled to 213 * 214 * @return list of {@link ExamOwner} 215 */ 216 public List<ExamOwner> getOwners() { 217 return iOwners; 218 } 219 220 /** 221 * True if the student is available (for examination timetabling) during the 222 * given period 223 * 224 * @param period 225 * a period 226 * @return true if a student can attend an exam at the given period, false 227 * if otherwise 228 */ 229 public boolean isAvailable(ExamPeriod period) { 230 return (iAvailable == null ? true : iAvailable[period.getIndex()]); 231 } 232 233 /** 234 * Set whether the student is available (for examination timetabling) during 235 * the given period 236 * 237 * @param period 238 * a period 239 * @param available 240 * true if a student can attend an exam at the given period, 241 * false if otherwise 242 */ 243 public void setAvailable(int period, boolean available) { 244 if (iAvailable == null) { 245 iAvailable = new boolean[((ExamModel)getModel()).getNrPeriods()]; 246 for (int i = 0; i < iAvailable.length; i++) 247 iAvailable[i] = true; 248 } 249 iAvailable[period] = available; 250 } 251 252 /* 253 @Override 254 public Context createAssignmentContext(Assignment<Exam, ExamPlacement> assignment) { 255 return new Context(assignment); 256 } 257 258 public class Context implements AssignmentConstraintContext<Exam, ExamPlacement> { 259 private Set<Exam>[] iTable; 260 private Set<Exam>[] iDayTable; 261 262 @SuppressWarnings("unchecked") 263 public Context(Assignment<Exam, ExamPlacement> assignment) { 264 ExamModel model = (ExamModel)getModel(); 265 iTable = new Set[model.getNrPeriods()]; 266 for (int i = 0; i < iTable.length; i++) 267 iTable[i] = new HashSet<Exam>(); 268 iDayTable = new Set[model.getNrDays()]; 269 for (int i = 0; i < iDayTable.length; i++) 270 iDayTable[i] = new HashSet<Exam>(); 271 for (Exam exam: variables()) { 272 ExamPlacement placement = assignment.getValue(exam); 273 if (placement != null) 274 assigned(assignment, placement); 275 } 276 } 277 278 @Override 279 public void assigned(Assignment<Exam, ExamPlacement> assignment, ExamPlacement placement) { 280 iTable[placement.getPeriod().getIndex()].add(placement.variable()); 281 iDayTable[placement.getPeriod().getDay()].add(placement.variable()); 282 } 283 284 @Override 285 public void unassigned(Assignment<Exam, ExamPlacement> assignment, ExamPlacement placement) { 286 iTable[placement.getPeriod().getIndex()].remove(placement.variable()); 287 iDayTable[placement.getPeriod().getDay()].remove(placement.variable()); 288 } 289 290 291 public Set<Exam> getExams(int period) { return iTable[period]; } 292 293 public Set<Exam> getExamsOfDay(int day) { return iDayTable[day]; } 294 295 } 296 */ 297}