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    }