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 protected RequestPriority iRequestPriority = RequestPriority.Normal; 051 /** True means that method {@link Request#values()} will cache its results. */ 052 public static boolean sCacheValues = false; 053 054 /** 055 * Constructor 056 * 057 * @param id 058 * course/free time request unique id 059 * @param priority 060 * request priority -- if there is a choice, request with lower 061 * priority is more preferred to be assigned 062 * @param alternative 063 * true if the request is alternative (alternative request can be 064 * assigned instead of a non-alternative course requests, if it 065 * is left unassigned) 066 * @param student 067 * student to which this request belongs 068 */ 069 public Request(long id, int priority, boolean alternative, Student student) { 070 iId = id; 071 iPriority = priority; 072 iAlternative = alternative; 073 iStudent = student; 074 iStudent.getRequests().add(this); 075 } 076 077 /** Request id */ 078 @Override 079 public long getId() { 080 return iId; 081 } 082 083 /** 084 * Request priority -- if there is a choice, request with lower priority is 085 * more preferred to be assigned 086 * @return request priority 087 */ 088 public int getPriority() { 089 return iPriority; 090 } 091 092 /** Set request priority 093 * @param priority request priority 094 **/ 095 public void setPriority(int priority) { 096 iPriority = priority; 097 } 098 099 /** 100 * True, if the request is alternative (alternative request can be assigned 101 * instead of a non-alternative course requests, if it is left unassigned) 102 * @return is alternative request 103 */ 104 public boolean isAlternative() { 105 return iAlternative; 106 } 107 108 /** Student to which this request belongs 109 * @return student 110 **/ 111 public Student getStudent() { 112 return iStudent; 113 } 114 115 /** 116 * Compare to requests, non-alternative requests go first, otherwise use 117 * priority (a request with lower priority goes first) 118 */ 119 @Override 120 public int compareTo(Request r) { 121 if (getStudent().getId() == r.getStudent().getId()) 122 return (isAlternative() != r.isAlternative() ? isAlternative() ? 1 : -1 : getPriority() < r.getPriority() ? -1 : getPriority() > r.getPriority() ? 1 : 0); 123 else 124 return getStudent().compareTo(r.getStudent()); 125 } 126 127 /** Compute available enrollments 128 * @param assignment current assignment 129 * @return list of all enrollments 130 **/ 131 public abstract List<Enrollment> computeEnrollments(Assignment<Request, Enrollment> assignment); 132 133 /** 134 * Domain of this variable -- list of available enrollments. Method 135 * {@link Request#computeEnrollments(Assignment)} is used. 136 */ 137 @Override 138 public List<Enrollment> values(Assignment<Request, Enrollment> assignment) { 139 List<Enrollment> values = super.values(assignment); 140 if (values != null) 141 return values; 142 values = computeEnrollments(assignment); 143 if (sCacheValues) 144 setValues(values); 145 return values; 146 } 147 148 /** 149 * Assign given enrollment to this request. This method also calls 150 * {@link SctAssignment#assigned(Assignment, Enrollment)} on for all the assignments of the 151 * enrollment. 152 */ 153 @Override 154 public void variableAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment enrollment) { 155 super.variableAssigned(assignment, iteration, enrollment); 156 for (SctAssignment a : enrollment.getAssignments()) { 157 a.assigned(assignment, enrollment); 158 } 159 if (enrollment.getConfig() != null) 160 enrollment.getConfig().getContext(assignment).assigned(assignment, enrollment); 161 if (enrollment.getCourse() != null) 162 enrollment.getCourse().assigned(assignment, enrollment); 163 if (enrollment.getReservation() != null) 164 enrollment.getReservation().getContext(assignment).assigned(assignment, enrollment); 165 } 166 167 /** 168 * Unassign currently assigned enrollment from this request. This method 169 * also calls {@link SctAssignment#unassigned(Assignment, Enrollment)} on for all the 170 * assignments of the current enrollment. 171 */ 172 @Override 173 public void variableUnassigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment enrollment) { 174 super.variableUnassigned(assignment, iteration, enrollment); 175 for (SctAssignment a : enrollment.getAssignments()) { 176 a.unassigned(assignment, enrollment); 177 } 178 if (enrollment.getConfig() != null) 179 enrollment.getConfig().getContext(assignment).unassigned(assignment, enrollment); 180 if (enrollment.getCourse() != null) 181 enrollment.getCourse().unassigned(assignment, enrollment); 182 if (enrollment.getReservation() != null) 183 enrollment.getReservation().getContext(assignment).unassigned(assignment, enrollment); 184 } 185 186 /** Get bound, i.e., the value of the best possible enrollment 187 * @return uppper bound 188 **/ 189 public abstract double getBound(); 190 191 /** 192 * Request weight, set by default to 1.0, defines the amount of space which 193 * will be taken in the section by this request. 194 * @return request weight 195 */ 196 public double getWeight() { 197 return iWeight; 198 } 199 200 /** 201 * Set request weight. It defines the amount of space which will be taken in 202 * the section by this request. 203 * @param weight request weight 204 */ 205 public void setWeight(double weight) { 206 iWeight = weight; 207 } 208 209 /** Return true if request is assigned. 210 * @param assignment current assignment 211 * @return true if this request is assigned 212 **/ 213 public boolean isAssigned(Assignment<Request, Enrollment> assignment) { 214 return assignment.getValue(this) != null; 215 } 216 217 @Override 218 public int hashCode() { 219 return (int) (iId ^ (iId >>> 32) ^ getStudent().getId() ^ (getStudent().getId() >>> 32)); 220 } 221 222 @Override 223 public boolean equals(Object o) { 224 if (o == null || !(o instanceof Request)) return false; 225 return getId() == ((Request)o).getId() && getStudent().getId() == ((Request)o).getStudent().getId(); 226 } 227 228 public class RequestContext implements AssignmentContext { 229 private Double iWeight = null; 230 231 public RequestContext(Assignment<Request, Enrollment> assignment) { 232 Enrollment enrollment = assignment.getValue(Request.this); 233 if (enrollment != null) 234 setLastWeight(enrollment.getRequest().getWeight() * ((StudentSectioningModel)getModel()).getStudentWeights().getWeight(assignment, enrollment)); 235 } 236 237 public Double getLastWeight() { return iWeight; } 238 public void setLastWeight(Double weight) { iWeight = weight; } 239 } 240 241 242 @Override 243 public RequestContext createAssignmentContext(Assignment<Request, Enrollment> assignment) { 244 return new RequestContext(assignment); 245 } 246 247 /** 248 * Return true if this request can track MPP 249 * @return true if the request is course request and it either has an initial enrollment or some selected choices. 250 */ 251 public boolean isMPP() { 252 return false; 253 } 254 255 /** 256 * Return true if this request has any selection 257 * @return true if the request is course request and has some selected choices. 258 */ 259 public boolean hasSelection() { 260 return false; 261 } 262 263 /** 264 * Smallest credit provided by this request 265 */ 266 public abstract float getMinCredit(); 267 268 /** 269 * Is this request critical for the student to progress towards his/her degree 270 */ 271 @Deprecated 272 public boolean isCritical() { 273 return getRequestPriority() != null && getRequestPriority().isCritical(); 274 } 275 276 /** 277 * Check if this request is {@link CourseRequest} and contains the given course. 278 */ 279 public boolean hasCourse(Course course) { 280 return false; 281 } 282 283 /** 284 * Check if this request is {@link CourseRequest} and a parent of some other course 285 */ 286 public boolean hasChildren() { 287 return false; 288 } 289 290 /** 291 * Importance of the request. Higher priority requests are assigned before lower priority requests. 292 */ 293 public void setRequestPriority(RequestPriority priority) { 294 iRequestPriority = priority; 295 } 296 297 /** 298 * Importance of the request. Higher priority requests are assigned before lower priority requests. 299 */ 300 public RequestPriority getRequestPriority() { 301 return iRequestPriority; 302 } 303 304 /** 305 * Importance of the request for the student to progress towards his/her degree. 306 * The request priority is used to re-order course priorities (if desired), 307 * and to assign requests of a higher priority first (before requests of a lower priority). 308 */ 309 public static enum RequestPriority { 310 LC("l", 1.0), 311 Critical("c", 1.0), 312 Vital("v", 0.8), 313 Important("i", 0.5), 314 Normal("", null), // this is the default priority 315 VisitingF2F("f", null), // low priority for face-to-face courses of visiting students 316 ; 317 318 String iAbbv; 319 Double iBoost; 320 RequestPriority(String abbv, Double boost) { 321 iAbbv = abbv; 322 iBoost = boost; 323 } 324 public String getAbbreviation() { return iAbbv; } 325 public Double getBoost() { return iBoost; } 326 public boolean isCritical(Request r) { 327 return r.getRequestPriority().ordinal() <= ordinal(); 328 } 329 public boolean isHigher(Request r) { 330 return ordinal() < r.getRequestPriority().ordinal(); 331 } 332 public boolean isSame(Request r) { 333 return ordinal() == r.getRequestPriority().ordinal(); 334 } 335 public boolean isCritical() { 336 return ordinal() < Normal.ordinal(); 337 } 338 public int compareCriticalsTo(RequestPriority rp) { 339 if (!isCritical() && !rp.isCritical()) return 0; // do not compare non-critical levels 340 return compareTo(rp); 341 } 342 } 343}