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