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