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}