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}