    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}