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}