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