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}