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