    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    }