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