001 package ttsolver.constraint;
002
003 import ifs.model.*;
004 import java.util.*;
005 import ttsolver.model.*;
006 import ifs.util.*;
007
008 /**
009 * Instructor constraint.
010 * <br>
011 * Classes with this instructor can not overlap in time. Also, for back-to-back classes,
012 * there is the following reasoning:<ul>
013 * <li>if the distance is equal or below {@link InstructorConstraint#sNoPreferenceLimit} .. no preference
014 * <li>if the distance is above {@link InstructorConstraint#sNoPreferenceLimit} and below {@link InstructorConstraint#sDiscouragedLimit} .. constraint is discouraged (soft, preference = 1)
015 * <li>if the distance is above {@link InstructorConstraint#sDiscouragedLimit} and below {@link InstructorConstraint#sProhibitedLimit} .. constraint is strongly discouraged (soft, preference = 2)
016 * <li>if the distance is above {@link InstructorConstraint#sProhibitedLimit} .. constraint is prohibited (hard)
017 * </ul>
018 *
019 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomáš Müller</a>
020 * @version 1.0
021 */
022
023 public class InstructorConstraint extends ResourceConstraint {
024 /** Back-to-back classes: maximal distance for no prefernce */
025 public static double sNoPreferenceLimit = 0.0;
026 /** Back-to-back classes: maximal distance for discouraged prefernce */
027 public static double sDiscouragedLimit = 5.0;
028 /** Back-to-back classes: maximal distance for strongly discouraged prefernce (everything above is prohibited) */
029 public static double sProhibitedLimit = 20.0;
030
031 /** Constructor
032 * @param id instructor id
033 * @param name instructor name
034 * @param slotsPerDay number of slots per day
035 * @param days number of days
036 */
037 public InstructorConstraint(String id, String name, int slotsPerDay, int days) {
038 super(id,name,slotsPerDay,days);
039 }
040
041 /** Back-to-back preference of two placements (3 means prohibited) */
042 public static int getDistancePreference(Placement p1, Placement p2) {
043 if (!Placement.shareDays(p1,p2)) return 0;
044 int s1 = p1.getTimeLocation().getStartSlot() % edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY;
045 int s2 = p2.getTimeLocation().getStartSlot() % edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY;
046 if (s1+p1.getTimeLocation().getLength()!=s2 &&
047 s2+p2.getTimeLocation().getLength()!=s1) return 0;
048 double distance = Placement.getDistance(p1,p2);
049 if (distance==sNoPreferenceLimit) return 0;
050 if (distance<=sDiscouragedLimit) return 1;
051 if (distance<=sProhibitedLimit) return 2;
052 return 3;
053 }
054
055 public void computeConflicts(Value value, Set conflicts) {
056 super.computeConflicts(value, conflicts);
057 Lecture lecture = (Lecture) value.variable();
058 Placement placement = (Placement) value;
059 int[] startSlots = placement.getTimeLocation().getStartSlots();
060 for (int i=0;i<startSlots.length;i++) {
061 int prevSlot = startSlots[i]-1;
062 if (prevSlot>=0 && (prevSlot/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY) == (startSlots[i]/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY)) {
063 Lecture conf = iResource[prevSlot];
064 if (conf!=null && Placement.getDistance(placement,(Placement)conf.getAssignment())>sProhibitedLimit) conflicts.add(conf.getAssignment());
065 }
066 int nextSlot = startSlots[i]+placement.getTimeLocation().getLength();
067 if ((nextSlot/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY) == (startSlots[i]/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY)) {
068 Lecture conf = iResource[nextSlot];
069 if (conf!=null && Placement.getDistance(placement,(Placement)conf.getAssignment())>sProhibitedLimit) conflicts.add(conf.getAssignment());
070 }
071 }
072 }
073
074 public boolean inConflict(Value value) {
075 if (super.inConflict(value)) return true;
076 Lecture lecture = (Lecture) value.variable();
077 Placement placement = (Placement) value;
078 int[] startSlots = placement.getTimeLocation().getStartSlots();
079 for (int i=0;i<startSlots.length;i++) {
080 int prevSlot = startSlots[i]-1;
081 if (prevSlot>=0 && (prevSlot/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY) == (startSlots[i]/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY)) {
082 Lecture conf = iResource[prevSlot];
083 if (conf!=null && Placement.getDistance(placement,(Placement)conf.getAssignment())>sProhibitedLimit) return true;
084 }
085 int nextSlot = startSlots[i]+placement.getTimeLocation().getLength();
086 if ((nextSlot/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY) == (startSlots[i]/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY)) {
087 Lecture conf = iResource[nextSlot];
088 if (conf!=null && Placement.getDistance(placement,(Placement)conf.getAssignment())>sProhibitedLimit) return true;
089 }
090 }
091 return false;
092 }
093
094 public boolean isConsistent(Value value1, Value value2) {
095 if (!super.isConsistent(value1,value2)) return false;
096 return getDistancePreference((Placement)value1,(Placement)value2)==3;
097 }
098
099 /** Back-to-back preference of the given placement */
100 public int getPreference(Value value) {
101 Lecture lecture = (Lecture)value.variable();
102 Placement placement = (Placement)value;
103 int pref = 0;
104 int[] startSlots = placement.getTimeLocation().getStartSlots();
105 for (int i=0;i<startSlots.length;i++) {
106 int prevSlot = startSlots[i]-1;
107 if (prevSlot>=0 && (prevSlot/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY) == (startSlots[i]/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY)) {
108 Lecture conf = iResource[prevSlot];
109 double dist = (conf==null?0.0:Placement.getDistance(placement,(Placement)conf.getAssignment()));
110 if (dist>sNoPreferenceLimit && dist<=sProhibitedLimit) pref++;
111 if (dist>sDiscouragedLimit && dist<=sProhibitedLimit) pref++;
112 }
113 int nextSlot = startSlots[i]+placement.getTimeLocation().getLength();
114 if ((nextSlot/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY) == (startSlots[i]/edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY)) {
115 Lecture conf = iResource[nextSlot];
116 double dist = (conf==null?0.0:Placement.getDistance(placement,(Placement)conf.getAssignment()));
117 if (dist>sNoPreferenceLimit && dist<=sProhibitedLimit) pref++;
118 if (dist>sDiscouragedLimit && dist<=sProhibitedLimit) pref++;
119 }
120 }
121 return pref;
122 }
123
124 /** Overall back-to-back preference of this instructor */
125 public int getPreference() {
126 int pref = 0;
127 for (int slot=1;slot<iResource.length;) {
128 if ((slot%edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY)==0 || iResource[slot]==null) {
129 slot++; continue;
130 }
131 if (iResource[slot-1]!=null) {
132 double dist = Placement.getDistance((Placement)iResource[slot-1].getAssignment(),(Placement)iResource[slot].getAssignment());
133 if (dist>sNoPreferenceLimit && dist<=sProhibitedLimit) pref++;
134 if (dist>sDiscouragedLimit && dist<=sProhibitedLimit) pref++;
135 }
136 slot += ((Placement)iResource[slot].getAssignment()).getTimeLocation().getLength();
137 }
138 return pref;
139 }
140
141 /** Worst back-to-back preference of this instructor */
142 public int getWorstPreference() {
143 int pref = 0;
144 for (int slot=1;slot<iResource.length;) {
145 if ((slot%edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY)==0 || iResource[slot]==null) {
146 slot++; continue;
147 }
148 if (iResource[slot-1]!=null) pref+=2;
149 slot += ((Placement)iResource[slot].getAssignment()).getTimeLocation().getLength();
150 }
151 return pref;
152 }
153 }