001    package net.sf.cpsolver.exam.model;
002    
003    import java.util.ArrayList;
004    import java.util.HashSet;
005    import java.util.List;
006    import java.util.Set;
007    
008    import 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 Tomas Muller<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     */
033    public 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    }