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