    001package org.cpsolver.studentsct.constraint;
    002
    003import java.util.Set;
    004
    005import org.cpsolver.ifs.assignment.Assignment;
    006import org.cpsolver.ifs.model.Constraint;
    007import org.cpsolver.studentsct.model.CourseRequest;
    008import org.cpsolver.studentsct.model.Enrollment;
    009import org.cpsolver.studentsct.model.Request;
    010import org.cpsolver.studentsct.model.Student;
    011
    012
    013/**
    014 * This constraints ensures that a student is not enrolled into sections that
    015 * are overlapping in time.
    016 *
    017 * <br>
    018 * <br>
    019 *
    020 * @author  Tomáš Müller
    021 * @version StudentSct 1.3 (Student Sectioning)<br>
    022 *          Copyright (C) 2007 - 2014 Tomáš Müller<br>
    023 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    024 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    025 * <br>
    026 *          This library is free software; you can redistribute it and/or modify
    027 *          it under the terms of the GNU Lesser General Public License as
    028 *          published by the Free Software Foundation; either version 3 of the
    029 *          License, or (at your option) any later version. <br>
    030 * <br>
    031 *          This library is distributed in the hope that it will be useful, but
    032 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    033 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    034 *          Lesser General Public License for more details. <br>
    035 * <br>
    036 *          You should have received a copy of the GNU Lesser General Public
    037 *          License along with this library; if not see
    038 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
    039 */
    040public class StudentConflict extends Constraint<Request, Enrollment> {
    041
    042    /**
    043     * Constructor
    044     * @param student student for which the constraint is created
    045     */
    046    public StudentConflict(Student student) {
    047        super();
    048        for (Request request : student.getRequests())
    049            addVariable(request);
    050    }
    051
    052    /**
    053     * True if the given enrollment can be assigned to the student. A new enrollment
    054     * cannot be assigned to a student when the student already has the desired
    055     * number of requests assigned (i.e., number of non-alternative course
    056     * requests).
    057     * @param assignment current assignment
    058     * @param enrollment given enrollment
    059     * @return true if the given request can be assigned
    060     **/
    061    public static boolean canAssign(Assignment<Request, Enrollment> assignment, Enrollment enrollment, Set<Enrollment> conflicts) {
    062        int alt = 0;
    063        float credit = 0;
    064        boolean found = false;
    065        for (Request r : enrollment.getStudent().getRequests()) {
    066            Enrollment e = (r.equals(enrollment.getRequest()) ? enrollment : r.getAssignment(assignment));
    067            if (r.equals(enrollment.getRequest())) {
    068                found = true;
    069            } else if (e != null && conflicts != null && conflicts.contains(e)) {
    070                e = null;
    071            }
    072            boolean assigned = (e != null || r.equals(enrollment.getRequest()));
    073            boolean course = (r instanceof CourseRequest);
    074            boolean waitlist = (course && ((CourseRequest) r).isWaitlist());
    075            if (r.isAlternative()) {
    076                if (assigned || (!found && waitlist))
    077                    alt--;
    078            } else {
    079                if (course && !waitlist && !assigned)
    080                    alt++;
    081            }
    082            if (e != null) credit += e.getCredit();
    083        }
    084        return (alt >= 0 && credit <= enrollment.getStudent().getMaxCredit());
    085    }
    086
    087    /**
    088     * A given enrollment is conflicting when the student is enrolled into
    089     * another course / free time request that has an assignment that is
    090     * overlapping with one or more assignments of the given section. See
    091     * {@link Enrollment#isOverlapping(Enrollment)} for more details. All such
    092     * overlapping enrollments are added into the provided set of conflicts.
    093     *
    094     * @param enrollment
    095     *            {@link Enrollment} that is being considered
    096     * @param conflicts
    097     *            resultant list of conflicting enrollments
    098     */
    099    @Override
    100    public void computeConflicts(Assignment<Request, Enrollment> assignment, Enrollment enrollment, Set<Enrollment> conflicts) {
    101        // for all assigned course requests -> if overlapping with this
    102        // enrollment -> conflict
    103        for (Request request : variables()) {
    104            if (request.equals(enrollment.getRequest()))
    105                continue;
    106            Enrollment e = assignment.getValue(request);
    107            if (e == null)
    108                continue;
    109            if (enrollment.isOverlapping(e))
    110                conflicts.add(e);
    111        }
    112
    113        // if this enrollment cannot be assigned (student already has a full
    114        // schedule) -> unassignd a lowest priority request
    115        while (!enrollment.getAssignments().isEmpty() && !canAssign(assignment, enrollment, conflicts)) {
    116            Enrollment lowestPriorityEnrollment = null;
    117            int lowestPriority = -1;
    118            for (Request request : variables()) {
    119                if (request.equals(enrollment.getRequest()))
    120                    continue;
    121                if (!(request instanceof CourseRequest) || ((CourseRequest)request).isWaitlist())
    122                    continue;
    123                Enrollment e = assignment.getValue(request);
    124                if (e == null || conflicts.contains(e))
    125                    continue;
    126                if (lowestPriority < request.getPriority()) {
    127                    lowestPriority = request.getPriority();
    128                    lowestPriorityEnrollment = e;
    129                }
    130            }
    131            if (lowestPriorityEnrollment != null) {
    132                conflicts.add(lowestPriorityEnrollment);
    133            } else {
    134                conflicts.add(enrollment); // there are only alternatives or wait-listed courses
    135                break;
    136            }
    137        }
    138    }
    139
    140    /** Two enrollments are consistent if they are not overlapping in time */
    141    @Override
    142    public boolean isConsistent(Enrollment e1, Enrollment e2) {
    143        return !e1.isOverlapping(e2);
    144    }
    145
    146    /**
    147     * A given enrollment is conflicting when the student is enrolled into
    148     * another course / free time request that has an assignment that is
    149     * overlapping with one or more assignments of the given section. See
    150     * {@link Enrollment#isOverlapping(Enrollment)} for more details.
    151     *
    152     * @param enrollment
    153     *            {@link Enrollment} that is being considered
    154     * @return true, if the student is enrolled into another enrollment of a
    155     *         different request that is overlapping in time with the given
    156     *         enrollment
    157     */
    158    @Override
    159    public boolean inConflict(Assignment<Request, Enrollment> assignment, Enrollment enrollment) {
    160        // for all assigned course requests -> if overlapping with this
    161        // enrollment -> conflict
    162        for (Request request : variables()) {
    163            if (request.equals(enrollment.getRequest()))
    164                continue;
    165            Enrollment e = assignment.getValue(request);
    166            if (e == null)
    167                continue;
    168            if (enrollment.isOverlapping(e))
    169                return true;
    170        }
    171
    172        // if this enrollment cannot be assigned (student already has a full
    173        // schedule) -> conflict
    174        if (!canAssign(assignment, enrollment, null))
    175            return true;
    176
    177        // nothing above -> no conflict
    178        return false;
    179    }
    180
    181    @Override
    182    public String toString() {
    183        return "StudentConflicts";
    184    }
    185}