001 package ttsolver.constraint;
002
003 import ifs.model.*;
004 import ifs.util.*;
005 import java.util.*;
006 import ttsolver.model.*;
007
008 /**
009 * Group constraint.
010 * <br>
011 * This constraint expresses relations between several classes, e.g., that two sections of the same lecture can not
012 * be taught at the same time, or that some classes have to be taught one immediately after another. It can be either
013 * hard or soft.
014 * <br><br>
015 * Following constraints are now supported:
016 * <table border='1'><tr><th>Constraint</th><thComment</th></tr>
017 * <tr><td>SAME_TIME</td><td>Same time: given classes have to be taught in the same hours. If the classes are of different length, the smaller one cannot start before the longer one and it cannot end after the longer one.</td></tr>
018 * <tr><td>SAME_DAYS</td><td>Same days: given classes have to be taught in the same day. If the classes are of different time patterns, the days of one class have to form a subset of the days of the other class.</td></tr>
019 * <tr><td>BTB</td><td>Back-to-back constraint: given classes have to be taught in the same room and they have to follow one strictly after another.</td></tr>
020 * <tr><td>BTB_TIME</td><td>Back-to-back constraint: given classes have to follow one strictly after another, but they can be taught in different rooms.</td></tr>
021 * <tr><td>DIFF_TIME</td><td>Different time: given classes cannot overlap in time.</td></tr>
022 * <tr><td>NHB(1), NHB(1.5), NHB(2), ... NHB(8)</td><td>Number of hours between: between the given classes, the exact number of hours have to be kept.</td></tr>
023 * <tr><td>SAME_START</td><td>Same starting hour: given classes have to start in the same hour.</td></tr>
024 * <tr><td>SAME_ROOM</td><td>Same room: given classes have to be placed in the same room.</td></tr>
025 * <tr><td>NHB_GTE(1)</td><td>Greater than or equal to 1 hour between: between the given classes, the number of hours have to be one or more.</td></tr>
026 * <tr><td>NHB_LT(6)</td><td>Less than 6 hours between: between the given classes, the number of hours have to be less than six.</td></tr>
027 * </table>
028 *
029 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomáš Müller</a>
030 * @version 1.0
031 */
032
033 public class GroupConstraint extends Constraint {
034 private long iId;
035 private int iPreference;
036 private int iType;
037 private boolean iIsRequired;
038 private boolean iIsProhibited;
039 private Counter iGlobalPreference = null;
040 private int iLastPreference = 0;
041
042 /** Same time: given classes have to be taught in the same hours. If the classes are of different length, the smaller one cannot start before the longer one and it cannot end after the longer one.*/
043 public static int TYPE_SAME_TIME = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("SAME_TIME").getId();
044 /** Same days: given classes have to be taught in the same day. If the classes are of different time patterns, the days of one class have to form a subset of the days of the other class. */
045 public static int TYPE_SAME_DAYS = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("SAME_DAYS").getId();
046 /** Back-to-back constraint: given classes have to be taught in the same room and they have to follow one strictly after another. */
047 public static int TYPE_BTB = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("BTB").getId();
048 /** Back-to-back constraint: given classes have to follow one strictly after another, but they can be taught in different rooms. */
049 public static int TYPE_BTB_TIME = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("BTB_TIME").getId();
050 /** Different time: given classes cannot overlap in time. */
051 public static int TYPE_DIFF_TIME = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("DIFF_TIME").getId();
052 /** One hour between: between the given classes, the exact number of hours have to be kept.*/
053 public static int TYPE_NHB_1 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB(1)").getId();
054 /** Two hours between: between the given classes, the exact number of hours have to be kept.*/
055 public static int TYPE_NHB_2 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB(2)").getId();
056 /** Three hours between: between the given classes, the exact number of hours have to be kept.*/
057 public static int TYPE_NHB_3 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB(3)").getId();
058 /** Four hours between: between the given classes, the exact number of hours have to be kept.*/
059 public static int TYPE_NHB_4 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB(4)").getId();
060 /** Five hours between: between the given classes, the exact number of hours have to be kept.*/
061 public static int TYPE_NHB_5 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB(5)").getId();
062 /** Six hours between: between the given classes, the exact number of hours have to be kept.*/
063 public static int TYPE_NHB_6 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB(6)").getId();
064 /** Seven hours between: between the given classes, the exact number of hours have to be kept.*/
065 public static int TYPE_NHB_7 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB(7)").getId();
066 /** Eight hours between: between the given classes, the exact number of hours have to be kept.*/
067 public static int TYPE_NHB_8 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB(8)").getId();
068 /** Same room: given classes have to placed in the same room.*/
069 public static int TYPE_SAME_START = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("SAME_START").getId();
070 /** Same room: given classes have to placed in the same room.*/
071 public static int TYPE_SAME_ROOM = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("SAME_ROOM").getId();
072 /** Greater than or equal to 1 hour between: between the given classes, the number of hours have to be one or more.*/
073 public static int TYPE_NHB_GTE_1 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB_GTE(1)").getId();
074 /** Less than 6 hours between: between the given classes, the number of hours have to be less than six. */
075 public static int TYPE_NHB_LT_6 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB_LT(6)").getId();
076 /** One and half hour between: between the given classes, the exact number of hours have to be kept.*/
077 public static int TYPE_NHB_1_5 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB(1.5)").getId();
078 /** Four and half hours between: between the given classes, the exact number of hours have to be kept.*/
079 public static int TYPE_NHB_4_5 = edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType("NHB(4.5)").getId();
080
081 /** Constructor
082 * @param id constraint id
083 * @param type constraint type (e.g, {@link GroupConstraint#TYPE_SAME_TIME})
084 * @param preference time preferent ("R" for required, "P" for prohibited, "-2", "-1", "1", "2" for soft preference)
085 */
086 public GroupConstraint(long id, int type, String preference) {
087 iId=id;
088 iType=type;
089 iIsRequired=preference.equals("R");
090 iIsProhibited=preference.equals("P");
091 iPreference=(iIsRequired?0:iIsProhibited?0:Integer.parseInt(preference));
092 }
093
094 /** Constructor
095 * @param id constraint id
096 * @param type constraint type (e.g, "SAME_TIME")
097 * @param preference time preferent ("R" for required, "P" for prohibited, "-2", "-1", "1", "2" for soft preference)
098 */
099 public GroupConstraint(long id, String type, String preference) {
100 this(id, edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType(type).getId(),preference);
101 }
102
103 /** Constraint id */
104 public long getId() { return iId; }
105 /** Constraint type (e.g, {@link GroupConstraint#TYPE_SAME_TIME} */
106 public int getType() { return iType; }
107 /** Constraint type (e.g, "SAME_TIME") */
108 public String getTypeStr() { return edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType(iType).getType(); }
109 /** Is constraint required */
110 public boolean isRequired() { return iIsRequired; }
111 /** Is constraint prohibited */
112 public boolean isProhibited() { return iIsProhibited; }
113 /** Prolog reference: "R" for required, "P" for prohibited", "-2",.."2" for preference */
114 public String getPrologPreference() { return (iIsRequired?"R":iIsProhibited?"P":String.valueOf(iPreference)); }
115 /** Global preference counter */
116 public void setGlobalPreference(Counter globalPreference) { iGlobalPreference = globalPreference; }
117 /** Global preference counter */
118 public Counter getGlobalPreference() { return iGlobalPreference;}
119
120
121 public boolean isConsistent(Value value1, Value value2) {
122 if (!isHard()) return true;
123 if (!isSatisfiedPair((Lecture)value1.variable(), (Placement)value1, (Lecture)value2.variable(), (Placement)value2))
124 return false;
125 if (isBackToBack(getType())) {
126 Hashtable assignments = new Hashtable();
127 assignments.put(value1.variable(), value1);
128 assignments.put(value2.variable(), value2);
129 if (!isSatisfiedSeq(assignments, false, null))
130 return false;
131 }
132 return true;
133 }
134
135 public void computeConflicts(Value value, Set conflicts) {
136 if (!isHard()) return;
137 for (Enumeration e=variables().elements();e.hasMoreElements();) {
138 Variable v = (Variable)e.nextElement();
139 if (v.equals(value.variable())) continue; //ignore this variable
140 if (v.getAssignment()==null) continue; //there is an unassigned variable -- great, still a chance to get violated
141 if (!isSatisfiedPair((Lecture)v, (Placement)v.getAssignment(), (Lecture)value.variable(), (Placement)value))
142 conflicts.add(v.getAssignment());
143 }
144 Hashtable assignments = new Hashtable();
145 assignments.put(value.variable(), value);
146 isSatisfiedSeq(assignments, true, conflicts);
147 }
148
149 public boolean inConflict(Value value) {
150 if (!isHard()) return false;
151 for (Enumeration e=variables().elements();e.hasMoreElements();) {
152 Variable v = (Variable)e.nextElement();
153 if (v.equals(value.variable())) continue; //ignore this variable
154 if (v.getAssignment()==null) continue; //there is an unassigned variable -- great, still a chance to get violated
155 if (!isSatisfiedPair((Lecture)v, (Placement)v.getAssignment(), (Lecture)value.variable(), (Placement)value))
156 return true;
157 }
158 Hashtable assignments = new Hashtable();
159 assignments.put(value.variable(), value);
160 return isSatisfiedSeq(assignments, true, null);
161 }
162
163 /** Constraint preference (0 if prohibited or reqired) */
164 public int getPreference() {
165 return iPreference;
166 }
167
168 /** Current constraint preference (0 if prohibited or reqired, depends on current satisfaction of the constraint) */
169 public int getCurrentPreference() {
170 if (isHard()) return 0; //no preference
171 if (countAssignedVariables()<2) return 0;
172 if (iPreference<0) { //preference version (violated -> 0, satisfied -> preference)
173 for (Enumeration e1=variables().elements();e1.hasMoreElements();) {
174 Variable v1 = (Variable)e1.nextElement();
175 if (v1.getAssignment()==null) continue;
176 for (Enumeration e2=variables().elements();e2.hasMoreElements();) {
177 Variable v2 = (Variable)e2.nextElement();
178 if (v2.getAssignment()==null) continue;
179 if (v1.equals(v2)) continue;
180 if (!isSatisfiedPair((Lecture)v1, (Placement)v1.getAssignment(), (Lecture)v2, (Placement)v2.getAssignment()))
181 return 0;
182 }
183 }
184 if (!isSatisfiedSeq(null, true, null))
185 return 0;
186 return iPreference;
187 } else { //discouraged version (violated -> prefernce, satisfied -> 0)
188 for (Enumeration e1=variables().elements();e1.hasMoreElements();) {
189 Variable v1 = (Variable)e1.nextElement();
190 if (v1.getAssignment()==null) continue;
191 for (Enumeration e2=variables().elements();e2.hasMoreElements();) {
192 Variable v2 = (Variable)e2.nextElement();
193 if (v2.getAssignment()==null) continue;
194 if (v1.equals(v2)) continue;
195 if (!isSatisfiedPair((Lecture)v1, (Placement)v1.getAssignment(), (Lecture)v2, (Placement)v2.getAssignment()))
196 return iPreference;
197 }
198 }
199 if (!isSatisfiedSeq(null, true, null))
200 return iPreference;
201 return 0;
202 }
203 }
204
205 /** Current constraint preference (if given placement is assigned) */
206 public int getCurrentPreference(Placement placement) {
207 if (isHard()) return 0; //no preference
208 if (iPreference<0) { //preference version
209 for (Enumeration e1=variables().elements();e1.hasMoreElements();) {
210 Variable v1 = (Variable)e1.nextElement();
211 if (v1.getAssignment()==null) continue;
212 if (v1.equals(placement.variable())) continue;
213 if (!isSatisfiedPair((Lecture)v1, (Placement)v1.getAssignment(), (Lecture)placement.variable(), placement))
214 return 0;
215 }
216 if (isBackToBack(getType())) {
217 Hashtable assignment = new Hashtable();
218 assignment.put(placement.variable(), placement);
219 if (!isSatisfiedSeq(assignment, true, null))
220 return 0;
221 }
222 return iPreference;
223 } else { //discouraged version
224 for (Enumeration e1=variables().elements();e1.hasMoreElements();) {
225 Variable v1 = (Variable)e1.nextElement();
226 if (v1.getAssignment()==null) continue;
227 if (v1.equals(placement.variable())) continue;
228 if (!isSatisfiedPair((Lecture)v1, (Placement)v1.getAssignment(), (Lecture)placement.variable(), placement))
229 return iPreference;
230 }
231 if (isBackToBack(getType())) {
232 Hashtable assignment = new Hashtable();
233 assignment.put(placement.variable(), placement);
234 if (!isSatisfiedSeq(assignment, true, null))
235 return iPreference;
236 }
237 return 0;
238 }
239 }
240
241 public void unassigned(long iteration, Value value) {
242 super.unassigned(iteration, value);
243 if (iIsRequired || iIsProhibited) return;
244 iGlobalPreference.dec(iLastPreference);
245 iLastPreference = getCurrentPreference();
246 iGlobalPreference.inc(iLastPreference);
247 }
248
249 public void assigned(long iteration, Value value) {
250 super.assigned(iteration, value);
251 if (iIsRequired || iIsProhibited) return;
252 iGlobalPreference.dec(iLastPreference);
253 iLastPreference = getCurrentPreference();
254 iGlobalPreference.inc(iLastPreference);
255 }
256
257 public String toString() {
258 return "GroupConstraint{id="+iId+", type="+edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType(iType).getType()+", preference="+getPrologPreference()+", variables="+variables().size()+", lectures"+ToolBox.col2string(variables(),4)+"\n }";
259 }
260
261 public boolean isHard() {return iIsRequired || iIsProhibited; }
262
263 public String getName() {
264 StringBuffer varNames = new StringBuffer();
265 for (Enumeration e=variables().elements();e.hasMoreElements();)
266 varNames.append(varNames.length()>0?", ":"").append(((Variable)e.nextElement()).getName());
267 return edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType(iType).getDescription()+" ["+varNames+"]";
268 }
269 public String getDescription() { return edu.purdue.smas.timetable.data.Preferences.getPreference(getPrologPreference()).getName().toLowerCase(); }
270
271
272 private static int getGapMin(int type) {
273 if (type==TYPE_BTB) return 0;
274 else if (type==TYPE_BTB_TIME) return 0;
275 else if (type==TYPE_NHB_1) return 2;
276 else if (type==TYPE_NHB_1_5) return 3;
277 else if (type==TYPE_NHB_2) return 4;
278 else if (type==TYPE_NHB_3) return 6;
279 else if (type==TYPE_NHB_4) return 8;
280 else if (type==TYPE_NHB_4_5) return 9;
281 else if (type==TYPE_NHB_5) return 10;
282 else if (type==TYPE_NHB_6) return 12;
283 else if (type==TYPE_NHB_7) return 14;
284 else if (type==TYPE_NHB_8) return 16;
285 else if (type==TYPE_NHB_GTE_1) return 1;
286 else if (type==TYPE_NHB_LT_6) return 0;
287 return -1;
288 }
289
290 private static int getGapMax(int type) {
291 if (type==TYPE_BTB) return 0;
292 else if (type==TYPE_BTB_TIME) return 0;
293 else if (type==TYPE_NHB_1) return 2;
294 else if (type==TYPE_NHB_1_5) return 3;
295 else if (type==TYPE_NHB_2) return 4;
296 else if (type==TYPE_NHB_3) return 6;
297 else if (type==TYPE_NHB_4) return 8;
298 else if (type==TYPE_NHB_4_5) return 9;
299 else if (type==TYPE_NHB_5) return 10;
300 else if (type==TYPE_NHB_6) return 12;
301 else if (type==TYPE_NHB_7) return 14;
302 else if (type==TYPE_NHB_8) return 16;
303 else if (type==TYPE_NHB_GTE_1) return edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY;
304 else if (type==TYPE_NHB_LT_6) return 11;
305 return -1;
306 }
307
308 private static boolean isBackToBack(int type) {
309 if (type==TYPE_BTB) return true;
310 if (type==TYPE_BTB_TIME) return true;
311 if (type==TYPE_NHB_1) return true;
312 if (type==TYPE_NHB_1_5) return true;
313 if (type==TYPE_NHB_2) return true;
314 if (type==TYPE_NHB_3) return true;
315 if (type==TYPE_NHB_4) return true;
316 if (type==TYPE_NHB_4_5) return true;
317 if (type==TYPE_NHB_5) return true;
318 if (type==TYPE_NHB_6) return true;
319 if (type==TYPE_NHB_7) return true;
320 if (type==TYPE_NHB_8) return true;
321 if (type==TYPE_NHB_GTE_1) return true;
322 if (type==TYPE_NHB_LT_6) return true;
323 return false;
324 }
325
326 private static boolean isBackToBackTime(int type) {
327 if (type==TYPE_BTB_TIME) return true;
328 if (type==TYPE_NHB_1) return true;
329 if (type==TYPE_NHB_1_5) return true;
330 if (type==TYPE_NHB_2) return true;
331 if (type==TYPE_NHB_3) return true;
332 if (type==TYPE_NHB_4) return true;
333 if (type==TYPE_NHB_4_5) return true;
334 if (type==TYPE_NHB_5) return true;
335 if (type==TYPE_NHB_6) return true;
336 if (type==TYPE_NHB_7) return true;
337 if (type==TYPE_NHB_8) return true;
338 if (type==TYPE_NHB_GTE_1) return true;
339 if (type==TYPE_NHB_LT_6) return true;
340 return false;
341 }
342
343 private static boolean sameRoom(int type) {
344 if (type==TYPE_SAME_ROOM) return true;
345 return false;
346 }
347
348 private static boolean sameStartHour(int type) {
349 return (type==TYPE_SAME_START);
350 }
351
352 private static boolean sameHours(int type) {
353 return (type==TYPE_SAME_TIME);
354 }
355
356 private static boolean sameDays(int type) {
357 if (type==TYPE_SAME_DAYS) return true;
358 return false;
359 }
360
361 private static boolean notOverlap(int type) {
362 return (type==TYPE_DIFF_TIME);
363 }
364
365 private static boolean sameDays(int[] days1, int[] days2) {
366 if (days2.length<days1.length) return sameDays(days2,days1);
367 int i2=0;
368 for (int i1=0;i1<days1.length;i1++) {
369 int d1 = days1[i1] / edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY;
370 while (true) {
371 if (i2==days2.length) return false;
372 int d2 = days2[i2] / edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY;
373 if (d1==d2) break;
374 i2++;
375 if (i2==days2.length) return false;
376 }
377 i2++;
378 }
379 return true;
380 }
381
382 private static boolean sameHours(int start1, int len1, int start2, int len2) {
383 if (len1>len2) return sameHours(start2, len2, start1, len1);
384 start1 %= edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY;
385 start2 %= edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY;
386 return (start1>=start2 && start1+len1<=start2+len2);
387 }
388
389 private static boolean canFill(int totalGap, int gapMin, int gapMax, Vector lengths) {
390 if (gapMin<=totalGap && totalGap<=gapMax) return true;
391 if (totalGap<2*gapMin) return false;
392 for (int i=0;i<lengths.size();i++) {
393 int length = ((Integer)lengths.elementAt(i)).intValue(); lengths.removeElementAt(i);
394 for (int gap=gapMin;gap<=gapMax;gap++)
395 if (canFill(totalGap-gap-length,gapMin, gapMax, lengths)) return true;
396 lengths.insertElementAt(new Integer(length), i);
397 }
398 return false;
399 }
400
401
402 private boolean isSatisfiedSeq(Hashtable assignments, boolean considerCurrentAssignments, Set conflicts) {
403 int gapMin = getGapMin(getType());
404 int gapMax = getGapMax(getType());
405 if (gapMin<0 || gapMax<0) return true;
406
407 Vector lengths = new FastVector();
408
409 Placement [] res = new Placement[edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY];
410 for (int i=0;i<edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY;i++) res[i]=null;
411
412 Vector unAssignedLectures = new Vector();
413 int nrLectures=0;
414 String roomId = null;
415
416 for (Enumeration e=variables().elements();e.hasMoreElements();) {
417 Lecture lecture = (Lecture)e.nextElement();
418 Placement placement = (Placement)(assignments==null?null:assignments.get(lecture));
419 if (placement==null && considerCurrentAssignments) placement = (Placement)lecture.getAssignment();
420 if (placement==null) {
421 lengths.addElement(new Integer(((Placement)lecture.values().firstElement()).getTimeLocation().getLength()));
422 } else {
423 int pos=placement.getTimeLocation().getSlots()[0] % edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY;
424 int length=placement.getTimeLocation().getLength();
425 for (int j=pos;j<pos+length;j++) {
426 if (res[j]!=null) {
427 if (conflicts==null) return false;
428 if (!assignments.containsKey(lecture))
429 conflicts.add(placement);
430 else if (!assignments.containsKey(res[j].variable()))
431 conflicts.add(res[j]);
432 }
433 }
434 for (int j=pos;j<pos+length;j++) res[j]=placement;
435 nrLectures++;
436 }
437 }
438 if (nrLectures<=1) return true;
439
440 if (iIsRequired || (!iIsProhibited && iPreference<0)) {
441 int i=0;
442 Placement p=res[i];
443 while (p==null) p=res[++i];
444 i+=res[i].getTimeLocation().getLength();
445 nrLectures--;
446 while (nrLectures>0) {
447 int gap=0;
448 while (i<edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY && res[i]==null) { gap++; i++; }
449 if (i==edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY) break;
450 if (!canFill(gap, gapMin, gapMax, lengths)) {
451 if (conflicts==null) return false;
452 if (assignments==null || !assignments.containsKey(p.variable()))
453 conflicts.add(p);
454 else if (!assignments.containsKey(res[i].variable()))
455 conflicts.add(res[i]);
456 }
457 p=res[i];
458 i+=res[i].getTimeLocation().getLength();
459 nrLectures--;
460 }
461 } else if (iIsProhibited || (!iIsRequired && iPreference>0)) {
462 int i=0;
463 Placement p=res[i];
464 while (p==null) p=res[++i];
465 i+=res[i].getTimeLocation().getLength();
466 nrLectures--;
467 while (nrLectures>0) {
468 int gap=0;
469 while (i<edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY && res[i]==null) { gap++; i++; }
470 if (i==edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY) break;
471 if ((gapMin==0 || !canFill(gap, 0, gapMin-1, lengths)) &&
472 (gapMax>=edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY || !canFill(gap, gapMax+1, edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY, lengths))) {
473 if (conflicts==null) return false;
474 if (assignments==null || !assignments.containsKey(p.variable()))
475 conflicts.add(p);
476 else if (!assignments.containsKey(res[i].variable()))
477 conflicts.add(res[i]);
478 }
479 p=res[i];
480 i+=res[i].getTimeLocation().getLength();
481 nrLectures--;
482 }
483 }
484 return true;
485 }
486
487 private boolean isSatisfiedPair(Lecture lec1, Placement plc1, Lecture lec2, Placement plc2) {
488 if (iIsRequired || (!iIsProhibited && iPreference<0)) {
489 if (sameRoom(getType()) || (isBackToBack(getType()) && !isBackToBackTime(getType()))) {
490 if (!plc1.getRoomLocation().getId().equals(plc2.getRoomLocation().getId()))
491 return false;
492 }
493 if (sameStartHour(getType())) {
494 if ((plc1.getTimeLocation().getStartSlot()%edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY) !=
495 (plc2.getTimeLocation().getStartSlot()%edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY))
496 return false;
497 }
498 if (sameHours(getType())) {
499 if (!sameHours(plc1.getTimeLocation().getStartSlot(), plc1.getTimeLocation().getLength(),
500 plc2.getTimeLocation().getStartSlot(), plc2.getTimeLocation().getLength()))
501 return false;
502 }
503 if (sameDays(getType()) || isBackToBack(getType())) {
504 if (!sameDays(plc1.getTimeLocation().getStartSlots(), plc2.getTimeLocation().getStartSlots()))
505 return false;
506 }
507 if (notOverlap(getType())) {
508 if (plc1.getTimeLocation().hasIntersection(plc2.getTimeLocation()))
509 return false;
510 }
511 } else if (iIsProhibited || (!iIsRequired && iPreference>0)) {
512 if (sameRoom(getType())) {
513 if (plc1.getRoomLocation().getId().equals(plc2.getRoomLocation().getId()))
514 return false;
515 }
516 if (sameHours(getType())) {
517 if (plc1.getTimeLocation().shareHours(plc2.getTimeLocation()))
518 return false;
519 }
520 if (sameStartHour(getType())) {
521 if ((plc1.getTimeLocation().getStartSlot()%edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY) ==
522 (plc2.getTimeLocation().getStartSlot()%edu.purdue.smas.timetable.util.Constants.SLOTS_PER_DAY))
523 return false;
524 }
525 if (sameDays(getType())) {
526 if (plc1.getTimeLocation().shareDays(plc2.getTimeLocation()))
527 return false;
528 }
529 if (notOverlap(getType())) {
530 if (!plc1.getTimeLocation().hasIntersection(plc2.getTimeLocation()))
531 return false;
532 }
533 if (isBackToBack(getType())) { //still same time
534 if (!sameDays(plc1.getTimeLocation().getStartSlots(), plc2.getTimeLocation().getStartSlots()))
535 return false;
536 if (!isBackToBackTime(getType())) { //still same room
537 if (!plc1.getRoomLocation().getId().equals(plc2.getRoomLocation().getId()))
538 return false;
539 }
540 }
541 }
542 return true;
543 }
544
545 /*
546 public void getInfo(Dictionary info) {
547 StringBuffer varNames = new StringBuffer();
548 for (Enumeration e=variables().elements();e.hasMoreElements();) {
549 Variable variable = (Variable)e.nextElement();
550 varNames.append(varNames.length()>0?", ":"");
551 varNames.append(variable.getName());
552 if (variable.getAssignment()!=null)
553 varNames.append(" "+variable.getAssignment().getName());
554 }
555 info.put("gc"+iId, edu.purdue.smas.timetable.data.GroupConstraintTypes.getGroupConstraintType(iType).getDescription()+" (pref="+getDescription()+" ("+iIsRequired+"/"+iIsProhibited+"/"+iPreference+")"+", current="+getCurrentPreference()+", vars=["+varNames+"])");
556 }
557 */
558
559 }