001package org.cpsolver.studentsct.heuristics.studentord; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.Comparator; 006import java.util.HashSet; 007import java.util.HashMap; 008import java.util.List; 009 010import org.cpsolver.ifs.util.DataProperties; 011import org.cpsolver.studentsct.model.Config; 012import org.cpsolver.studentsct.model.Course; 013import org.cpsolver.studentsct.model.CourseRequest; 014import org.cpsolver.studentsct.model.Request; 015import org.cpsolver.studentsct.model.Request.RequestPriority; 016import org.cpsolver.studentsct.model.Section; 017import org.cpsolver.studentsct.model.Student; 018import org.cpsolver.studentsct.model.Subpart; 019 020 021/** 022 * Return the given set of students in an order of average number of choices of 023 * each student (students with more choices first). 024 * 025 * @author Tomáš Müller 026 * @version StudentSct 1.3 (Student Sectioning)<br> 027 * Copyright (C) 2007 - 2014 Tomáš Müller<br> 028 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 029 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 030 * <br> 031 * This library is free software; you can redistribute it and/or modify 032 * it under the terms of the GNU Lesser General Public License as 033 * published by the Free Software Foundation; either version 3 of the 034 * License, or (at your option) any later version. <br> 035 * <br> 036 * This library is distributed in the hope that it will be useful, but 037 * WITHOUT ANY WARRANTY; without even the implied warranty of 038 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 039 * Lesser General Public License for more details. <br> 040 * <br> 041 * You should have received a copy of the GNU Lesser General Public 042 * License along with this library; if not see 043 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 044 */ 045public class StudentChoiceOrder implements StudentOrder, Comparator<Student> { 046 private boolean iReverse = false; 047 private boolean iFast = true; 048 private boolean iCriticalOnly = false; 049 private RequestPriority iPriority = RequestPriority.Critical; 050 private HashMap<Config, Integer> iCache = new HashMap<Config, Integer>(); 051 052 public StudentChoiceOrder(DataProperties config) { 053 iReverse = config.getPropertyBoolean("StudentChoiceOrder.Reverse", iReverse); 054 iFast = config.getPropertyBoolean("StudentChoiceOrder.Fast", iFast); 055 iCriticalOnly = config.getPropertyBoolean("StudentChoiceOrder.CriticalOnly", iCriticalOnly); 056 } 057 058 /** Is order reversed 059 * @return true if the order is reversed 060 **/ 061 public boolean isReverse() { 062 return iReverse; 063 } 064 065 /** Set reverse order 066 * @param reverse true if students are to be in a reversed order 067 **/ 068 public void setReverse(boolean reverse) { 069 iReverse = reverse; 070 } 071 072 public boolean isCriticalOnly() { return iCriticalOnly; } 073 public void setCriticalOnly(boolean critOnly) { iCriticalOnly = critOnly; } 074 public void setRequestPriority(RequestPriority priority) { iPriority = priority; } 075 public RequestPriority getRequestPriority() { return iPriority; } 076 077 /** Order the given list of students */ 078 @Override 079 public List<Student> order(List<Student> students) { 080 List<Student> ret = new ArrayList<Student>(students); 081 Collections.sort(ret, this); 082 return ret; 083 } 084 085 @Override 086 public int compare(Student s1, Student s2) { 087 if (s1.getPriority() != s2.getPriority()) return (s1.getPriority().ordinal() < s2.getPriority().ordinal() ? -1 : 1); 088 try { 089 int cmp = -Double.compare(avgNrChoices(s1), avgNrChoices(s2)); 090 if (cmp != 0) 091 return (iReverse ? -1 : 1) * cmp; 092 } catch (Exception e) { 093 e.printStackTrace(); 094 } 095 return (iReverse ? -1 : 1) * Double.compare(s1.getId(), s2.getId()); 096 } 097 098 private int nrChoices(Config config, int idx, HashSet<Section> sections) { 099 if (iFast) { 100 int nrChoices = 1; 101 for (Subpart subpart: config.getSubparts()) { 102 if (subpart.getChildren().isEmpty()) 103 nrChoices *= subpart.getSections().size(); 104 } 105 return nrChoices; 106 } 107 if (config.getSubparts().size() == idx) { 108 return 1; 109 } else { 110 Subpart subpart = config.getSubparts().get(idx); 111 int choicesThisSubpart = 0; 112 for (Section section : subpart.getSections()) { 113 if (section.getParent() != null && !sections.contains(section.getParent())) 114 continue; 115 if (section.isOverlapping(sections)) 116 continue; 117 sections.add(section); 118 choicesThisSubpart += nrChoices(config, idx + 1, sections); 119 sections.remove(section); 120 } 121 return choicesThisSubpart; 122 } 123 } 124 125 /** Average number of choices for each student 126 * @param student given student 127 * @return average number of choices of the given student 128 **/ 129 public double avgNrChoices(Student student) { 130 int nrRequests = 0; 131 int nrChoices = 0; 132 for (Request request : student.getRequests()) { 133 if (request instanceof CourseRequest) { 134 CourseRequest cr = (CourseRequest) request; 135 if (iCriticalOnly && (!iPriority.isCritical(cr) || cr.isAlternative())) continue; 136 for (Course course : cr.getCourses()) { 137 for (Config config : course.getOffering().getConfigs()) { 138 Integer nrChoicesThisCfg = iCache.get(config); 139 if (nrChoicesThisCfg == null) { 140 nrChoicesThisCfg = Integer.valueOf(nrChoices(config, 0, new HashSet<Section>())); 141 iCache.put(config, nrChoicesThisCfg); 142 } 143 nrChoices += nrChoicesThisCfg.intValue(); 144 } 145 } 146 nrRequests++; 147 } 148 } 149 return (nrRequests == 0 ? 0.0 : ((double) nrChoices) / nrRequests); 150 } 151}