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