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}