001package net.sf.cpsolver.exam.model; 002 003import java.util.ArrayList; 004import java.util.HashSet; 005import java.util.List; 006import java.util.Set; 007 008import net.sf.cpsolver.ifs.model.Constraint; 009 010/** 011 * A student. <br> 012 * <br> 013 * 014 * @version ExamTT 1.2 (Examination Timetabling)<br> 015 * Copyright (C) 2008 - 2010 Tomáš Müller<br> 016 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 017 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 018 * <br> 019 * This library is free software; you can redistribute it and/or modify 020 * it under the terms of the GNU Lesser General Public License as 021 * published by the Free Software Foundation; either version 3 of the 022 * License, or (at your option) any later version. <br> 023 * <br> 024 * This library is distributed in the hope that it will be useful, but 025 * WITHOUT ANY WARRANTY; without even the implied warranty of 026 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 027 * Lesser General Public License for more details. <br> 028 * <br> 029 * You should have received a copy of the GNU Lesser General Public 030 * License along with this library; if not see 031 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 032 */ 033public class ExamStudent extends Constraint<Exam, ExamPlacement> { 034 private Set<Exam>[] iTable; 035 private Set<Exam>[] iDayTable; 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 @SuppressWarnings("unchecked") 049 public ExamStudent(ExamModel model, long id) { 050 super(); 051 iAllowDirectConflicts = model.getProperties().getPropertyBoolean("Student.AllowDirectConflicts", 052 iAllowDirectConflicts); 053 iAssignedVariables = null; 054 iId = id; 055 iTable = new Set[model.getNrPeriods()]; 056 for (int i = 0; i < iTable.length; i++) 057 iTable[i] = new HashSet<Exam>(); 058 iDayTable = new Set[model.getNrDays()]; 059 for (int i = 0; i < iDayTable.length; i++) 060 iDayTable[i] = new HashSet<Exam>(); 061 } 062 063 /** 064 * True if direct student conflicts are allowed for this student 065 */ 066 public boolean isAllowDirectConflicts() { 067 return iAllowDirectConflicts; 068 } 069 070 /** 071 * Set whether direct student conflicts are allowed for this student 072 */ 073 public void setAllowDirectConflicts(boolean allowDirectConflicts) { 074 iAllowDirectConflicts = allowDirectConflicts; 075 } 076 077 /** 078 * True if the given two exams can have a direct student conflict with this 079 * student, i.e., they can be placed at the same period. 080 * 081 * @param ex1 082 * an exam 083 * @param ex2 084 * an exam 085 * @return {@link ExamStudent#isAllowDirectConflicts()} and 086 * {@link Exam#isAllowDirectConflicts()} for both exams 087 */ 088 public boolean canConflict(Exam ex1, Exam ex2) { 089 return isAllowDirectConflicts() && ex1.isAllowDirectConflicts() && ex2.isAllowDirectConflicts(); 090 } 091 092 /** 093 * Exam(s) enrolled by the student that are scheduled in the given period 094 */ 095 public Set<Exam> getExams(ExamPeriod period) { 096 return iTable[period.getIndex()]; 097 } 098 099 /** 100 * Exam(s) enrolled by the student that are scheduled in the given day 101 */ 102 public Set<Exam> getExamsADay(ExamPeriod period) { 103 return iDayTable[period.getDay()]; 104 } 105 106 /** 107 * Exam(s) enrolled by the student that are scheduled in the given day 108 */ 109 public Set<Exam> getExamsADay(int day) { 110 return iDayTable[day]; 111 } 112 113 /** 114 * Compute conflicts between the given assignment of an exam and all the 115 * current assignments (of this student). Only not-allowed conflicts (see 116 * {@link ExamStudent#canConflict(Exam, Exam)} are considered. 117 */ 118 @Override 119 public void computeConflicts(ExamPlacement p, Set<ExamPlacement> conflicts) { 120 Exam ex = p.variable(); 121 for (Exam exam : iTable[p.getPeriod().getIndex()]) { 122 if (!canConflict(ex, exam)) 123 conflicts.add(exam.getAssignment()); 124 } 125 } 126 127 /** 128 * Check whether there is a conflict between the given assignment of an exam 129 * and all the current assignments (of this student). Only not-allowed 130 * conflicts (see {@link ExamStudent#canConflict(Exam, Exam)} are 131 * considered. 132 */ 133 @Override 134 public boolean inConflict(ExamPlacement p) { 135 Exam ex = p.variable(); 136 for (Exam exam : iTable[p.getPeriod().getIndex()]) { 137 if (!canConflict(ex, exam)) 138 return true; 139 } 140 return false; 141 } 142 143 /** 144 * True if the given exams can conflict (see 145 * {@link ExamStudent#canConflict(Exam, Exam)}), or if they are placed at 146 * different periods. 147 */ 148 @Override 149 public boolean isConsistent(ExamPlacement p1, ExamPlacement p2) { 150 return (p1.getPeriod() != p2.getPeriod() || canConflict(p1.variable(), p2.variable())); 151 } 152 153 /** 154 * An exam was assigned, update student assignment table 155 */ 156 public void afterAssigned(long iteration, ExamPlacement value) { 157 ExamPlacement p = value; 158 iTable[p.getPeriod().getIndex()].add(value.variable()); 159 iDayTable[p.getPeriod().getDay()].add(value.variable()); 160 } 161 162 /** 163 * An exam was unassigned, update student assignment table 164 */ 165 public void afterUnassigned(long iteration, ExamPlacement value) { 166 ExamPlacement p = value; 167 iTable[p.getPeriod().getIndex()].remove(value.variable()); 168 iDayTable[p.getPeriod().getDay()].remove(value.variable()); 169 } 170 171 /** 172 * Compare two student for equality 173 */ 174 @Override 175 public boolean equals(Object o) { 176 if (o == null || !(o instanceof ExamStudent)) 177 return false; 178 ExamStudent s = (ExamStudent) o; 179 return getId() == s.getId(); 180 } 181 182 /** 183 * Hash code 184 */ 185 @Override 186 public int hashCode() { 187 return (int) (getId() ^ (getId() >>> 32)); 188 } 189 190 /** 191 * Student unique id 192 */ 193 @Override 194 public String toString() { 195 return String.valueOf(getId()); 196 } 197 198 /** 199 * Compare two students (by student ids) 200 */ 201 @Override 202 public int compareTo(Constraint<Exam, ExamPlacement> o) { 203 return toString().compareTo(o.toString()); 204 } 205 206 /** 207 * Constraint is hard if {@link ExamStudent#isAllowDirectConflicts()} is 208 * false. 209 */ 210 @Override 211 public boolean isHard() { 212 return !isAllowDirectConflicts(); 213 } 214 215 /** 216 * Courses and/or sections that this student is enrolled to 217 * 218 * @return list of {@link ExamOwner} 219 */ 220 public List<ExamOwner> getOwners() { 221 return iOwners; 222 } 223 224 /** 225 * True if the student is available (for examination timetabling) during the 226 * given period 227 * 228 * @param period 229 * a period 230 * @return true if a student can attend an exam at the given period, false 231 * if otherwise 232 */ 233 public boolean isAvailable(ExamPeriod period) { 234 return (iAvailable == null ? true : iAvailable[period.getIndex()]); 235 } 236 237 /** 238 * Set whether the student is available (for examination timetabling) during 239 * the given period 240 * 241 * @param period 242 * a period 243 * @param available 244 * true if a student can attend an exam at the given period, 245 * false if otherwise 246 */ 247 public void setAvailable(int period, boolean available) { 248 if (iAvailable == null) { 249 iAvailable = new boolean[iTable.length]; 250 for (int i = 0; i < iTable.length; i++) 251 iAvailable[i] = true; 252 } 253 iAvailable[period] = available; 254 } 255 256}