    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 : getPriority() > r.getPriority() ? 1 : 0);
    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     * Check if this request is {@link CourseRequest} and contains the given course.
    277     */
    278    public boolean hasCourse(Course course) {
    279        return false;
    280    }
    281
    282    /**
    283     * Check if this request is {@link CourseRequest} and a parent of some other course
    284     */
    285    public boolean hasChildren() {
    286        return false;
    287    }
    288
    289    /**
    290     * Importance of the request. Higher priority requests are assigned before lower priority requests.
    291     */
    292    public abstract RequestPriority getRequestPriority();
    293
    294    /**
    295     * Importance of the request for the student to progress towards his/her degree.
    296     * The request priority is used to re-order course priorities (if desired),
    297     * and to assign requests of a higher priority first (before requests of a lower priority).
    298     */
    299    public static enum RequestPriority {
    300        LC("l", 1.0),
    301        Critical("c", 1.0),
    302        Vital("v", 0.8),
    303        Important("i", 0.5),
    304        Normal("", null), // this is the default priority
    305        VisitingF2F("f", null), // low priority for face-to-face courses of visiting students
    306        ;
    307
    308        String iAbbv;
    309        Double iBoost;
    310        RequestPriority(String abbv, Double boost) {
    311            iAbbv = abbv;
    312            iBoost = boost;
    313        }
    314        public String getAbbreviation() { return iAbbv; }
    315        public Double getBoost() { return iBoost; }
    316        public boolean isCritical(Request r) {
    317            return r.getRequestPriority().ordinal() <= ordinal();
    318        }
    319        public boolean isHigher(Request r) {
    320            return ordinal() < r.getRequestPriority().ordinal();
    321        }
    322        public boolean isSame(Request r) {
    323            return ordinal() == r.getRequestPriority().ordinal();
    324        }
    325        public boolean isCritical() {
    326            return ordinal() < Normal.ordinal();
    327        }
    328        public int compareCriticalsTo(RequestPriority rp) {
    329            if (!isCritical() && !rp.isCritical()) return 0; // do not compare non-critical levels
    330            return compareTo(rp);
    331        }
    332    }
    333}