001    package net.sf.cpsolver.coursett.constraint;
002    
003    import java.util.BitSet;
004    import java.util.Enumeration;
005    import java.util.HashSet;
006    import java.util.Set;
007    import java.util.Vector;
008    
009    import net.sf.cpsolver.coursett.Constants;
010    import net.sf.cpsolver.coursett.model.Lecture;
011    import net.sf.cpsolver.coursett.model.Placement;
012    import net.sf.cpsolver.coursett.model.TimeLocation;
013    import net.sf.cpsolver.coursett.model.TimetableModel;
014    import net.sf.cpsolver.ifs.model.Constraint;
015    import net.sf.cpsolver.ifs.model.Value;
016    import net.sf.cpsolver.ifs.util.FastVector;
017    
018    /**
019     * Instructor constraint.
020     * <br>
021     * Classes with this instructor can not overlap in time. Also, for back-to-back classes, 
022     * there is the following reasoning:<ul>
023     * <li>if the distance is equal or below {@link TimetableModel#getInstructorNoPreferenceLimit()} .. no preference
024     * <li>if the distance is above {@link TimetableModel#getInstructorNoPreferenceLimit()} and below {@link TimetableModel#getInstructorDiscouragedLimit()} .. constraint is discouraged (soft, preference = 1)
025     * <li>if the distance is above {@link TimetableModel#getInstructorDiscouragedLimit()} and below {@link TimetableModel#getInstructorProhibitedLimit()} .. constraint is strongly discouraged (soft, preference = 2)
026     * <li>if the distance is above {@link TimetableModel#getInstructorProhibitedLimit()} .. constraint is prohibited (hard)
027     * </ul>
028     * <br>
029     * When {@link InstructorConstraint#isIgnoreDistances()} is set to true, the constraint never prohibits two back-to-back classes (but it still tries to minimize the above back-to-back preferences).
030     *
031     * @version
032     * CourseTT 1.1 (University Course Timetabling)<br>
033     * Copyright (C) 2006 Tomáš Müller<br>
034     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
035     * Lazenska 391, 76314 Zlin, Czech Republic<br>
036     * <br>
037     * This library is free software; you can redistribute it and/or
038     * modify it under the terms of the GNU Lesser General Public
039     * License as published by the Free Software Foundation; either
040     * version 2.1 of the License, or (at your option) any later version.
041     * <br><br>
042     * This library is distributed in the hope that it will be useful,
043     * but WITHOUT ANY WARRANTY; without even the implied warranty of
044     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
045     * Lesser General Public License for more details.
046     * <br><br>
047     * You should have received a copy of the GNU Lesser General Public
048     * License along with this library; if not, write to the Free Software
049     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
050     */
051    
052    public class InstructorConstraint extends Constraint {
053        
054        public int iPreference = 0;
055        
056        /** table iResource[slot] = lecture using this resource placed in the given time slot (null if empty) */
057        protected Vector[] iResource;
058        private Long iResourceId;
059        private String iName;
060        private String iPuid;
061        private Vector[] iAvailable = null;
062        private boolean iIgnoreDistances = false;
063        private Long iType = null;
064        
065        
066        /** Constructor
067         * @param id instructor id
068         * @param name instructor name
069         */
070        public InstructorConstraint(Long id, String puid, String name, boolean ignDist) {
071            iResourceId = id;
072            iName = name;
073            iPuid = puid;
074            iIgnoreDistances = ignDist;
075            iResource = new Vector[Constants.SLOTS_PER_DAY * Constants.DAY_CODES.length];
076            for (int i=0;i<iResource.length;i++)
077                iResource[i]=new FastVector(3);
078        }
079    
080        public Vector getPlacements(int slot, Placement placement) {
081            return getPlacements(slot, placement.getTimeLocation().getWeekCode());
082        }
083        
084        public Vector getPlacements(int slot, BitSet weekCode) {
085            Vector placements = new FastVector(iResource[slot].size());
086            for (Enumeration e=iResource[slot].elements();e.hasMoreElements();) {
087                    Placement p = (Placement)e.nextElement();
088                    if (p.getTimeLocation().shareWeeks(weekCode))
089                            placements.addElement(p);
090            }
091            return placements;
092        }
093    
094        public Placement getPlacement(int slot, int day) {
095            for (Enumeration e=iResource[slot].elements();e.hasMoreElements();) {
096                    Placement p = (Placement)e.nextElement();
097                    if (p.getTimeLocation().hasDay(day))
098                            return p;
099            }
100            return null;
101        }
102    
103        public void setNotAvailable(Placement placement) {
104            if (iAvailable==null) {
105                    iAvailable = new Vector[Constants.SLOTS_PER_DAY * Constants.DAY_CODES.length];
106                    for (int i=0;i<iResource.length;i++)
107                            iAvailable[i]=null;
108            }
109                    for (Enumeration e=placement.getTimeLocation().getSlots();e.hasMoreElements();) {
110                            int slot = ((Integer)e.nextElement()).intValue();
111                if (iAvailable[slot]==null)
112                    iAvailable[slot] = new Vector(1);
113                            iAvailable[slot].addElement(placement);
114                    }
115        }
116        public boolean isAvailable(int slot) {
117            if (iAvailable==null) return true;
118            return (iAvailable[slot]==null || iAvailable[slot].isEmpty());
119        }
120        public boolean isAvailable(Lecture lecture, TimeLocation time) {
121            if (iAvailable==null) return true;
122            for (Enumeration e=time.getSlots();e.hasMoreElements();) {
123                    int slot = ((Integer)e.nextElement()).intValue();
124                    if (iAvailable[slot]!=null) {
125                    for (Enumeration f=iAvailable[slot].elements();f.hasMoreElements();) {
126                        Placement p = (Placement)f.nextElement();
127                        if (lecture.canShareRoom((Lecture)p.variable())) continue;
128                        if (time.shareWeeks(p.getTimeLocation())) return false;
129                    }
130                }
131            }
132            return true;
133        }
134        public boolean isAvailable(Lecture lecture, Placement placement) {
135            if (iAvailable==null) return true;
136            for (Enumeration e=placement.getTimeLocation().getSlots();e.hasMoreElements();) {
137                    int slot = ((Integer)e.nextElement()).intValue();
138                if (iAvailable[slot]!=null) {
139                    for (Enumeration f=iAvailable[slot].elements();f.hasMoreElements();) {
140                        Placement p = (Placement)f.nextElement();
141                        if (lecture.canShareRoom((Lecture)p.variable()) && placement.sameRooms(p)) continue;
142                        if (placement.getTimeLocation().shareWeeks(p.getTimeLocation())) return false;
143                    }
144                }
145            }
146            if (!iIgnoreDistances) {
147                TimetableModel m = (TimetableModel)getModel();
148                for (Enumeration e=placement.getTimeLocation().getStartSlots();e.hasMoreElements();) {
149                    int startSlot = ((Integer)e.nextElement()).intValue();
150                    int prevSlot = startSlot-1;
151                    if (prevSlot>=0 && (prevSlot/Constants.SLOTS_PER_DAY) == (startSlot/Constants.SLOTS_PER_DAY)) {
152                        if (iAvailable[prevSlot]!=null) {
153                            for (Enumeration f=iAvailable[prevSlot].elements();f.hasMoreElements();) {
154                                Placement p = (Placement)f.nextElement();
155                                if (lecture.canShareRoom((Lecture)p.variable()) && placement.sameRooms(p)) continue;
156                                if (placement.getTimeLocation().shareWeeks(p.getTimeLocation()) && Placement.getDistance(p,placement)>m.getInstructorProhibitedLimit())
157                                    return false;
158                            }
159                        }
160                    }
161                    int nextSlot = startSlot+placement.getTimeLocation().getLength();
162                    if ((nextSlot/Constants.SLOTS_PER_DAY) == (startSlot/Constants.SLOTS_PER_DAY)) {
163                        if (iAvailable[nextSlot]!=null) {
164                            for (Enumeration f=iAvailable[nextSlot].elements();f.hasMoreElements();) {
165                                Placement p = (Placement)f.nextElement();
166                                if (lecture.canShareRoom((Lecture)p.variable()) && placement.sameRooms(p)) continue;
167                                if (placement.getTimeLocation().shareWeeks(p.getTimeLocation()) && Placement.getDistance(p,placement)>m.getInstructorProhibitedLimit())
168                                    return false;
169                            }
170                        }
171                    }
172                }
173            }
174            return true;
175        }
176        public Vector[] getAvailableArray() {
177            return iAvailable;
178        }
179        
180        /** Back-to-back preference of two placements (3 means prohibited) */
181        public int getDistancePreference(Placement p1, Placement p2) {
182            if (!p1.getTimeLocation().shareDays(p2.getTimeLocation())) return 0;
183            if (!p1.getTimeLocation().shareWeeks(p2.getTimeLocation())) return 0;
184            int s1 = p1.getTimeLocation().getStartSlot() % Constants.SLOTS_PER_DAY;
185            int s2 = p2.getTimeLocation().getStartSlot() % Constants.SLOTS_PER_DAY;
186            if (s1+p1.getTimeLocation().getLength()!=s2 && s2+p2.getTimeLocation().getLength()!=s1) return 0;
187            double distance = Placement.getDistance(p1,p2);
188            TimetableModel m = (TimetableModel)p1.variable().getModel();
189            if (distance<=m.getInstructorNoPreferenceLimit()) return Constants.sPreferenceLevelNeutral;
190            if (distance<=m.getInstructorDiscouragedLimit()) return Constants.sPreferenceLevelDiscouraged;
191            if (iIgnoreDistances || distance<=m.getInstructorProhibitedLimit()) return Constants.sPreferenceLevelStronglyDiscouraged;
192            return Constants.sPreferenceLevelProhibited;
193        }
194        
195        /** Resource id */
196        public Long getResourceId() { return iResourceId; }
197        /** Resource name */
198        public String getName() { return iName; }
199    
200        public void computeConflicts(Value value, Set conflicts) {
201            Lecture lecture = (Lecture) value.variable();
202            Placement placement = (Placement) value;
203            BitSet weekCode = placement.getTimeLocation().getWeekCode();
204            
205            for (Enumeration e=placement.getTimeLocation().getSlots();e.hasMoreElements();) {
206                    int slot = ((Integer)e.nextElement()).intValue();
207                    for (Enumeration f=iResource[slot].elements();f.hasMoreElements();) {
208                            Placement p = (Placement)f.nextElement();
209                            if (!p.equals(lecture.getAssignment()) && p.getTimeLocation().shareWeeks(weekCode)) {
210                                    if (p.canShareRooms(placement) && p.sameRooms(placement)) continue;
211                                    conflicts.add(p);
212                            }
213                    }
214            }
215            if (!iIgnoreDistances) {
216                TimetableModel m = (TimetableModel)getModel();
217                for (Enumeration e=placement.getTimeLocation().getStartSlots();e.hasMoreElements();) {
218                    int startSlot = ((Integer)e.nextElement()).intValue();
219                    int prevSlot = startSlot-1;
220                    if (prevSlot>=0 && (prevSlot/Constants.SLOTS_PER_DAY) == (startSlot/Constants.SLOTS_PER_DAY)) {
221                        Vector conf = getPlacements(prevSlot,placement);
222                        for (Enumeration i=conf.elements();i.hasMoreElements();) {
223                            Placement c = (Placement)i.nextElement();
224                            if (lecture.equals(c.variable())) continue;
225                            if (Placement.getDistance(placement,c)>m.getInstructorProhibitedLimit()) {
226                                if (c.canShareRooms(placement) && c.sameRooms(placement)) continue;
227                                conflicts.add(c);
228                            }
229                        }
230                    }
231                    int nextSlot = startSlot+placement.getTimeLocation().getLength();
232                    if ((nextSlot/Constants.SLOTS_PER_DAY) == (startSlot/Constants.SLOTS_PER_DAY)) {
233                        Vector conf = getPlacements(nextSlot,placement);
234                        for (Enumeration i=conf.elements();i.hasMoreElements();) {
235                            Placement c = (Placement)i.nextElement();
236                            if (lecture.equals(c.variable())) continue;
237                            if (Placement.getDistance(placement,c)>m.getInstructorProhibitedLimit()) {
238                                if (c.canShareRooms(placement) && c.sameRooms(placement)) continue;
239                                conflicts.add(c);
240                            }
241                        }
242                    }
243                }
244            }
245        }
246        
247        public boolean inConflict(Value value) {
248            Lecture lecture = (Lecture) value.variable();
249            Placement placement = (Placement) value;
250            BitSet weekCode = placement.getTimeLocation().getWeekCode();
251            for (Enumeration e=placement.getTimeLocation().getSlots();e.hasMoreElements();) {
252                    int slot = ((Integer)e.nextElement()).intValue();
253                    for (Enumeration f=iResource[slot].elements();f.hasMoreElements();) {
254                            Placement p = (Placement)f.nextElement();
255                            if (!p.equals(lecture.getAssignment()) && p.getTimeLocation().shareWeeks(weekCode)) {
256                                    if (p.canShareRooms(placement) && p.sameRooms(placement)) continue;
257                                    return true;
258                            }
259                    }
260            }
261            if (!iIgnoreDistances) {
262                TimetableModel m = (TimetableModel)getModel();
263                for (Enumeration e=placement.getTimeLocation().getStartSlots();e.hasMoreElements();) {
264                    int startSlot = ((Integer)e.nextElement()).intValue();
265                    int prevSlot = startSlot-1;
266                    if (prevSlot>=0 && (prevSlot/Constants.SLOTS_PER_DAY) == (startSlot/Constants.SLOTS_PER_DAY)) {
267                        Vector conf = getPlacements(prevSlot,placement);
268                        for (Enumeration i=conf.elements();i.hasMoreElements();) {
269                            Placement c = (Placement)i.nextElement();
270                            if (lecture.equals(c.variable())) continue;
271                            if (Placement.getDistance(placement,c)>m.getInstructorProhibitedLimit()) {
272                                if (c.canShareRooms(placement) && c.sameRooms(placement)) continue;
273                                return true;
274                            }
275                        }
276                    }
277                    int nextSlot = startSlot+placement.getTimeLocation().getLength();
278                    if ((nextSlot/Constants.SLOTS_PER_DAY) == (startSlot/Constants.SLOTS_PER_DAY)) {
279                        Vector conf = getPlacements(nextSlot,placement);
280                        for (Enumeration i=conf.elements();i.hasMoreElements();) {
281                            Placement c = (Placement)i.nextElement();
282                            if (lecture.equals(c.variable())) continue;
283                            if (Placement.getDistance(placement,c)>m.getInstructorProhibitedLimit()) {
284                                if (c.canShareRooms(placement) && c.sameRooms(placement)) continue;
285                                return true;
286                            }
287                        }
288                    }
289                }
290            }
291            return false;
292        }
293        
294        public boolean isConsistent(Value value1, Value value2) {
295            Placement p1 = (Placement) value1;
296            Placement p2 = (Placement) value2;
297            if (p1.canShareRooms(p2) && p1.sameRooms(p2)) return true;
298            if (p1.getTimeLocation().hasIntersection(p2.getTimeLocation())) return false;
299            return getDistancePreference(p1,p2)!=Constants.sPreferenceLevelProhibited;
300        }
301        
302        public void assigned(long iteration, Value value) {
303            super.assigned(iteration, value);
304            Placement placement = (Placement) value;
305            iPreference += getPreference(value);
306            for (Enumeration e=placement.getTimeLocation().getSlots();e.hasMoreElements();) {
307                    int slot = ((Integer)e.nextElement()).intValue();
308                iResource[slot].addElement(placement);
309            }
310        }
311        
312        public void unassigned(long iteration, Value value) {
313            super.unassigned(iteration, value);
314            Placement placement = (Placement) value;
315            iPreference -= getPreference(value);
316            for (Enumeration e=placement.getTimeLocation().getSlots();e.hasMoreElements();) {
317                    int slot = ((Integer)e.nextElement()).intValue();
318                iResource[slot].removeElement(placement);
319            }
320        }
321        
322        /** Lookup table getResource()[slot] -> lecture using this resource placed in the given time slot (null if empty) */
323        public Vector getResource(int slot) { return iResource[slot]; }
324        public Placement[] getResourceOfWeek(int startDay) {
325            Placement[] ret = new Placement[iResource.length];
326            for (int i=0;i<iResource.length;i++) {
327                    ret[i]=getPlacement(i,startDay+(i/Constants.SLOTS_PER_DAY));
328            }
329            return ret;
330        }
331    
332        /** Number of useless slots for this resource */
333        public int countUselessSlots() {
334            int ret = 0;
335            for (int d=0;d<Constants.DAY_CODES.length;d++) {
336                for (int s=1;s<Constants.SLOTS_PER_DAY-1;s++) {
337                    int slot = d*Constants.SLOTS_PER_DAY+s;
338                    if (iResource[slot-1]!=null && iResource[slot]==null && iResource[slot+1]!=null)
339                        ret++;
340                }
341            }
342            return ret;
343        }
344        
345        /** Resource usage usage */
346        protected void printUsage(StringBuffer sb) {
347            for (int slot=0;slot<iResource.length;slot++) {
348                for (Enumeration e=iResource[slot].elements();e.hasMoreElements();) {
349                    Placement p = (Placement)e.nextElement();
350                    int day = slot / Constants.SLOTS_PER_DAY;
351                    int time = slot * Constants.SLOT_LENGTH_MIN + Constants.FIRST_SLOT_TIME_MIN;
352                    int h = time / 60;
353                    int m = time % 60;
354                    String d = Constants.DAY_NAMES_SHORT[day];
355                    int slots = p.getTimeLocation().getLength();
356                    time += (30*slots);
357                    int h2 = time / 60;
358                    int m2 = time % 60;
359                    sb.append(sb.length()==0?"":",\n        ").append("["+d+(h>12?h-12:h)+":"+(m<10?"0":"")+m+(h>=12?"p":"a")+"-"+(h2>12?h2-12:h2)+":"+(m2<10?"0":"")+m2+(h2>=12?"p":"a")+"]=").append(p.variable().getName());
360                    slot+=slots-1;
361                    //sb.append(sb.length()==0?"":", ").append("s"+(slot+1)+"=").append(((Lecture)getResource()[slot]).getName());
362                }
363            }
364        }
365    
366        public String toString() {
367            return "Instructor "+getName();
368        }    
369        
370        /** Back-to-back preference of the given placement */
371        public int getPreference(Value value) {
372            Lecture lecture = (Lecture)value.variable();
373            Placement placement = (Placement)value;
374            int pref = 0;
375            TimetableModel m = (TimetableModel)getModel();
376            HashSet used = new HashSet();
377            for (Enumeration e=placement.getTimeLocation().getStartSlots();e.hasMoreElements();) {
378                    int startSlot = ((Integer)e.nextElement()).intValue();
379                int prevSlot = startSlot-1;
380                if (prevSlot>=0 && (prevSlot/Constants.SLOTS_PER_DAY) == (startSlot/Constants.SLOTS_PER_DAY)) {
381                    Vector conf = getPlacements(prevSlot,placement);
382                    for (Enumeration i=conf.elements();i.hasMoreElements();) {
383                            Placement c = (Placement)i.nextElement();
384                            if (lecture.equals(c.variable())) continue;
385                            if (!used.add(c)) continue; 
386                            double dist = Placement.getDistance(placement,c);
387                            if (dist>m.getInstructorNoPreferenceLimit() && dist<=m.getInstructorDiscouragedLimit()) pref+=Constants.sPreferenceLevelDiscouraged;
388                            if (dist>m.getInstructorDiscouragedLimit() && (dist<=m.getInstructorProhibitedLimit() || iIgnoreDistances)) pref+=Constants.sPreferenceLevelStronglyDiscouraged;
389                            if (!iIgnoreDistances && dist>m.getInstructorProhibitedLimit()) pref += Constants.sPreferenceLevelProhibited;
390                    }
391                }
392                int nextSlot = startSlot+placement.getTimeLocation().getLength();
393                if ((nextSlot/Constants.SLOTS_PER_DAY) == (startSlot/Constants.SLOTS_PER_DAY)) {
394                    Vector conf = getPlacements(nextSlot,placement);
395                    for (Enumeration i=conf.elements();i.hasMoreElements();) {
396                            Placement c = (Placement)i.nextElement();
397                            if (lecture.equals(c.variable())) continue;
398                            if (!used.add(c)) continue; 
399                            double dist = Placement.getDistance(placement,c);
400                            if (dist>m.getInstructorNoPreferenceLimit() && dist<=m.getInstructorDiscouragedLimit()) pref+=Constants.sPreferenceLevelDiscouraged;
401                            if (dist>m.getInstructorDiscouragedLimit() && (dist<=m.getInstructorProhibitedLimit() || iIgnoreDistances)) pref+=Constants.sPreferenceLevelStronglyDiscouraged;
402                            if (!iIgnoreDistances && dist>m.getInstructorProhibitedLimit()) pref = Constants.sPreferenceLevelProhibited;
403                    }
404                }
405            }
406            return pref;
407        }
408    
409        public int getPreferenceCombination(Value value) {
410            Lecture lecture = (Lecture)value.variable();
411            Placement placement = (Placement)value;
412            int pref = 0;
413            HashSet used = new HashSet();
414            TimetableModel m = (TimetableModel)getModel();
415            for (Enumeration e=placement.getTimeLocation().getStartSlots();e.hasMoreElements();) {
416                    int startSlot = ((Integer)e.nextElement()).intValue();
417                int prevSlot = startSlot-1;
418                if (prevSlot>=0 && (prevSlot/Constants.SLOTS_PER_DAY) == (startSlot/Constants.SLOTS_PER_DAY)) {
419                    Vector conf = getPlacements(prevSlot,placement);
420                    for (Enumeration i=conf.elements();i.hasMoreElements();) {
421                            Placement c = (Placement)i.nextElement();
422                            if (lecture.equals(c.variable())) continue;
423                            if (!used.add(c)) continue; 
424                            double dist = Placement.getDistance(placement,c);
425                            if (dist>m.getInstructorNoPreferenceLimit() && dist<=m.getInstructorDiscouragedLimit()) pref = Math.max(pref,Constants.sPreferenceLevelDiscouraged);
426                            if (dist>m.getInstructorDiscouragedLimit() && (dist<=m.getInstructorProhibitedLimit() || iIgnoreDistances)) pref = Math.max(pref,Constants.sPreferenceLevelStronglyDiscouraged);
427                            if (!iIgnoreDistances && dist>m.getInstructorProhibitedLimit()) pref = Math.max(pref,Constants.sPreferenceLevelProhibited);
428                    }
429                }
430                int nextSlot = startSlot+placement.getTimeLocation().getLength();
431                if ((nextSlot/Constants.SLOTS_PER_DAY) == (startSlot/Constants.SLOTS_PER_DAY)) {
432                    Vector conf = getPlacements(nextSlot,placement);
433                    for (Enumeration i=conf.elements();i.hasMoreElements();) {
434                            Placement c = (Placement)i.nextElement();
435                            if (lecture.equals(c.variable())) continue;
436                            if (!used.add(c)) continue;
437                            double dist = Placement.getDistance(placement,c);
438                            if (dist>m.getInstructorNoPreferenceLimit() && dist<=m.getInstructorDiscouragedLimit()) pref = Math.max(pref,Constants.sPreferenceLevelDiscouraged);
439                            if (dist>m.getInstructorDiscouragedLimit() && (dist<=m.getInstructorProhibitedLimit() || iIgnoreDistances)) pref = Math.max(pref,Constants.sPreferenceLevelStronglyDiscouraged);
440                            if (!iIgnoreDistances && dist>m.getInstructorProhibitedLimit()) pref = Math.max(pref,Constants.sPreferenceLevelProhibited);
441                    }
442                }
443            }
444            return pref;
445        }
446    
447        /** Overall back-to-back preference of this instructor */
448        public int getPreference() {
449            /*
450            if (iPreference!=countPreference()) {
451                    System.err.println("InstructorConstraint.getPreference() is not working properly");
452            }
453            */
454            return iPreference;
455        }
456        public int countPreference() {
457            int pref = 0;
458            HashSet used = new HashSet();
459            TimetableModel m = (TimetableModel)getModel();
460            for (int slot=1;slot<iResource.length;slot++) {
461                if ((slot%Constants.SLOTS_PER_DAY)==0) continue;
462                for (Enumeration e=iResource[slot].elements();e.hasMoreElements();) {
463                    Placement placement = (Placement)e.nextElement();
464                    Vector prevPlacements = getPlacements(slot-1,placement);
465                    for (Enumeration i=prevPlacements.elements();i.hasMoreElements();) {
466                            Placement prevPlacement = (Placement)i.nextElement();
467                            if (!used.add(prevPlacement)) continue; 
468                            double dist = Placement.getDistance(prevPlacement,placement);
469                            if (dist>m.getInstructorNoPreferenceLimit() && dist<=m.getInstructorDiscouragedLimit()) pref+=Constants.sPreferenceLevelDiscouraged;
470                            if (dist>m.getInstructorDiscouragedLimit()) pref+=Constants.sPreferenceLevelStronglyDiscouraged;
471                    }
472                }
473            }
474            return pref;
475        }
476        
477        /** Worst back-to-back preference of this instructor */
478        public int getWorstPreference() {
479            return Constants.sPreferenceLevelStronglyDiscouraged*(variables().size()-1);
480        }
481        
482        public String getPuid() {
483            return iPuid;
484        }
485        
486        public boolean isIgnoreDistances() {
487            return iIgnoreDistances;
488        }
489    
490        public Long getType() { return iType; }
491        public void setType(Long type) { iType = type; }
492    }