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}