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