    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}