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 Tomas Muller<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) {
061        int alt = 0;
062        boolean found = false;
063        for (Request r : enrollment.getStudent().getRequests()) {
064            if (r.equals(enrollment.getRequest()))
065                found = true;
066            boolean assigned = (r.isAssigned(assignment) || r.equals(enrollment.getRequest()));
067            boolean course = (r instanceof CourseRequest);
068            boolean waitlist = (course && ((CourseRequest) r).isWaitlist());
069            if (r.isAlternative()) {
070                if (assigned || (!found && waitlist))
071                    alt--;
072            } else {
073                if (course && !waitlist && !assigned)
074                    alt++;
075            }
076        }
077        return (alt >= 0);
078    }
079
080    /**
081     * A given enrollment is conflicting when the student is enrolled into
082     * another course / free time request that has an assignment that is
083     * overlapping with one or more assignments of the given section. See
084     * {@link Enrollment#isOverlapping(Enrollment)} for more details. All such
085     * overlapping enrollments are added into the provided set of conflicts.
086     * 
087     * @param enrollment
088     *            {@link Enrollment} that is being considered
089     * @param conflicts
090     *            resultant list of conflicting enrollments
091     */
092    @Override
093    public void computeConflicts(Assignment<Request, Enrollment> assignment, Enrollment enrollment, Set<Enrollment> conflicts) {
094        // for all assigned course requests -> if overlapping with this
095        // enrollment -> conflict
096        for (Request request : variables()) {
097            if (request.equals(enrollment.getRequest()))
098                continue;
099            Enrollment e = assignment.getValue(request);
100            if (e == null)
101                continue;
102            if (enrollment.isOverlapping(e))
103                conflicts.add(e);
104        }
105
106        // if this enrollment cannot be assigned (student already has a full
107        // schedule) -> unassignd a lowest priority request
108        if (!enrollment.getAssignments().isEmpty() && !canAssign(assignment, enrollment)) {
109            Enrollment lowestPriorityEnrollment = null;
110            int lowestPriority = -1;
111            for (Request request : variables()) {
112                if (request.equals(enrollment.getRequest()))
113                    continue;
114                if (!(request instanceof CourseRequest) || ((CourseRequest)request).isWaitlist())
115                    continue;
116                Enrollment e = assignment.getValue(request);
117                if (e == null)
118                    continue;
119                if (lowestPriority < request.getPriority()) {
120                    lowestPriority = request.getPriority();
121                    lowestPriorityEnrollment = e;
122                }
123            }
124            if (lowestPriorityEnrollment != null)
125                conflicts.add(lowestPriorityEnrollment);
126            else
127                conflicts.add(enrollment); // there are only alternatives or wait-listed courses
128        }
129    }
130
131    /** Two enrollments are consistent if they are not overlapping in time */
132    @Override
133    public boolean isConsistent(Enrollment e1, Enrollment e2) {
134        return !e1.isOverlapping(e2);
135    }
136
137    /**
138     * A given enrollment is conflicting when the student is enrolled into
139     * another course / free time request that has an assignment that is
140     * overlapping with one or more assignments of the given section. See
141     * {@link Enrollment#isOverlapping(Enrollment)} for more details.
142     * 
143     * @param enrollment
144     *            {@link Enrollment} that is being considered
145     * @return true, if the student is enrolled into another enrollment of a
146     *         different request that is overlapping in time with the given
147     *         enrollment
148     */
149    @Override
150    public boolean inConflict(Assignment<Request, Enrollment> assignment, Enrollment enrollment) {
151        // for all assigned course requests -> if overlapping with this
152        // enrollment -> conflict
153        for (Request request : variables()) {
154            if (request.equals(enrollment.getRequest()))
155                continue;
156            Enrollment e = assignment.getValue(request);
157            if (e == null)
158                continue;
159            if (enrollment.isOverlapping(e))
160                return true;
161        }
162
163        // if this enrollment cannot be assigned (student already has a full
164        // schedule) -> conflict
165        if (!canAssign(assignment, enrollment))
166            return true;
167
168        // nothing above -> no conflict
169        return false;
170    }
171
172    @Override
173    public String toString() {
174        return "StudentConflicts";
175    }
176}