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}