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