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