    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}