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