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