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