    001package org.cpsolver.studentsct.online.expectations;
    002
    003import org.cpsolver.ifs.assignment.Assignment;
    004import org.cpsolver.ifs.util.DataProperties;
    005import org.cpsolver.studentsct.model.Config;
    006import org.cpsolver.studentsct.model.Enrollment;
    007import org.cpsolver.studentsct.model.Request;
    008import org.cpsolver.studentsct.model.Section;
    009import org.cpsolver.studentsct.model.Subpart;
    010import org.cpsolver.studentsct.online.OnlineConfig;
    011import org.cpsolver.studentsct.online.OnlineSection;
    012
    013/**
    014 * A class is considered over-expected, when there less space available than expected. The
    015 * expectations can be increased by the given percentage (parameter OverExpected.Percentage,
    016 * defaults to 1.0).
    017 * Expectation rounding can be defined by OverExpected.Rounding parameter, defaults to round
    018 * (other values are none, ceil, and floor).<br><br>
    019 * Unlimited classes are never over-expected. A class is over-expected when the number of
    020 * enrolled students (including the student in question) + expectations (multiplied by
    021 * OverExpected.Percentage) is greater or equal the section limit.
    022 *
    023 *
    024 * @author  Tomáš Müller
    025 * @version StudentSct 1.3 (Student Sectioning)<br>
    026 *          Copyright (C) 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 <a
    042 *          href='http://www.gnu.org/licenses'>http://www.gnu.org/licenses</a>.
    043 *
    044 */
    045public class PercentageOverExpected implements OverExpectedCriterion {
    046    private Double iPercentage = null;
    047    private Rounding iRounding = Rounding.ROUND;
    048
    049    /**
    050     * Expectations rounding
    051     */
    052    public static enum Rounding {
    053        /** no rounding */
    054        NONE,
    055        /** ceiling, using {@link Math#ceil(double)} */
    056        CEIL,
    057        /** floor, using {@link Math#floor(double)} */
    058        FLOOR,
    059        /** rounding, using {@link Math#round(double)} */
    060        ROUND,
    061    }
    062
    063    public PercentageOverExpected(DataProperties config) {
    064        iPercentage = config.getPropertyDouble("OverExpected.Percentage", iPercentage);
    065        iRounding = Rounding.valueOf(config.getProperty("OverExpected.Rounding", iRounding.name()).toUpperCase());
    066    }
    067
    068    public PercentageOverExpected(Double percentage) {
    069        super();
    070        iPercentage = percentage;
    071    }
    072
    073    public PercentageOverExpected() {
    074        this((Double) null);
    075    }
    076
    077    /**
    078     * Over-expected percentage, defaults to 1.0
    079     * @return expectations adjustment
    080     */
    081    public double getPercentage() {
    082        return iPercentage == null ? 1.0 : iPercentage;
    083    }
    084
    085    /**
    086     * Over-expected percentage, defaults to 1.0
    087     * @param percentage expectations adjustment
    088     */
    089    public void setPercentage(Double percentage) {
    090        iPercentage = percentage;
    091    }
    092
    093    /**
    094     * Round the given value using the rounding from OverExpected.Rounding parameter
    095     * @param value given value
    096     * @return rounded value
    097     */
    098    protected double round(double value) {
    099        switch (iRounding) {
    100            case CEIL:
    101                return Math.ceil(value);
    102            case FLOOR:
    103                return Math.floor(value);
    104            case ROUND:
    105                return Math.round(value);
    106            default:
    107                return value;
    108        }
    109    }
    110
    111    /**
    112     * Check if there are expectations on any of the sections of the given subpart
    113     * @param subpart given subpart
    114     * @return true if there is at least one section with positive {@link Section#getSpaceExpected()}
    115     */
    116    protected boolean hasExpectations(Subpart subpart) {
    117        for (Section section : subpart.getSections())
    118            if (round(section.getSpaceExpected()) > 0.0)
    119                return true;
    120        return false;
    121    }
    122
    123    /**
    124     * Config enrollment (using {@link OnlineConfig#getEnrollment()} if applicable}, {@link Config#getEnrollmentWeight(Assignment, Request)} otherwise)
    125     * @param assignment current assignment
    126     * @param config given configuration
    127     * @param request given request
    128     * @return current enrollment of the section, excluding the request
    129     */
    130    protected double getEnrollment(Assignment<Request, Enrollment> assignment, Config config, Request request) {
    131        if (config instanceof OnlineConfig) {
    132            return ((OnlineConfig) config).getEnrollment();
    133        } else {
    134            return config.getEnrollmentWeight(assignment, request);
    135        }
    136    }
    137
    138    /**
    139     * Section enrollment (using {@link OnlineSection#getEnrollment()} if applicable}, {@link Section#getEnrollmentWeight(Assignment, Request)} otherwise)
    140     * @param assignment current assignment
    141     * @param section given section
    142     * @param request given request
    143     * @return current enrollment of the section, excluding the request
    144     */
    145    protected double getEnrollment(Assignment<Request, Enrollment> assignment, Section section, Request request) {
    146        if (section instanceof OnlineSection) {
    147            return ((OnlineSection) section).getEnrollment();
    148        } else {
    149            return section.getEnrollmentWeight(assignment, request);
    150        }
    151    }
    152
    153    /**
    154     * Section limit (using {@link OnlineSection#getEnrollment()} if applicable}, {@link Section#getLimit()} otherwise)
    155     * @param section given section
    156     * @return limit of the given section
    157     */
    158    protected int getLimit(Section section) {
    159        if (section.getLimit() < 0)
    160            return section.getLimit();
    161        if (section instanceof OnlineSection) {
    162            return section.getLimit() + ((OnlineSection) section).getEnrollment();
    163        } else {
    164            return section.getLimit();
    165        }
    166    }
    167
    168    /**
    169     * Subpart limit (using {@link OnlineConfig#getEnrollment()} if applicable}, {@link Subpart#getLimit()} otherwise)
    170     * @param subpart given subpart
    171     * @return limit of the given subpart
    172     */
    173    protected int getLimit(Subpart subpart) {
    174        int limit = subpart.getLimit();
    175        if (limit < 0)
    176            return limit;
    177        if (subpart.getConfig() instanceof OnlineConfig)
    178            limit += ((OnlineConfig) subpart.getConfig()).getEnrollment();
    179        return limit;
    180    }
    181
    182    @Override
    183    public double getOverExpected(Assignment<Request, Enrollment> assignment, Section section, Request request) {
    184        if (section.getLimit() <= 0)
    185            return 0.0; // ignore unlimited & not available
    186
    187        double expected = round(getPercentage() * section.getSpaceExpected());
    188        double enrolled = getEnrollment(assignment, section, request) + request.getWeight();
    189        double limit = getLimit(section);
    190        int subparts = section.getSubpart().getConfig().getSubparts().size();
    191
    192        return expected + enrolled > limit ? 1.0 / subparts : 0.0;
    193    }
    194
    195    @Override
    196    public Integer getExpected(int sectionLimit, double expectedSpace) {
    197        if (sectionLimit <= 0)
    198            return null;
    199
    200        double expected = round(getPercentage() * expectedSpace);
    201        if (expected > 0.0)
    202            return (int) Math.floor(expected);
    203
    204        return null;
    205    }
    206
    207    @Override
    208    public String toString() {
    209        return "perc(" + getPercentage() + ")";
    210    }
    211
    212}