001    package net.sf.cpsolver.coursett.constraint;
002    
003    import java.util.Enumeration;
004    import java.util.HashSet;
005    import java.util.Hashtable;
006    import java.util.Set;
007    import java.util.Vector;
008    
009    import org.apache.log4j.Logger;
010    
011    
012    import net.sf.cpsolver.coursett.Constants;
013    import net.sf.cpsolver.coursett.model.Lecture;
014    import net.sf.cpsolver.coursett.model.Placement;
015    import net.sf.cpsolver.coursett.model.TimeLocation;
016    import net.sf.cpsolver.coursett.model.TimetableModel;
017    import net.sf.cpsolver.ifs.model.Constraint;
018    import net.sf.cpsolver.ifs.model.Value;
019    import net.sf.cpsolver.ifs.model.Variable;
020    import net.sf.cpsolver.ifs.util.FastVector;
021    
022    /**
023     * Group constraint.
024     * <br>
025     * This constraint expresses relations between several classes, e.g., that two sections of the same lecture can not
026     * be taught at the same time, or that some classes have to be taught one immediately after another. It can be either
027     * hard or soft.
028     * <br><br>
029     * Following constraints are now supported:
030     * <table border='1'><tr><th>Constraint</th><th>Comment</th></tr>
031     * <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>
032     * <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>
033     * <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>
034     * <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>
035     * <tr><td>DIFF_TIME</td><td>Different time: given classes cannot overlap in time.</td></tr>
036     * <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>
037     * <tr><td>SAME_START</td><td>Same starting hour: given classes have to start in the same hour.</td></tr>
038     * <tr><td>SAME_ROOM</td><td>Same room: given classes have to be placed in the same room.</td></tr>
039     * <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>
040     * <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>
041     * </table>
042     *
043     * @version
044     * CourseTT 1.1 (University Course Timetabling)<br>
045     * Copyright (C) 2006 Tomáš Müller<br>
046     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
047     * Lazenska 391, 76314 Zlin, Czech Republic<br>
048     * <br>
049     * This library is free software; you can redistribute it and/or
050     * modify it under the terms of the GNU Lesser General Public
051     * License as published by the Free Software Foundation; either
052     * version 2.1 of the License, or (at your option) any later version.
053     * <br><br>
054     * This library is distributed in the hope that it will be useful,
055     * but WITHOUT ANY WARRANTY; without even the implied warranty of
056     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
057     * Lesser General Public License for more details.
058     * <br><br>
059     * You should have received a copy of the GNU Lesser General Public
060     * License along with this library; if not, write to the Free Software
061     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
062     */
063    
064    public class GroupConstraint extends Constraint {
065            private static Logger sLogger = Logger.getLogger(GroupConstraint.class);
066        private Long iId;
067        private int iPreference;
068        private String iType;
069        private boolean iIsRequired;
070        private boolean iIsProhibited;
071        private int iLastPreference = 0;
072        
073        /** 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.*/
074        public static String TYPE_SAME_TIME = "SAME_TIME";
075        /** 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. */
076        public static String TYPE_SAME_DAYS  = "SAME_DAYS";
077        /** Back-to-back constraint: given classes have to be taught in the same room and they have to follow one strictly after another. */
078        public static String TYPE_BTB       = "BTB";
079        /** Back-to-back constraint: given classes have to follow one strictly after another, but they can be taught in different rooms. */
080        public static String TYPE_BTB_TIME  = "BTB_TIME";
081        /** Different time: given classes cannot overlap in time. */
082        public static String TYPE_DIFF_TIME = "DIFF_TIME";
083        /** One hour between: between the given classes, the exact number of hours have to be kept.*/
084        public static String TYPE_NHB_1     = "NHB(1)";
085        /** Two hours between: between the given classes, the exact number of hours have to be kept.*/
086        public static String TYPE_NHB_2     = "NHB(2)";
087        /** Three hours between: between the given classes, the exact number of hours have to be kept.*/
088        public static String TYPE_NHB_3     = "NHB(3)";
089        /** Four hours between: between the given classes, the exact number of hours have to be kept.*/
090        public static String TYPE_NHB_4     = "NHB(4)";
091        /** Five hours between: between the given classes, the exact number of hours have to be kept.*/
092        public static String TYPE_NHB_5     = "NHB(5)";
093        /** Six hours between: between the given classes, the exact number of hours have to be kept.*/
094        public static String TYPE_NHB_6     = "NHB(6)";
095        /** Seven hours between: between the given classes, the exact number of hours have to be kept.*/
096        public static String TYPE_NHB_7     = "NHB(7)";
097        /** Eight hours between: between the given classes, the exact number of hours have to be kept.*/
098        public static String TYPE_NHB_8     = "NHB(8)";
099        /** Same room: given classes have to placed in the same room.*/
100        public static String TYPE_SAME_START = "SAME_START";
101        /** Same room: given classes have to placed in the same room.*/
102        public static String TYPE_SAME_ROOM = "SAME_ROOM";
103        /** Greater than or equal to 1 hour between: between the given classes, the number of hours have to be one or more.*/
104        public static String TYPE_NHB_GTE_1 = "NHB_GTE(1)";
105        /** Less than 6 hours between: between the given classes, the number of hours have to be less than six. */
106        public static String TYPE_NHB_LT_6  = "NHB_LT(6)";
107        /** One and half hour between: between the given classes, the exact number of hours have to be kept.*/
108        public static String TYPE_NHB_1_5   = "NHB(1.5)";
109        /** Four and half hours between: between the given classes, the exact number of hours have to be kept.*/
110        public static String TYPE_NHB_4_5   = "NHB(4.5)";
111        public static String TYPE_SAME_STUDENTS = "SAME_STUDENTS";
112        public static String TYPE_SAME_INSTR = "SAME_INSTR";
113        public static String TYPE_CAN_SHARE_ROOM = "CAN_SHARE_ROOM";
114        public static String TYPE_PRECEDENCE = "PRECEDENCE";
115        public static String TYPE_BTB_DAY = "BTB_DAY";
116        public static String TYPE_MEET_WITH = "MEET_WITH";
117        public static String TYPE_NDB_GT_1 = "NDB_GT_1";
118        public static String TYPE_CH_NOTOVERLAP = "CH_NOTOVERLAP";
119        public static String TYPE_FOLLOWING_DAY = "FOLLOWING_DAY";
120        public static String TYPE_EVERY_OTHER_DAY = "EVERY_OTHER_DAY";
121        
122        
123        public GroupConstraint() {}
124        
125        public void addVariable(Variable variable) {
126            if (!variables().contains(variable)) super.addVariable(variable);
127            if (isChildrenNotOverlap(getType())) {
128                    Lecture lecture = (Lecture)variable;
129                    if (lecture.getChildrenSubpartIds()!=null) {
130                    for (Enumeration e1=lecture.getChildrenSubpartIds();e1.hasMoreElements();) {
131                            Long subpartId = (Long)e1.nextElement();
132                            for (Enumeration e2=lecture.getChildren(subpartId).elements();e2.hasMoreElements();) {
133                                    Lecture ch = (Lecture)e2.nextElement();
134                                    if (!variables().contains(ch)) super.addVariable(ch);
135                            }
136                    }
137                    }
138            }
139        }
140        
141        public void removeVariable(Variable variable) {
142            if (variables().contains(variable)) super.removeVariable(variable);
143            if (isChildrenNotOverlap(getType())) {
144                    Lecture lecture = (Lecture)variable;
145                    if (lecture.getChildrenSubpartIds()!=null) {
146                    for (Enumeration e1=lecture.getChildrenSubpartIds();e1.hasMoreElements();) {
147                            Long subpartId = (Long)e1.nextElement();
148                            for (Enumeration e2=lecture.getChildren(subpartId).elements();e2.hasMoreElements();) {
149                                    Lecture ch = (Lecture)e2.nextElement();
150                                    if (variables().contains(ch)) super.removeVariable(ch);
151                            }
152                    }
153                    }
154            }
155        }
156        
157        /** Constructor
158         * @param id constraint id
159         * @param type constraString type (e.g, "SAME_TIME")
160         * @param preference time preferent ("R" for required, "P" for prohibited, "-2", "-1", "1", "2" for soft preference)
161         */
162        public GroupConstraint(Long id, String type, String preference) {
163            iId=id;
164            iType=type;
165            iIsRequired=preference.equals(Constants.sPreferenceRequired);
166            iIsProhibited=preference.equals(Constants.sPreferenceProhibited);
167            iPreference=Constants.preference2preferenceLevel(preference);
168        }
169        
170        /** Constraint id */
171        public Long getConstraintId() { return iId; }
172        public long getId() { return (iId==null?-1:iId.longValue()); }
173        /** ConstraString type (e.g, {@link GroupConstraint#TYPE_SAME_TIME} */
174        public String getType() { return iType; }
175        public void setType(String type) { iType = type; }
176        /** Is constraint required */
177        public boolean isRequired() { return iIsRequired; }
178        /** Is constraint prohibited */
179        public boolean isProhibited() { return iIsProhibited; }
180        /** Prolog reference: "R" for required, "P" for prohibited", "-2",.."2" for preference */
181        public String getPrologPreference() { return Constants.preferenceLevel2preference(iPreference); }
182        
183        
184        public boolean isConsistent(Value value1, Value value2) {
185            if (!isHard()) return true;
186            if (!isSatisfiedPair((Lecture)value1.variable(), (Placement)value1, (Lecture)value2.variable(), (Placement)value2))
187                return false;
188            if (isBackToBack(getType())) {
189                Hashtable assignments = new Hashtable();
190                assignments.put(value1.variable(), value1);
191                assignments.put(value2.variable(), value2);
192                if (!isSatisfiedSeq(assignments, false, null))
193                    return false;
194            }
195            return true;
196        }
197        
198        public void computeConflicts(Value value, Set conflicts) {
199            if (!isHard()) return;
200            for (Enumeration e=variables().elements();e.hasMoreElements();) {
201                Variable v = (Variable)e.nextElement();
202                if (v.equals(value.variable())) continue; //ignore this variable
203                if (v.getAssignment()==null) continue; //there is an unassigned variable -- great, still a chance to get violated
204                if (!isSatisfiedPair((Lecture)v, (Placement)v.getAssignment(), (Lecture)value.variable(), (Placement)value))
205                    conflicts.add(v.getAssignment());
206            }
207            Hashtable assignments = new Hashtable();
208            assignments.put(value.variable(), value);
209            if (!isSatisfiedSeq(assignments, true, conflicts))
210                    conflicts.add(value);
211        }
212        
213        public boolean inConflict(Value value) {
214            if (!isHard()) return false;
215            for (Enumeration e=variables().elements();e.hasMoreElements();) {
216                Variable v = (Variable)e.nextElement();
217                if (v.equals(value.variable())) continue; //ignore this variable
218                if (v.getAssignment()==null) continue; //there is an unassigned variable -- great, still a chance to get violated
219                if (!isSatisfiedPair((Lecture)v, (Placement)v.getAssignment(), (Lecture)value.variable(), (Placement)value))
220                    return true;
221            }
222            Hashtable assignments = new Hashtable();
223            assignments.put(value.variable(), value);
224            return isSatisfiedSeq(assignments, true, null);
225        }
226        
227        /** Constraint preference (0 if prohibited or reqired) */
228        public int getPreference() {
229            return iPreference;
230        }
231        
232        /** Current constraint preference (0 if prohibited or reqired, depends on current satisfaction of the constraint) */
233        public int getCurrentPreference() { 
234            if (isHard()) return 0; //no preference
235            if (countAssignedVariables()<2) return 0;
236            if (iPreference<0) { //preference version (violated -> 0, satisfied -> preference)
237                for (Enumeration e1=variables().elements();e1.hasMoreElements();) {
238                    Variable v1 = (Variable)e1.nextElement();
239                    if (v1.getAssignment()==null) continue;
240                    for (Enumeration e2=variables().elements();e2.hasMoreElements();) {
241                        Variable v2 = (Variable)e2.nextElement();
242                        if (v2.getAssignment()==null) continue;
243                        if (v1.equals(v2)) continue;
244                        if (!isSatisfiedPair((Lecture)v1, (Placement)v1.getAssignment(), (Lecture)v2, (Placement)v2.getAssignment()))
245                            return 0;
246                    }
247                }
248                if (!isSatisfiedSeq(null, true, null))
249                    return 0;
250                return iPreference;
251            } else { //discouraged version (violated -> prefernce, satisfied -> 0)
252                for (Enumeration e1=variables().elements();e1.hasMoreElements();) {
253                    Variable v1 = (Variable)e1.nextElement();
254                    if (v1.getAssignment()==null) continue;
255                    for (Enumeration e2=variables().elements();e2.hasMoreElements();) {
256                        Variable v2 = (Variable)e2.nextElement();
257                        if (v2.getAssignment()==null) continue;
258                        if (v1.equals(v2)) continue;
259                        if (!isSatisfiedPair((Lecture)v1, (Placement)v1.getAssignment(), (Lecture)v2, (Placement)v2.getAssignment()))
260                            return iPreference;
261                    }
262                }
263                if (!isSatisfiedSeq(null, true, null))
264                    return iPreference;
265                return 0;
266            }
267        }
268        
269        /** Current constraint preference (if given placement is assigned) */
270        public int getCurrentPreference(Placement placement) { 
271            //if (isHard()) return 0; //no preference
272            if (iPreference<0) { //preference version
273                for (Enumeration e1=variables().elements();e1.hasMoreElements();) {
274                    Variable v1 = (Variable)e1.nextElement();
275                    if (v1.getAssignment()==null) continue;
276                    if (v1.equals(placement.variable())) continue;
277                    if (!isSatisfiedPair((Lecture)v1, (Placement)v1.getAssignment(), (Lecture)placement.variable(), placement))
278                            return 0;
279                }
280                if (isBackToBack(getType())) {
281                    Hashtable assignment = new Hashtable();
282                    assignment.put(placement.variable(), placement);
283                    if (!isSatisfiedSeq(assignment, true, null))
284                        return 0;
285                }
286                return iPreference;
287            } else { //discouraged version
288                for (Enumeration e1=variables().elements();e1.hasMoreElements();) {
289                    Variable v1 = (Variable)e1.nextElement();
290                    if (v1.getAssignment()==null) continue;
291                    if (v1.equals(placement.variable())) continue;
292                    if (!isSatisfiedPair((Lecture)v1, (Placement)v1.getAssignment(), (Lecture)placement.variable(), placement))
293                            return iPreference;
294                }
295                if (isBackToBack(getType())) {
296                    Hashtable assignment = new Hashtable();
297                    assignment.put(placement.variable(), placement);
298                    if (!isSatisfiedSeq(assignment, true, null))
299                        return iPreference;
300                }
301                return 0;
302            }
303        }
304        
305        public void unassigned(long iteration, Value value) {
306            super.unassigned(iteration, value);
307            if (iIsRequired || iIsProhibited) return;
308            ((TimetableModel)getModel()).getGlobalGroupConstraintPreferenceCounter().dec(iLastPreference);
309            iLastPreference = getCurrentPreference();
310            ((TimetableModel)getModel()).getGlobalGroupConstraintPreferenceCounter().inc(iLastPreference);
311        }
312        
313        public void assigned(long iteration, Value value) {
314            super.assigned(iteration, value);
315            if (iIsRequired || iIsProhibited) return;
316            ((TimetableModel)getModel()).getGlobalGroupConstraintPreferenceCounter().dec(iLastPreference);
317            iLastPreference = getCurrentPreference();
318            ((TimetableModel)getModel()).getGlobalGroupConstraintPreferenceCounter().inc(iLastPreference);
319        }
320        
321        public String toString() {
322            StringBuffer sb = new StringBuffer();
323            sb.append(getName());
324            sb.append(" between ");
325            for (Enumeration e=variables().elements();e.hasMoreElements();) {
326                Variable v = (Variable)e.nextElement();
327                sb.append(v.getName());
328                if (e.hasMoreElements()) sb.append(", ");
329            }
330            return sb.toString();
331        }
332        
333        public boolean isHard() {return iIsRequired || iIsProhibited; }
334        
335        public String getName() {
336            return getName(iType);
337        }
338        
339        public static String getName(String type) {
340            return type;
341        }
342        
343        private static int getGapMin(String type) {
344            if (type.equals(TYPE_BTB)) return 0;
345            else if (type.equals(TYPE_BTB_TIME)) return 0;
346            else if (type.equals(TYPE_NHB_1)) return 6*2;
347            else if (type.equals(TYPE_NHB_1_5)) return 6*3;
348            else if (type.equals(TYPE_NHB_2)) return 6*4;
349            else if (type.equals(TYPE_NHB_3)) return 6*6;
350            else if (type.equals(TYPE_NHB_4)) return 6*8;
351            else if (type.equals(TYPE_NHB_4_5)) return 6*9;
352            else if (type.equals(TYPE_NHB_5)) return 6*10;
353            else if (type.equals(TYPE_NHB_6)) return 6*12;
354            else if (type.equals(TYPE_NHB_7)) return 6*14;
355            else if (type.equals(TYPE_NHB_8)) return 6*16;
356            else if (type.equals(TYPE_NHB_GTE_1)) return 6*1;
357            else if (type.equals(TYPE_NHB_LT_6)) return 0;
358            return -1;
359        }
360        
361        private static int getGapMax(String type) {
362            if (type.equals(TYPE_BTB)) return 0;
363            else if (type.equals(TYPE_BTB_TIME)) return 0;
364            else if (type.equals(TYPE_NHB_1)) return 6*2;
365            else if (type.equals(TYPE_NHB_1_5)) return 6*3;
366            else if (type.equals(TYPE_NHB_2)) return 6*4;
367            else if (type.equals(TYPE_NHB_3)) return 6*6;
368            else if (type.equals(TYPE_NHB_4)) return 6*8;
369            else if (type.equals(TYPE_NHB_4_5)) return 6*9;
370            else if (type.equals(TYPE_NHB_5)) return 6*10;
371            else if (type.equals(TYPE_NHB_6)) return 6*12;
372            else if (type.equals(TYPE_NHB_7)) return 6*14;
373            else if (type.equals(TYPE_NHB_8)) return 6*16;
374            else if (type.equals(TYPE_NHB_GTE_1)) return Constants.SLOTS_PER_DAY;
375            else if (type.equals(TYPE_NHB_LT_6)) return 6*11;
376            return -1;
377        }
378        
379        private static boolean isBackToBack(String type) {
380            if (type.equals(TYPE_BTB)) return true;
381            if (type.equals(TYPE_BTB_TIME)) return true;
382            if (type.equals(TYPE_NHB_1)) return true;
383            if (type.equals(TYPE_NHB_1_5)) return true;
384            if (type.equals(TYPE_NHB_2)) return true;
385            if (type.equals(TYPE_NHB_3)) return true;
386            if (type.equals(TYPE_NHB_4)) return true;
387            if (type.equals(TYPE_NHB_4_5)) return true;
388            if (type.equals(TYPE_NHB_5)) return true;
389            if (type.equals(TYPE_NHB_6)) return true;
390            if (type.equals(TYPE_NHB_7)) return true;
391            if (type.equals(TYPE_NHB_8)) return true;
392            if (type.equals(TYPE_NHB_GTE_1)) return true;
393            if (type.equals(TYPE_NHB_LT_6)) return true;
394            return false;
395        }
396        
397        private static boolean isBackToBackTime(String type) {
398            if (type.equals(TYPE_BTB_TIME)) return true;
399            if (type.equals(TYPE_NHB_1)) return true;
400            if (type.equals(TYPE_NHB_1_5)) return true;
401            if (type.equals(TYPE_NHB_2)) return true;
402            if (type.equals(TYPE_NHB_3)) return true;
403            if (type.equals(TYPE_NHB_4)) return true;
404            if (type.equals(TYPE_NHB_4_5)) return true;
405            if (type.equals(TYPE_NHB_5)) return true;
406            if (type.equals(TYPE_NHB_6)) return true;
407            if (type.equals(TYPE_NHB_7)) return true;
408            if (type.equals(TYPE_NHB_8)) return true;
409            if (type.equals(TYPE_NHB_GTE_1)) return true;
410            if (type.equals(TYPE_NHB_LT_6)) return true;
411            return false;
412        }
413        
414        private static boolean sameRoom(String type) {
415            if (type.equals(TYPE_SAME_ROOM)) return true;
416            if (type.equals(TYPE_MEET_WITH)) return true;
417            return false;
418        }
419        
420        private static boolean isPrecedence(String type) {
421            if (type.equals(TYPE_PRECEDENCE)) return true;
422            return false;
423        }
424        
425        private boolean isPrecedence(Placement p1, Placement p2, boolean firstGoesFirst) {
426            int ord1 = variables().indexOf(p1.variable());
427            int ord2 = variables().indexOf(p2.variable());
428            TimeLocation t1=null, t2=null;
429            if (ord1<ord2) {
430                    if (firstGoesFirst) {
431                            t1 = p1.getTimeLocation(); t2 = p2.getTimeLocation();
432                    } else {
433                            t2 = p1.getTimeLocation(); t1 = p2.getTimeLocation();
434                    }
435            } else {
436                    if (!firstGoesFirst) {
437                            t1 = p1.getTimeLocation(); t2 = p2.getTimeLocation();
438                    } else {
439                            t2 = p1.getTimeLocation(); t1 = p2.getTimeLocation();
440                    }
441            }
442            return t1.getStartSlots().nextInt()+t1.getLength()<=t2.getStartSlots().nextInt();
443        }
444    
445        private static boolean sameStudents(String type) {
446            if (type.equals(TYPE_SAME_STUDENTS)) return true;
447            return false;
448        }
449        
450        private static boolean sameInstructor(String type) {
451            if (type.equals(TYPE_SAME_INSTR)) return true;
452            return false;
453        }
454    
455        public static boolean canShareRooms(String type) {
456            if (type.equals(TYPE_CAN_SHARE_ROOM)) return true;
457            if (type.equals(TYPE_MEET_WITH)) return true;
458            return false;
459        }
460    
461        private static boolean sameStartHour(String type) {
462            return (type.equals(TYPE_SAME_START));
463        }
464        
465        private static boolean sameHours(String type) {
466            return (type.equals(TYPE_SAME_TIME) || type.equals(TYPE_MEET_WITH));
467        }
468        
469        private static boolean sameDays(String type) {
470            if (type.equals(TYPE_SAME_DAYS) || type.equals(TYPE_MEET_WITH)) return true;
471            return false;
472        }
473        
474        private static boolean notOverlap(String type) {
475            return (type.equals(TYPE_DIFF_TIME));
476        }
477        
478        private static boolean isBackToBackDay(String type) {
479            return (type.equals(TYPE_BTB_DAY));
480        }
481    
482        private static boolean isBackToBackDays(TimeLocation t1, TimeLocation t2) {
483            int f1 = -1, f2 = -1, e1 = -1, e2 = -1;
484            for (int i=0;i<Constants.DAY_CODES.length;i++) {
485                    if ((t1.getDayCode()&Constants.DAY_CODES[i])!=0) {
486                            if (f1<0) f1 = i;
487                            e1 = i;
488                    }
489                    if ((t2.getDayCode()&Constants.DAY_CODES[i])!=0) {
490                            if (f2<0) f2 = i;
491                            e2 = i;
492                    }
493            }
494            return (e1+1==f2) || (e2+1==f1);
495        }
496        
497        private static boolean isNrDaysBetweenGreaterThanOne(String type) {
498            return (type.equals(TYPE_NDB_GT_1));
499        }
500    
501        private static boolean isChildrenNotOverlap(String type) {
502            return (type.equals(TYPE_CH_NOTOVERLAP));
503        }
504        
505        private static boolean isFollowingDay(String type) {
506            return (type.equals(TYPE_FOLLOWING_DAY));
507        }
508        
509        private static boolean isEveryOtherDay(String type) {
510            return (type.equals(TYPE_EVERY_OTHER_DAY));
511        }
512        
513        private static boolean isNrDaysBetweenGreaterThanOne(TimeLocation t1, TimeLocation t2) {
514            int f1 = -1, f2 = -1, e1 = -1, e2 = -1;
515            for (int i=0;i<Constants.DAY_CODES.length;i++) {
516                    if ((t1.getDayCode()&Constants.DAY_CODES[i])!=0) {
517                            if (f1<0) f1 = i;
518                            e1 = i;
519                    }
520                    if ((t2.getDayCode()&Constants.DAY_CODES[i])!=0) {
521                            if (f2<0) f2 = i;
522                            e2 = i;
523                    }
524            }
525            return (e1-f2>2) || (e2-f1>2);
526        }
527    
528        private boolean isFollowingDay(Placement p1, Placement p2, boolean firstGoesFirst) {
529            int ord1 = variables().indexOf(p1.variable());
530            int ord2 = variables().indexOf(p2.variable());
531            TimeLocation t1=null, t2=null;
532            if (ord1<ord2) {
533                    if (firstGoesFirst) {
534                            t1 = p1.getTimeLocation(); t2 = p2.getTimeLocation();
535                    } else {
536                            t2 = p1.getTimeLocation(); t1 = p2.getTimeLocation();
537                    }
538            } else {
539                    if (!firstGoesFirst) {
540                            t1 = p1.getTimeLocation(); t2 = p2.getTimeLocation();
541                    } else {
542                            t2 = p1.getTimeLocation(); t1 = p2.getTimeLocation();
543                    }
544            }
545            int f1 = -1, f2 = -1, e1 = -1, e2 = -1;
546            for (int i=0;i<Constants.DAY_CODES.length;i++) {
547                    if ((t1.getDayCode()&Constants.DAY_CODES[i])!=0) {
548                            if (f1<0) f1 = i;
549                            e1 = i;
550                    }
551                    if ((t2.getDayCode()&Constants.DAY_CODES[i])!=0) {
552                            if (f2<0) f2 = i;
553                            e2 = i;
554                    }
555            }
556            return ((e1+1)%Constants.NR_DAYS_WEEK==f2);
557        }
558    
559        private boolean isEveryOtherDay(Placement p1, Placement p2, boolean firstGoesFirst) {
560            int ord1 = variables().indexOf(p1.variable());
561            int ord2 = variables().indexOf(p2.variable());
562            TimeLocation t1=null, t2=null;
563            if (ord1<ord2) {
564                    if (firstGoesFirst) {
565                            t1 = p1.getTimeLocation(); t2 = p2.getTimeLocation();
566                    } else {
567                            t2 = p1.getTimeLocation(); t1 = p2.getTimeLocation();
568                    }
569            } else {
570                    if (!firstGoesFirst) {
571                            t1 = p1.getTimeLocation(); t2 = p2.getTimeLocation();
572                    } else {
573                            t2 = p1.getTimeLocation(); t1 = p2.getTimeLocation();
574                    }
575            }
576            int f1 = -1, f2 = -1, e1 = -1, e2 = -1;
577            for (int i=0;i<Constants.DAY_CODES.length;i++) {
578                    if ((t1.getDayCode()&Constants.DAY_CODES[i])!=0) {
579                            if (f1<0) f1 = i;
580                            e1 = i;
581                    }
582                    if ((t2.getDayCode()&Constants.DAY_CODES[i])!=0) {
583                            if (f2<0) f2 = i;
584                            e2 = i;
585                    }
586            }
587            return ((e1+2)%Constants.NR_DAYS_WEEK==f2);
588        }
589    
590        private static boolean sameDays(int[] days1, int[] days2) {
591            if (days2.length<days1.length) return sameDays(days2,days1);
592            int i2=0;
593            for (int i1=0;i1<days1.length;i1++) {
594                int d1 = days1[i1];
595                while (true) {
596                    if (i2==days2.length) return false;
597                    int d2 = days2[i2];
598                    if (d1==d2) break;
599                    i2++;
600                    if (i2==days2.length) return false;
601                }
602                i2++;
603            }
604            return true;
605        }
606        
607        private static boolean sameHours(int start1, int len1, int start2, int len2) {
608            if (len1>len2) return sameHours(start2, len2, start1, len1);
609            start1 %= Constants.SLOTS_PER_DAY;
610            start2 %= Constants.SLOTS_PER_DAY;
611            return (start1>=start2 && start1+len1<=start2+len2);
612        }
613        
614        private static boolean canFill(int totalGap, int gapMin, int gapMax, Vector lengths) {
615            if (gapMin<=totalGap && totalGap<=gapMax) return true;
616            if (totalGap<2*gapMin) return false;
617            for (int i=0;i<lengths.size();i++) {
618                int length = ((Integer)lengths.elementAt(i)).intValue(); lengths.removeElementAt(i);
619                for (int gap=gapMin;gap<=gapMax;gap++)
620                    if (canFill(totalGap-gap-length,gapMin, gapMax, lengths)) return true;
621                lengths.insertElementAt(new Integer(length), i);
622            }
623            return false;
624        }
625        
626        private boolean isSatisfiedSeq(Hashtable assignments, boolean considerCurrentAssignments, Set conflicts) {
627            if (conflicts==null)
628                    return isSatisfiedSeqCheck(assignments, considerCurrentAssignments, conflicts);
629            else {
630                    Set bestConflicts = isSatisfiedRecursive(0, assignments, considerCurrentAssignments, conflicts, new HashSet(), null);
631                    if (bestConflicts==null) return false;
632                    conflicts.addAll(bestConflicts);
633                    return true;
634            }
635        }
636        
637        private Set isSatisfiedRecursive(int idx, Hashtable assignments, boolean considerCurrentAssignments, Set conflicts, Set newConflicts, Set bestConflicts) {
638            if (idx==variables().size() && newConflicts.isEmpty()) return bestConflicts;
639            if (isSatisfiedSeqCheck(assignments, considerCurrentAssignments, conflicts)) {
640                    if (bestConflicts==null || bestConflicts.size()>newConflicts.size())
641                            return new HashSet(newConflicts);
642                    return bestConflicts; 
643            }
644            if (idx==variables().size()) return bestConflicts;
645            bestConflicts = isSatisfiedRecursive(idx+1, assignments, considerCurrentAssignments, conflicts, newConflicts, bestConflicts);
646            Lecture lecture = (Lecture)variables().elementAt(idx);
647            if (assignments!=null && assignments.containsKey(lecture)) return bestConflicts;
648            Placement placement = (Placement)(assignments==null?null:assignments.get(lecture));
649            if (placement==null && considerCurrentAssignments) placement = (Placement)lecture.getAssignment();
650            if (placement==null) return bestConflicts;
651            if (conflicts!=null && conflicts.contains(placement)) return bestConflicts;
652            conflicts.add(placement);
653            newConflicts.add(placement);
654            bestConflicts = isSatisfiedRecursive(idx+1,assignments, considerCurrentAssignments, conflicts, newConflicts, bestConflicts);
655            newConflicts.remove(placement);
656            conflicts.remove(placement);
657            return bestConflicts;
658        }
659        
660        
661        private boolean isSatisfiedSeqCheck(Hashtable assignments, boolean considerCurrentAssignments, Set conflicts) {
662            int gapMin = getGapMin(getType());
663            int gapMax = getGapMax(getType());
664            if (gapMin<0 || gapMax<0) return true;
665            
666            Vector lengths = new FastVector();
667            
668            Placement [] res = new Placement[Constants.SLOTS_PER_DAY];
669            for (int i=0;i<Constants.SLOTS_PER_DAY;i++) res[i]=null;
670            
671            Vector unAssignedLectures = new Vector();
672            int nrLectures=0;
673            String roomId = null;
674            
675            for (Enumeration e=variables().elements();e.hasMoreElements();) {
676                Lecture lecture = (Lecture)e.nextElement();
677                Placement placement = (Placement)(assignments==null?null:assignments.get(lecture));
678                if (placement==null && considerCurrentAssignments) placement = (Placement)lecture.getAssignment();
679                if (placement==null) {
680                    lengths.addElement(new Integer(((TimeLocation)lecture.timeLocations().firstElement()).getLength()));
681                } else if (conflicts!=null && conflicts.contains(placement)) {
682                    lengths.addElement(new Integer(((TimeLocation)lecture.timeLocations().firstElement()).getLength()));
683                } else {
684                    int pos=placement.getTimeLocation().getStartSlot();
685                    int length=placement.getTimeLocation().getLength();
686                    for (int j=pos;j<pos+length;j++) {
687                        if (res[j]!=null) {
688                            if (conflicts==null) return false;
689                            if (!assignments.containsKey(lecture))
690                                conflicts.add(placement);
691                            else if (!assignments.containsKey(res[j].variable()))
692                                conflicts.add(res[j]);
693                        }
694                    }
695                    for (int j=pos;j<pos+length;j++) res[j]=placement;
696                    nrLectures++;
697                }
698            }
699            if (nrLectures<=1) return true;
700            
701            if (iIsRequired || (!iIsProhibited && iPreference<0)) {
702                int i=0;
703                Placement p=res[i];
704                while (p==null) p=res[++i];
705                i+=res[i].getTimeLocation().getLength();
706                nrLectures--;
707                while (nrLectures>0) {
708                    int gap=0;
709                    while (i<Constants.SLOTS_PER_DAY && res[i]==null) { gap++; i++; }
710                    if (i==Constants.SLOTS_PER_DAY) break;
711                    if (!canFill(gap, gapMin, gapMax, lengths)) return false;
712                    p=res[i];
713                    i+=res[i].getTimeLocation().getLength();
714                    nrLectures--;
715                }
716            } else if (iIsProhibited || (!iIsRequired && iPreference>0)) {
717                int i=0;
718                Placement p=res[i];
719                while (p==null) p=res[++i];
720                i+=res[i].getTimeLocation().getLength();
721                nrLectures--;
722                while (nrLectures>0) {
723                    int gap=0;
724                    while (i<Constants.SLOTS_PER_DAY && res[i]==null) { gap++; i++; }
725                    if (i==Constants.SLOTS_PER_DAY) break;
726                    if ((gapMin==0 || !canFill(gap, 0, gapMin-1, lengths)) &&
727                            (gapMax>=Constants.SLOTS_PER_DAY || !canFill(gap, gapMax+1, Constants.SLOTS_PER_DAY, lengths))) {
728                            return false;
729                    }
730                    p=res[i];
731                    i+=res[i].getTimeLocation().getLength();
732                    nrLectures--;
733                }
734            }
735            return true;
736        }
737        
738        public boolean isSatisfied() {
739            if (isHard()) return true;
740            if (countAssignedVariables()<2) return true;
741            return (getPreference()<0 && getCurrentPreference()<0) || getPreference()==0 || (getPreference()>0 && getCurrentPreference()==0);
742        }
743        
744        public boolean isChildrenNotOverlap(Lecture lec1, Placement plc1, Lecture lec2, Placement plc2) {
745            if (lec1.getSchedulingSubpartId().equals(lec2.getSchedulingSubpartId())) {
746                    //same subpart
747                    boolean overlap = plc1.getTimeLocation().hasIntersection(plc2.getTimeLocation());
748    
749                    if (overlap && lec1.getParent()!=null && variables().contains(lec1.getParent()) && lec2.getParent()!=null && variables().contains(lec2.getParent())) {
750                            //children overlaps
751                            Placement p1 = (Placement)lec1.getParent().getAssignment();
752                            Placement p2 = (Placement)lec2.getParent().getAssignment();
753                                    //parents not overlap, but children do
754                            if (p1!=null && p2!=null && !p1.getTimeLocation().hasIntersection(p2.getTimeLocation())) return false;
755                    }
756                    
757                    if (!overlap && lec1.getChildrenSubpartIds()!=null && lec2.getChildrenSubpartIds()!=null) {
758                            //parents not overlap
759                    for (Enumeration e1=lec1.getChildrenSubpartIds();e1.hasMoreElements();) {
760                            Long subpartId = (Long)e1.nextElement();
761                            for (Enumeration e2=lec1.getChildren(subpartId).elements();e2.hasMoreElements();) {
762                                    Lecture c1 = (Lecture)e2.nextElement();
763                                    if (c1.getAssignment()==null) continue;
764                                    for (Enumeration e3=lec2.getChildren(subpartId).elements();e3.hasMoreElements();) {
765                                            Lecture c2 = (Lecture)e3.nextElement();
766                                            if (c2.getAssignment()==null) continue;
767                                            if (!c1.getSchedulingSubpartId().equals(c2.getSchedulingSubpartId())) continue;
768                                            Placement p1 = (Placement)c1.getAssignment();
769                                    Placement p2 = (Placement)c2.getAssignment();
770                                            //parents not overlap, but children do
771                                    if (p1.getTimeLocation().hasIntersection(p2.getTimeLocation())) return false;
772                                    }
773                            }
774                    }
775                    }
776            } else {
777                    //different subpart
778            }
779            return true;
780        }
781        
782        private boolean isSatisfiedPair(Lecture lec1, Placement plc1, Lecture lec2, Placement plc2) {
783            if (iIsRequired || (!iIsProhibited && iPreference<0)) {
784                if (sameRoom(getType()) || (isBackToBack(getType()) && !isBackToBackTime(getType()))) {
785                    if (!plc1.sameRooms(plc2)) return false;
786                }
787                if (sameStartHour(getType())) {
788                    if ((plc1.getTimeLocation().getStartSlot()%Constants.SLOTS_PER_DAY) !=
789                            (plc2.getTimeLocation().getStartSlot()%Constants.SLOTS_PER_DAY))
790                        return false;
791                }
792                if (sameHours(getType())) {
793                    if (!sameHours(plc1.getTimeLocation().getStartSlot(), plc1.getTimeLocation().getLength(),
794                            plc2.getTimeLocation().getStartSlot(), plc2.getTimeLocation().getLength()))
795                        return false;
796                }
797                if (sameDays(getType()) || isBackToBack(getType())) {
798                    if (!sameDays(plc1.getTimeLocation().getDaysArray(), plc2.getTimeLocation().getDaysArray()))
799                        return false;
800                }
801                if (notOverlap(getType())) {
802                    if (plc1.getTimeLocation().hasIntersection(plc2.getTimeLocation()))
803                        return false;
804                }
805                if (sameStudents(getType())) {
806                    if (JenrlConstraint.isInConflict(plc1,plc2)) return false;
807                }
808                if (sameInstructor(getType())) {
809                    TimeLocation t1=plc1.getTimeLocation(), t2=plc2.getTimeLocation();
810                    if (t1.shareDays(t2) && t1.shareWeeks(t2)) {
811                            if (t1.shareHours(t2)) return false; //overlap
812                        int s1 = t1.getStartSlot() % Constants.SLOTS_PER_DAY;
813                        int s2 = t2.getStartSlot() % Constants.SLOTS_PER_DAY;
814                        if (s1+t1.getLength()==s2 || s2+t2.getLength()==s1) { //back-to-back
815                            double distance = Placement.getDistance(plc1,plc2);
816                            TimetableModel m = (TimetableModel)getModel();
817                            if (distance>m.getInstructorProhibitedLimit())
818                                    return false;
819                        }
820                    }
821                }
822                if (isPrecedence(getType())) {
823                    return isPrecedence(plc1, plc2, true);
824                }
825                if (isBackToBackDay(getType())) {
826                    if (sameDays(plc1.getTimeLocation().getDaysArray(), plc2.getTimeLocation().getDaysArray())) return false;
827                    return isBackToBackDays(plc1.getTimeLocation(),plc2.getTimeLocation());
828                }
829                if (isNrDaysBetweenGreaterThanOne(getType())) {
830                    if (sameDays(plc1.getTimeLocation().getDaysArray(), plc2.getTimeLocation().getDaysArray())) return false;
831                    return isNrDaysBetweenGreaterThanOne(plc1.getTimeLocation(),plc2.getTimeLocation());
832                }
833                if (isChildrenNotOverlap(getType())) {
834                    return isChildrenNotOverlap(lec1, plc1, lec2, plc2);
835                }
836                if (isFollowingDay(getType())) {
837                    return isFollowingDay(plc1,plc2, true);
838                }
839                if (isEveryOtherDay(getType())) {
840                    return isEveryOtherDay(plc1,plc2, true);
841                }
842            } else if (iIsProhibited || (!iIsRequired && iPreference>0)) {
843                if (sameRoom(getType())) {
844                    if (plc1.sameRooms(plc2))
845                        return false;
846                }
847                if (sameHours(getType())) {
848                    if (plc1.getTimeLocation().shareHours(plc2.getTimeLocation()))
849                        return false;
850                }
851                if (sameStartHour(getType())) {
852                    if ((plc1.getTimeLocation().getStartSlot()%Constants.SLOTS_PER_DAY) ==
853                            (plc2.getTimeLocation().getStartSlot()%Constants.SLOTS_PER_DAY))
854                        return false;
855                }
856                if (sameDays(getType())) {
857                    if (plc1.getTimeLocation().shareDays(plc2.getTimeLocation()))
858                        return false;
859                }
860                if (notOverlap(getType())) {
861                    if (!plc1.getTimeLocation().hasIntersection(plc2.getTimeLocation()))
862                        return false;
863                }
864                if (isBackToBack(getType())) { //still same time
865                    if (!sameDays(plc1.getTimeLocation().getDaysArray(), plc2.getTimeLocation().getDaysArray()))
866                        return false;
867                    if (!isBackToBackTime(getType())) { //still same room
868                        if (!plc1.sameRooms(plc2))
869                            return false;
870                    }
871                }
872                if (isPrecedence(getType())) {
873                    return isPrecedence(plc1, plc2, false);
874                }
875                if (isBackToBackDay(getType())) {
876                    if (sameDays(plc1.getTimeLocation().getDaysArray(), plc2.getTimeLocation().getDaysArray())) return false;
877                    return !isBackToBackDays(plc1.getTimeLocation(),plc2.getTimeLocation());
878                }
879                if (isNrDaysBetweenGreaterThanOne(getType())) {
880                    if (sameDays(plc1.getTimeLocation().getDaysArray(), plc2.getTimeLocation().getDaysArray())) return false;
881                    return !isNrDaysBetweenGreaterThanOne(plc1.getTimeLocation(),plc2.getTimeLocation());
882                }
883                if (isFollowingDay(getType())) {
884                    return isFollowingDay(plc1, plc2, false);
885                }
886                if (isEveryOtherDay(getType())) {
887                    return isEveryOtherDay(plc1, plc2, false);
888                }
889            }
890            return true;
891        }
892        
893        /*
894        public void getInfo(Dictionary info) {
895            StringBuffer varNames = new StringBuffer();
896            for (Enumeration e=variables().elements();e.hasMoreElements();) {
897                Variable variable = (Variable)e.nextElement();
898                varNames.append(varNames.length()>0?", ":"");
899                varNames.append(variable.getName());
900                if (variable.getAssignment()!=null)
901                    varNames.append(" "+variable.getAssignment().getName());
902            }
903            info.put("gc"+iId, iType).getDescription()+" (pref="+getDescription()+" ("+iIsRequired+"/"+iIsProhibited+"/"+iPreference+")"+", current="+getCurrentPreference()+", vars=["+varNames+"])");
904        }
905         */
906        
907        /*
908        public static class GroupConstraintTypes {
909            private static Vector sGroupConstraintTypes = null;
910            
911            static {
912                    try {
913                            sGroupConstraintTypes = new Vector();
914                            for (Iterator i=DistributionType.findAll().iterator();i.hasNext();) {
915                                    DistributionType type = (DistributionType)i.next();
916                                    sGroupConstraintTypes.addElement(new GroupConstraintType(type.getRequirementId().intValue(),type.getReference(),type.getLabel()));
917                            }
918                    } catch (Exception e) {
919                            sLogger.error(e.getMessage(),e);
920                    sGroupConstraintTypes.addElement(new GroupConstraintType(1,"BTB","back-to-back"));
921                    sGroupConstraintTypes.addElement(new GroupConstraintType(2,"BTB_TIME","back-to-back time"));
922                    sGroupConstraintTypes.addElement(new GroupConstraintType(3,"SAME_TIME","same time"));
923                    sGroupConstraintTypes.addElement(new GroupConstraintType(4,"SAME_DAYS","same days"));
924                    sGroupConstraintTypes.addElement(new GroupConstraintType(5,"NHB(1)","1 hr between"));
925                    sGroupConstraintTypes.addElement(new GroupConstraintType(6,"NHB(2)","2 hrs between"));
926                    sGroupConstraintTypes.addElement(new GroupConstraintType(7,"NHB(3)","3 hrs between"));
927                    sGroupConstraintTypes.addElement(new GroupConstraintType(8,"NHB(4)","4 hrs between"));
928                    sGroupConstraintTypes.addElement(new GroupConstraintType(9,"NHB(5)","5 hrs between"));
929                    sGroupConstraintTypes.addElement(new GroupConstraintType(10,"NHB(6)","6 hrs between"));
930                    sGroupConstraintTypes.addElement(new GroupConstraintType(11,"NHB(7)","7 hrs between"));
931                    sGroupConstraintTypes.addElement(new GroupConstraintType(12,"NHB(8)","8 hrs between"));
932                    sGroupConstraintTypes.addElement(new GroupConstraintType(13,"DIFF_TIME","different time"));
933                    sGroupConstraintTypes.addElement(new GroupConstraintType(14,"NHB(1.5)","1.5 hrs between"));
934                    sGroupConstraintTypes.addElement(new GroupConstraintType(15,"NHB(4.5)","4.5 hrs between"));
935                    sGroupConstraintTypes.addElement(new GroupConstraintType(16,"SAME_START","same start time"));
936                    sGroupConstraintTypes.addElement(new GroupConstraintType(17,"SAME_ROOM","same room"));
937                    sGroupConstraintTypes.addElement(new GroupConstraintType(18,"NHB_GTE(1)","greater than or equal to 1 hour between"));
938                    sGroupConstraintTypes.addElement(new GroupConstraintType(19,"NHB_LT(6)","less than 6 hours between"));
939                    }
940                    if (getGroupConstraintType("SAME_STUDENTS")==null)
941                            sGroupConstraintTypes.addElement(new GroupConstraintType(20,"SAME_STUDENTS","same students"));
942                    if (getGroupConstraintType("SAME_INSTR")==null)
943                            sGroupConstraintTypes.addElement(new GroupConstraintType(21,"SAME_INSTR","same instructor"));
944                    if (getGroupConstraintType("CAN_SHARE_ROOM")==null)
945                            sGroupConstraintTypes.addElement(new GroupConstraintType(22,"CAN_SHARE_ROOM","can share rooms"));
946                    if (getGroupConstraintType("PRECEDENCE")==null)
947                            sGroupConstraintTypes.addElement(new GroupConstraintType(24,"PRECEDENCE","precedence"));
948                    if (getGroupConstraintType("BTB_DAY")==null)
949                            sGroupConstraintTypes.addElement(new GroupConstraintType(26,"BTB_DAY","back-to-back day"));
950                    if (getGroupConstraintType("MIN_GRUSE(10x1h)")==null)
951                            sGroupConstraintTypes.addElement(new GroupConstraintType(27,"MIN_GRUSE(10x1h)","minimize use of 1h groups"));
952                    if (getGroupConstraintType("MIN_GRUSE(5x2h)")==null)
953                            sGroupConstraintTypes.addElement(new GroupConstraintType(28,"MIN_GRUSE(5x2h)","minimize use of 2h groups"));
954                    if (getGroupConstraintType("MIN_GRUSE(3x3h)")==null)
955                            sGroupConstraintTypes.addElement(new GroupConstraintType(29,"MIN_GRUSE(3x3h)","minimize use of 3h groups"));
956                    if (getGroupConstraintType("MIN_GRUSE(2x5h)")==null)
957                            sGroupConstraintTypes.addElement(new GroupConstraintType(30,"MIN_GRUSE(2x5h)","minimize use of 5h groups"));
958                    if (getGroupConstraintType("MEET_WITH")==null)
959                            sGroupConstraintTypes.addElement(new GroupConstraintType(31,"MEET_WITH","meet together"));
960                    if (getGroupConstraintType("NDB_GT_1")==null)
961                            sGroupConstraintTypes.addElement(new GroupConstraintType(32,"NDB_GT_1",">1d btw"));
962                    if (getGroupConstraintType("CH_NOTOVERLAP")==null)
963                            sGroupConstraintTypes.addElement(new GroupConstraintType(33,"NDB_GT_1","ch no overlap"));
964                    if (getGroupConstraintType("FOLLOWING_DAY")==null)
965                            sGroupConstraintTypes.addElement(new GroupConstraintType(34,"FOLLOWING_DAY","following day"));
966                    if (getGroupConstraintType("EVERY_OTHER_DAY")==null)
967                            sGroupConstraintTypes.addElement(new GroupConstraintType(35,"EVERY_OTHER_DAY","every other day"));
968            }
969            
970            public static Enumeration elements() {
971                return sGroupConstraintTypes.elements();
972            }
973            
974            public static int size() {
975                return sGroupConstraintTypes.size();
976            }
977            
978            public static GroupConstraintType elementAt(int i) {
979                return (GroupConstraintType) sGroupConstraintTypes.elementAt(i);
980            }
981            
982            public static GroupConstraintType getGroupConstraintType(int id) {
983                for (Enumeration e=sGroupConstraintTypes.elements();e.hasMoreElements();) {
984                    GroupConstraintType gc = (GroupConstraintType)e.nextElement();
985                    if (gc.getId()==id) return gc;
986                }
987                return null;
988            }
989            
990            public static GroupConstraintType getGroupConstraintType(String type) {
991                for (Enumeration e=sGroupConstraintTypes.elements();e.hasMoreElements();) {
992                    GroupConstraintType gc = (GroupConstraintType)e.nextElement();
993                    if (gc.getType().equalsIgnoreCase(type)) return gc;
994                }
995                return null;
996            }
997    
998            public static class GroupConstraintType implements Comparable {
999                int iId;
1000                String iType;
1001                String iDescription;
1002                
1003                private GroupConstraintType(int id, String type, String desc) {
1004                    iId = id;
1005                    iType = type;
1006                    iDescription = desc;
1007                }
1008                
1009                public int getId() {
1010                    return iId;
1011                }
1012                public String getType() {
1013                    return iType;
1014                }
1015                public String getDescription() {
1016                    return iDescription;
1017                }
1018                
1019                public boolean equals(Object o) {
1020                    if (o==null || !(o instanceof GroupConstraintType)) return false;
1021                    return getId() == ((GroupConstraintType)o;
1022                }
1023                
1024                public int compareTo(Object o) {
1025                    if (o==null || !(o instanceof GroupConstraintType)) return 0;
1026                    GroupConstraintType g = (GroupConstraintType)o;
1027                    return getId() - ((GroupConstraintType)o;
1028                }
1029                
1030            }
1031        }
1032        */
1033    }