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    }