001package org.cpsolver.studentsct.model; 002 003import java.util.List; 004 005import org.cpsolver.ifs.assignment.Assignment; 006import org.cpsolver.ifs.assignment.context.AssignmentContext; 007import org.cpsolver.ifs.assignment.context.VariableWithContext; 008import org.cpsolver.studentsct.StudentSectioningModel; 009 010 011/** 012 * Representation of a request of a student for a course(s) or a free time. This 013 * can be either {@link CourseRequest} or {@link FreeTimeRequest}. Each request 014 * contains id, priority, weight, and a student. A request can be also marked as 015 * alternative. <br> 016 * <br> 017 * For each student, all non-alternative requests should be satisfied (an 018 * enrollment is assigned to a request). If not, an alternative request can be 019 * assigned instead of a non-alternative course request. In the case when only 020 * one of two requests can be assigned, the one with the lowest priority is 021 * preferred. <br> 022 * <br> 023 * 024 * @author Tomáš Müller 025 * @version StudentSct 1.3 (Student Sectioning)<br> 026 * Copyright (C) 2007 - 2014 Tomáš Müller<br> 027 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 028 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 029 * <br> 030 * This library is free software; you can redistribute it and/or modify 031 * it under the terms of the GNU Lesser General Public License as 032 * published by the Free Software Foundation; either version 3 of the 033 * License, or (at your option) any later version. <br> 034 * <br> 035 * This library is distributed in the hope that it will be useful, but 036 * WITHOUT ANY WARRANTY; without even the implied warranty of 037 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 038 * Lesser General Public License for more details. <br> 039 * <br> 040 * You should have received a copy of the GNU Lesser General Public 041 * License along with this library; if not see 042 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 043 */ 044public abstract class Request extends VariableWithContext<Request, Enrollment, Request.RequestContext> { 045 private long iId = -1; 046 private int iPriority = 0; 047 private boolean iAlternative = false; 048 private Student iStudent = null; 049 private double iWeight = 1.0; 050 /** True means that method {@link Request#values()} will cache its results. */ 051 public static boolean sCacheValues = false; 052 053 /** 054 * Constructor 055 * 056 * @param id 057 * course/free time request unique id 058 * @param priority 059 * request priority -- if there is a choice, request with lower 060 * priority is more preferred to be assigned 061 * @param alternative 062 * true if the request is alternative (alternative request can be 063 * assigned instead of a non-alternative course requests, if it 064 * is left unassigned) 065 * @param student 066 * student to which this request belongs 067 */ 068 public Request(long id, int priority, boolean alternative, Student student) { 069 iId = id; 070 iPriority = priority; 071 iAlternative = alternative; 072 iStudent = student; 073 iStudent.getRequests().add(this); 074 } 075 076 /** Request id */ 077 @Override 078 public long getId() { 079 return iId; 080 } 081 082 /** 083 * Request priority -- if there is a choice, request with lower priority is 084 * more preferred to be assigned 085 * @return request priority 086 */ 087 public int getPriority() { 088 return iPriority; 089 } 090 091 /** Set request priority 092 * @param priority request priority 093 **/ 094 public void setPriority(int priority) { 095 iPriority = priority; 096 } 097 098 /** 099 * True, if the request is alternative (alternative request can be assigned 100 * instead of a non-alternative course requests, if it is left unassigned) 101 * @return is alternative request 102 */ 103 public boolean isAlternative() { 104 return iAlternative; 105 } 106 107 /** Student to which this request belongs 108 * @return student 109 **/ 110 public Student getStudent() { 111 return iStudent; 112 } 113 114 /** 115 * Compare to requests, non-alternative requests go first, otherwise use 116 * priority (a request with lower priority goes first) 117 */ 118 @Override 119 public int compareTo(Request r) { 120 if (getStudent().getId() == r.getStudent().getId()) 121 return (isAlternative() != r.isAlternative() ? isAlternative() ? 1 : -1 : getPriority() < r.getPriority() ? -1 : 1); 122 else 123 return getStudent().compareTo(r.getStudent()); 124 } 125 126 /** Compute available enrollments 127 * @param assignment current assignment 128 * @return list of all enrollments 129 **/ 130 public abstract List<Enrollment> computeEnrollments(Assignment<Request, Enrollment> assignment); 131 132 /** 133 * Domain of this variable -- list of available enrollments. Method 134 * {@link Request#computeEnrollments(Assignment)} is used. 135 */ 136 @Override 137 public List<Enrollment> values(Assignment<Request, Enrollment> assignment) { 138 List<Enrollment> values = super.values(assignment); 139 if (values != null) 140 return values; 141 values = computeEnrollments(assignment); 142 if (sCacheValues) 143 setValues(values); 144 return values; 145 } 146 147 /** 148 * Assign given enrollment to this request. This method also calls 149 * {@link SctAssignment#assigned(Assignment, Enrollment)} on for all the assignments of the 150 * enrollment. 151 */ 152 @Override 153 public void variableAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment enrollment) { 154 super.variableAssigned(assignment, iteration, enrollment); 155 for (SctAssignment a : enrollment.getAssignments()) { 156 a.assigned(assignment, enrollment); 157 } 158 if (enrollment.getConfig() != null) 159 enrollment.getConfig().getContext(assignment).assigned(assignment, enrollment); 160 if (enrollment.getCourse() != null) 161 enrollment.getCourse().assigned(assignment, enrollment); 162 if (enrollment.getReservation() != null) 163 enrollment.getReservation().getContext(assignment).assigned(assignment, enrollment); 164 } 165 166 /** 167 * Unassign currently assigned enrollment from this request. This method 168 * also calls {@link SctAssignment#unassigned(Assignment, Enrollment)} on for all the 169 * assignments of the current enrollment. 170 */ 171 @Override 172 public void variableUnassigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment enrollment) { 173 super.variableUnassigned(assignment, iteration, enrollment); 174 for (SctAssignment a : enrollment.getAssignments()) { 175 a.unassigned(assignment, enrollment); 176 } 177 if (enrollment.getConfig() != null) 178 enrollment.getConfig().getContext(assignment).unassigned(assignment, enrollment); 179 if (enrollment.getCourse() != null) 180 enrollment.getCourse().unassigned(assignment, enrollment); 181 if (enrollment.getReservation() != null) 182 enrollment.getReservation().getContext(assignment).unassigned(assignment, enrollment); 183 } 184 185 /** Get bound, i.e., the value of the best possible enrollment 186 * @return uppper bound 187 **/ 188 public abstract double getBound(); 189 190 /** 191 * Request weight, set by default to 1.0, defines the amount of space which 192 * will be taken in the section by this request. 193 * @return request weight 194 */ 195 public double getWeight() { 196 return iWeight; 197 } 198 199 /** 200 * Set request weight. It defines the amount of space which will be taken in 201 * the section by this request. 202 * @param weight request weight 203 */ 204 public void setWeight(double weight) { 205 iWeight = weight; 206 } 207 208 /** Return true if request is assigned. 209 * @param assignment current assignment 210 * @return true if this request is assigned 211 **/ 212 public boolean isAssigned(Assignment<Request, Enrollment> assignment) { 213 return assignment.getValue(this) != null; 214 } 215 216 @Override 217 public int hashCode() { 218 return (int) (iId ^ (iId >>> 32) ^ getStudent().getId() ^ (getStudent().getId() >>> 32)); 219 } 220 221 @Override 222 public boolean equals(Object o) { 223 if (o == null || !(o instanceof Request)) return false; 224 return getId() == ((Request)o).getId() && getStudent().getId() == ((Request)o).getStudent().getId(); 225 } 226 227 public class RequestContext implements AssignmentContext { 228 private Double iWeight = null; 229 230 public RequestContext(Assignment<Request, Enrollment> assignment) { 231 Enrollment enrollment = assignment.getValue(Request.this); 232 if (enrollment != null) 233 setLastWeight(enrollment.getRequest().getWeight() * ((StudentSectioningModel)getModel()).getStudentWeights().getWeight(assignment, enrollment)); 234 } 235 236 public Double getLastWeight() { return iWeight; } 237 public void setLastWeight(Double weight) { iWeight = weight; } 238 } 239 240 241 @Override 242 public RequestContext createAssignmentContext(Assignment<Request, Enrollment> assignment) { 243 return new RequestContext(assignment); 244 } 245 246 /** 247 * Return true if this request can track MPP 248 * @return true if the request is course request and it either has an initial enrollment or some selected choices. 249 */ 250 public boolean isMPP() { 251 return false; 252 } 253 254 /** 255 * Return true if this request has any selection 256 * @return true if the request is course request and has some selected choices. 257 */ 258 public boolean hasSelection() { 259 return false; 260 } 261 262 /** 263 * Smallest credit provided by this request 264 */ 265 public abstract float getMinCredit(); 266 267 /** 268 * Is this request critical for the student to progress towards his/her degree 269 */ 270 @Deprecated 271 public boolean isCritical() { 272 return getRequestPriority() != null && getRequestPriority().isCritical(); 273 } 274 275 /** 276 * Importance of the request. Higher priority requests are assigned before lower priority requests. 277 */ 278 public abstract RequestPriority getRequestPriority(); 279 280 /** 281 * Importance of the request for the student to progress towards his/her degree. 282 * The request priority is used to re-order course priorities (if desired), 283 * and to assign requests of a higher priority first (before requests of a lower priority). 284 */ 285 public static enum RequestPriority { 286 LC("l", 1.0), 287 Critical("c", 1.0), 288 Vital("v", 0.8), 289 Important("i", 0.5), 290 Normal("", null), // this is the default priority 291 VisitingF2F("f", null), // low priority for face-to-face courses of visiting students 292 ; 293 294 String iAbbv; 295 Double iBoost; 296 RequestPriority(String abbv, Double boost) { 297 iAbbv = abbv; 298 iBoost = boost; 299 } 300 public String getAbbreviation() { return iAbbv; } 301 public Double getBoost() { return iBoost; } 302 public boolean isCritical(Request r) { 303 return r.getRequestPriority().ordinal() <= ordinal(); 304 } 305 public boolean isHigher(Request r) { 306 return ordinal() < r.getRequestPriority().ordinal(); 307 } 308 public boolean isSame(Request r) { 309 return ordinal() == r.getRequestPriority().ordinal(); 310 } 311 public boolean isCritical() { 312 return ordinal() < Normal.ordinal(); 313 } 314 public int compareCriticalsTo(RequestPriority rp) { 315 if (!isCritical() && !rp.isCritical()) return 0; // do not compare non-critical levels 316 return compareTo(rp); 317 } 318 } 319}