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}