001    package net.sf.cpsolver.coursett.model;
002    
003    import java.util.Collection;
004    import java.util.Collections;
005    import java.util.Enumeration;
006    import java.util.HashSet;
007    import java.util.Hashtable;
008    import java.util.Iterator;
009    import java.util.Map;
010    import java.util.Set;
011    import java.util.Vector;
012    
013    import net.sf.cpsolver.coursett.Constants;
014    import net.sf.cpsolver.coursett.constraint.ClassLimitConstraint;
015    import net.sf.cpsolver.coursett.constraint.DepartmentSpreadConstraint;
016    import net.sf.cpsolver.coursett.constraint.GroupConstraint;
017    import net.sf.cpsolver.coursett.constraint.InstructorConstraint;
018    import net.sf.cpsolver.coursett.constraint.JenrlConstraint;
019    import net.sf.cpsolver.coursett.constraint.RoomConstraint;
020    import net.sf.cpsolver.coursett.constraint.SpreadConstraint;
021    import net.sf.cpsolver.coursett.constraint.WeakeningConstraint;
022    import net.sf.cpsolver.ifs.constant.ConstantVariable;
023    import net.sf.cpsolver.ifs.model.Constraint;
024    import net.sf.cpsolver.ifs.model.Value;
025    import net.sf.cpsolver.ifs.model.Variable;
026    import net.sf.cpsolver.ifs.util.FastVector;
027    
028    
029    /**
030     * Lecture (variable).
031     *
032     * @version
033     * CourseTT 1.1 (University Course Timetabling)<br>
034     * Copyright (C) 2006 Tomáš Müller<br>
035     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
036     * Lazenska 391, 76314 Zlin, Czech Republic<br>
037     * <br>
038     * This library is free software; you can redistribute it and/or
039     * modify it under the terms of the GNU Lesser General Public
040     * License as published by the Free Software Foundation; either
041     * version 2.1 of the License, or (at your option) any later version.
042     * <br><br>
043     * This library is distributed in the hope that it will be useful,
044     * but WITHOUT ANY WARRANTY; without even the implied warranty of
045     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
046     * Lesser General Public License for more details.
047     * <br><br>
048     * You should have received a copy of the GNU Lesser General Public
049     * License along with this library; if not, write to the Free Software
050     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
051     */
052    
053    public class Lecture extends Variable implements ConstantVariable {
054        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Lecture.class);
055        private Long iClassId;
056        private Long iSolverGroupId;
057        private Long iSchedulingSubpartId;
058        private String iName;
059        private Long iDept;
060        private Long iScheduler;
061        private Vector iTimeLocations;
062        private Vector iRoomLocations;
063        private String iNote = null;
064        
065        private int iMinClassLimit;
066        private int iMaxClassLimit;
067        private double iRoomToLimitRatio;
068        private int iNrRooms;
069        private int iOrd;
070        
071        private Set iStudents = new HashSet();
072        private DepartmentSpreadConstraint iDeptSpreadConstraint = null;
073        private Set iSpreadConstraints = new HashSet();
074        private Set iWeakeningConstraints = new HashSet();
075        private Vector iInstructorConstraints = new Vector();
076        private ClassLimitConstraint iClassLimitConstraint = null;
077        
078        private Lecture iParent = null;
079        private Hashtable iChildren = null;
080        private Vector  iSameSubpartLectures = null;
081        private Configuration iParentConfiguration = null;
082    
083        private Hashtable iSameStudents = new Hashtable(10);
084        private Set iActiveJenrls = new HashSet();
085        private Vector iJenrlConstraints = new FastVector();
086        private Hashtable iJenrlConstraintsHash = new Hashtable();
087        private Hashtable iCommitedConflicts = new Hashtable();
088        private Set iGroupConstraints = new HashSet();
089        private Set iHardGroupSoftConstraints = new HashSet();
090        private Set iCanShareRoomGroupConstraints = new HashSet();
091        
092        public boolean iCommitted = false;
093        
094        public static boolean sSaveMemory = false;
095        public static boolean sAllowBreakHard = false;
096        
097        private Integer iCacheMinRoomSize = null;
098        private Integer iCacheMaxRoomSize = null;
099        private Integer iCacheMaxAchievableClassLimit = null;
100        
101        /** Constructor
102         * @param id unique identification
103         * @param name class name
104         * @param timeLocations set of time locations
105         * @param roomLocations set of room location
106         * @param initialPlacement initial placement
107         */
108        public Lecture(Long id, Long solverGroupId, Long schedulingSubpartId, String name, Vector timeLocations, Vector roomLocations, int nrRooms, Placement initialPlacement, int minClassLimit, int maxClassLimit, double room2limitRatio) {
109            super(initialPlacement);
110            iClassId = id;
111            iSchedulingSubpartId = schedulingSubpartId;
112            iTimeLocations = timeLocations;
113            iRoomLocations = roomLocations;
114            iName = name;
115            iMinClassLimit = minClassLimit;
116            iMaxClassLimit = maxClassLimit;
117            iRoomToLimitRatio = room2limitRatio;
118            iNrRooms = nrRooms;
119            iSolverGroupId = solverGroupId;
120        }
121        
122        public Lecture(Long id, Long solverGroupId, String name) {
123            super(null);
124            iClassId = id;
125            iSolverGroupId = solverGroupId;
126            iName = name;
127        }
128        
129        public Long getSolverGroupId() {
130            return iSolverGroupId;
131        }
132        
133        /** Add active jenrl constraint (active mean that there is at least one student between its classes) */
134        public void addActiveJenrl(JenrlConstraint constr) { iActiveJenrls.add(constr); }
135        /** Active jenrl constraints (active mean that there is at least one student between its classes) */
136        public Set activeJenrls() { return iActiveJenrls; }
137        /** Remove active jenrl constraint (active mean that there is at least one student between its classes) */
138        public void removeActiveJenrl(JenrlConstraint constr) { iActiveJenrls.remove(constr); }
139        
140        /** Class id */
141        public Long getClassId() { return iClassId; }
142        public Long getSchedulingSubpartId() { return iSchedulingSubpartId; }
143        /** Class name */
144        public String getName() { return iName; }
145        /** Class id */
146        public long getId() { return iClassId.longValue(); }
147        /** Instructor name */
148        public Vector getInstructorNames() { 
149            Vector ret = new Vector();
150            for (Enumeration e=iInstructorConstraints.elements();e.hasMoreElements();) {
151                    InstructorConstraint ic = (InstructorConstraint)e.nextElement();
152                    ret.addElement(ic.getName());
153            }
154            return ret;
155        }
156        public String getInstructorName() { 
157            StringBuffer sb = new StringBuffer();
158            for (Enumeration e=iInstructorConstraints.elements();e.hasMoreElements();) {
159                    InstructorConstraint ic = (InstructorConstraint)e.nextElement();
160                    sb.append(ic.getName());
161                    if (e.hasMoreElements())
162                            sb.append(", ");
163            }
164            return sb.toString();
165        }
166        
167        /** List of enrolled students */
168        public Set students() { return iStudents; }
169        
170        public double nrWeightedStudents() {
171            double w = 0.0;
172            for (Iterator i=iStudents.iterator();i.hasNext();) {
173                    Student s = (Student)i.next();
174                    w+=s.getOfferingWeight(getConfiguration());
175            }
176            return w;
177        }
178        
179        /** Add an enrolled student */
180        public void addStudent(Student student) {
181            if (!iStudents.add(student)) return;
182            if (getAssignment()!=null && getModel()!=null)
183                    ((TimetableModel)getModel()).getCommittedStudentConflictsCounter().inc(student.countConflictPlacements((Placement)getAssignment()));
184            iSameStudents.clear();
185            iCommitedConflicts.clear();
186        }
187        public void removeStudent(Student student) {
188            if (!iStudents.remove(student)) return;
189            if (getAssignment()!=null && getModel()!=null)
190                    ((TimetableModel)getModel()).getCommittedStudentConflictsCounter().dec(student.countConflictPlacements((Placement)getAssignment()));
191            iSameStudents.clear();
192            iCommitedConflicts.clear();
193        }
194        /** Returns true if the given student is enrolled */
195        public boolean hasStudent(Student student) { return iStudents.contains(student);}
196        /** Set of lectures of the same class (only section is different) */
197        public void setSameSubpartLectures(Vector sameSubpartLectures) {  iSameSubpartLectures = sameSubpartLectures; }
198        /** Set of lectures of the same class (only section is different) */
199        public Vector sameSubpartLectures() { return iSameSubpartLectures; }
200        /** List of students enrolled in this class as well as in the given class */
201        public Set sameStudents(Lecture lecture) {
202            if (iSameStudents.containsKey(lecture)) return (Set)iSameStudents.get(lecture);
203            Set ret = new HashSet(students());
204            ret.retainAll(lecture.students());
205            iSameStudents.put(lecture, ret);
206            return ret;
207        }
208        /** List of students of this class in conflict with the given assignment */
209        public Set conflictStudents(Value value) {
210            if (value==null) return new HashSet();
211            if (value.equals(getAssignment())) return conflictStudents();
212            Set ret = new HashSet();
213            for (Enumeration i1=jenrlConstraints(); i1.hasMoreElements();) { //constraints()
214                    JenrlConstraint jenrl = (JenrlConstraint)i1.nextElement();// constraint;
215                if (jenrl.jenrl(this, value)>0)
216                    ret.addAll(sameStudents((Lecture)jenrl.another(this)));
217            }
218            return ret;
219        }
220    
221        /** List of students of this class which are in conflict with any other assignment */
222        public Set conflictStudents() {
223            Set ret = new HashSet();
224            if (getAssignment()==null) return ret;
225            for (Iterator i1=activeJenrls().iterator(); i1.hasNext();) {
226                JenrlConstraint jenrl = (JenrlConstraint) i1.next();
227                ret.addAll(sameStudents((Lecture)jenrl.another(this)));
228            }
229            Placement placement = (Placement)getAssignment();
230            for (Iterator i1=students().iterator();i1.hasNext();) {
231                    Student student = (Student)i1.next();
232                    if (student.countConflictPlacements(placement)>0)
233                            ret.add(student);
234            }
235            return ret;
236        }
237        
238        /** Lectures different from this one, where it is student conflict of the given student between this and the lecture */
239        public Vector conflictLectures(Student student) {
240            Vector ret = new FastVector();
241            if (getAssignment()==null) return ret;
242            for (Iterator it=activeJenrls().iterator();it.hasNext();) {
243                JenrlConstraint jenrl = (JenrlConstraint) it.next();
244                Lecture lect = (Lecture)jenrl.another(this);
245                if (lect.students().contains(student)) ret.addElement(lect);
246            }
247            return ret;
248        }
249        
250        /** True if this lecture is in a student conflict with the given student */
251        public int isInConflict(Student student) {
252            if (getAssignment()==null) return 0;
253            int ret = 0;
254            for (Iterator it=activeJenrls().iterator();it.hasNext();) {
255                JenrlConstraint jenrl = (JenrlConstraint) it.next();
256                Lecture lect = (Lecture)jenrl.another(this);
257                if (lect.students().contains(student)) ret++;
258            }
259            return ret;
260        }
261        
262        private void computeValues(Vector values, boolean allowBreakHard, TimeLocation timeLocation, Vector roomLocations, int idx) {
263            if (roomLocations.size()==iNrRooms) {
264                    Placement p = new Placement(this,timeLocation, roomLocations);
265                    p.setVariable(this);
266                    if (sSaveMemory && !isValid(p)) return;
267                    if (getInitialAssignment()!=null && p.equals(getInitialAssignment())) setInitialAssignment(p);
268                    if (getAssignment()!=null && getAssignment().equals(p)) iValue=getAssignment();
269                    if (getBestAssignment()!=null && getBestAssignment().equals(p)) setBestAssignment(p);
270                    values.addElement(p);
271                    return;
272            }
273            for (int i=idx;i<iRoomLocations.size();i++) {
274                    RoomLocation roomLocation = (RoomLocation)iRoomLocations.elementAt(i);
275                    if (!allowBreakHard && Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(roomLocation.getPreference()))) continue;
276                    
277                    if (roomLocation.getRoomConstraint()!=null && !roomLocation.getRoomConstraint().isAvailable(this,timeLocation, getScheduler())) continue;
278                    roomLocations.addElement(roomLocation);
279                    computeValues(values, allowBreakHard, timeLocation, roomLocations, i+1);
280                    roomLocations.removeElementAt(roomLocations.size()-1);
281            }
282        }
283        
284        /** Domain -- all combinations of room and time locations */
285        public Vector computeValues(boolean allowBreakHard) {
286            Vector values = new FastVector(iRoomLocations.size()*iTimeLocations.size());
287            for (Enumeration i1=iTimeLocations.elements();i1.hasMoreElements();) {
288                TimeLocation timeLocation = (TimeLocation)i1.nextElement();
289                if (!allowBreakHard && Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(timeLocation.getPreference()))) continue;
290                if (timeLocation.getPreference()>500) continue;
291                boolean notAvailable=false;
292                    for (Enumeration e=getInstructorConstraints().elements();e.hasMoreElements();) {
293                            InstructorConstraint ic = (InstructorConstraint)e.nextElement();
294                            if (!ic.isAvailable(this, timeLocation)) {
295                                    notAvailable = true; break;
296                            }
297                    }
298                    if (notAvailable) continue;
299                if (iNrRooms==0) {
300                            Placement p = new Placement(this,timeLocation,(RoomLocation)null);
301                    for (Enumeration e=getInstructorConstraints().elements();e.hasMoreElements();) {
302                            InstructorConstraint ic = (InstructorConstraint)e.nextElement();
303                            if (!ic.isAvailable(this, p)) {
304                                    notAvailable = true; break;
305                            }
306                    }
307                    if (notAvailable) continue;
308                            p.setVariable(this);
309                            if (sSaveMemory && !isValid(p)) continue;
310                            if (getInitialAssignment()!=null && p.equals(getInitialAssignment())) setInitialAssignment(p);
311                            if (getAssignment()!=null && getAssignment().equals(p)) iValue=getAssignment();
312                            if (getBestAssignment()!=null && getBestAssignment().equals(p)) setBestAssignment(p);
313                            values.addElement(p);
314                } else if (iNrRooms==1) {
315                    for (Enumeration i2=iRoomLocations.elements();i2.hasMoreElements();) {
316                            RoomLocation roomLocation = (RoomLocation)i2.nextElement();
317                            if (!allowBreakHard && Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(roomLocation.getPreference()))) continue;
318                            if (roomLocation.getPreference()>500) continue;
319                            if (roomLocation.getRoomConstraint()!=null && !roomLocation.getRoomConstraint().isAvailable(this,timeLocation, getScheduler())) continue;
320                            Placement p = new Placement(this,timeLocation, roomLocation);
321                            p.setVariable(this);
322                            if (sSaveMemory && !isValid(p)) continue;
323                            if (getInitialAssignment()!=null && p.equals(getInitialAssignment())) setInitialAssignment(p);
324                            if (getAssignment()!=null && getAssignment().equals(p)) iValue=getAssignment();
325                            if (getBestAssignment()!=null && getBestAssignment().equals(p)) setBestAssignment(p);
326                            values.addElement(p);
327                    }
328                } else {
329                    computeValues(values, allowBreakHard, timeLocation, new Vector(iNrRooms), 0);
330                }
331            }
332            return values;
333        }
334        
335        /** All values */
336        public Vector values() {
337            if (super.values()==null) {
338                if (getInitialAssignment()!=null && iTimeLocations.size()==1 && iRoomLocations.size()==getNrRooms()) {
339                    Vector values = new Vector(1);
340                    values.addElement(getInitialAssignment());
341                    setValues(values);
342                } else {
343                    if (isCommitted() || !sSaveMemory)
344                            setValues(computeValues(sAllowBreakHard));
345                }
346            }
347            if (isCommitted())
348                    return super.values();
349            if (sSaveMemory) {
350                return computeValues(sAllowBreakHard);            
351            } else
352                return super.values();
353        }
354        
355        public boolean equals(Object o) {
356            try {
357                return getClassId().equals(((Lecture)o).getClassId());
358            } catch (Exception e) {
359                return false;
360            }
361        }
362        
363        /** Best time preference of this lecture */
364        private Double iBestTimePreferenceCache = null;
365        public double getBestTimePreference() {
366            if (iBestTimePreferenceCache==null) {
367                    double ret = Double.MAX_VALUE;
368                    for (Enumeration e=iTimeLocations.elements();e.hasMoreElements();) {
369                            TimeLocation time = (TimeLocation)e.nextElement();
370                            ret = Math.min(ret,time.getNormalizedPreference());
371                    }
372                    iBestTimePreferenceCache = new Double(ret);
373            }
374            return iBestTimePreferenceCache.doubleValue();
375        }
376        
377        /** Best room preference of this lecture */
378        public int getBestRoomPreference() { 
379            int ret = Integer.MAX_VALUE;
380            for (Enumeration e=iRoomLocations.elements();e.hasMoreElements();) {
381                    RoomLocation room = (RoomLocation)e.nextElement();
382                    ret = Math.min(ret,room.getPreference());
383            }
384            return ret;
385        }
386        
387        /** Number of student conflicts caused by the given assignment of this lecture */
388        public int countStudentConflicts(Value value) {
389            int studentConflictsSum = 0;
390            for (Enumeration i=jenrlConstraints();i.hasMoreElements();) {
391                JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
392                studentConflictsSum += jenrl.jenrl(this, value);
393            }
394            return studentConflictsSum;
395        }
396    
397        public int countStudentConflictsOfTheSameProblem(Value value) {
398            int studentConflictsSum = 0;
399            for (Enumeration i=jenrlConstraints();i.hasMoreElements();) {
400                JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
401                if (!jenrl.isOfTheSameProblem()) continue;
402                studentConflictsSum += jenrl.jenrl(this, value);
403            }
404            return studentConflictsSum;
405        }
406    
407        public int countHardStudentConflicts(Value value) {
408            int studentConflictsSum = 0;
409            if (!isSingleSection()) return 0;
410            for (Enumeration i=jenrlConstraints();i.hasMoreElements();) {
411                JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
412                if (!jenrl.areStudentConflictsHard()) continue;
413                studentConflictsSum += jenrl.jenrl(this, value);
414            }
415            return studentConflictsSum;
416        }
417        
418        public int countHardStudentConflictsOfTheSameProblem(Value value) {
419            int studentConflictsSum = 0;
420            for (Enumeration i=jenrlConstraints();i.hasMoreElements();) {
421                JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
422                if (!jenrl.isOfTheSameProblem()) continue;
423                if (!jenrl.areStudentConflictsHard()) continue;
424                studentConflictsSum += jenrl.jenrl(this, value);
425            }
426            return studentConflictsSum;
427        }
428    
429        public int countDistanceStudentConflicts(Value value) {
430            int studentConflictsSum = 0;
431            for (Enumeration i=jenrlConstraints();i.hasMoreElements();) {
432                JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
433                if (!jenrl.areStudentConflictsDistance(value)) continue;
434                studentConflictsSum += jenrl.jenrl(this, value);
435            }
436            return studentConflictsSum;
437        }
438    
439        public int countDistanceStudentConflictsOfTheSameProblem(Value value) {
440            int studentConflictsSum = 0;
441            for (Enumeration i=jenrlConstraints();i.hasMoreElements();) {
442                JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
443                if (!jenrl.isOfTheSameProblem()) continue;
444                if (!jenrl.areStudentConflictsDistance(value)) continue;
445                studentConflictsSum += jenrl.jenrl(this, value);
446            }
447            return studentConflictsSum;
448        }
449    
450        /** Number of student conflicts caused by the initial assignment of this lecture */
451        public int countInitialStudentConflicts() {
452            Value value = getInitialAssignment();
453            if (value==null) return 0;
454            int studentConflictsSum = 0;
455            for (Enumeration i=jenrlConstraints();i.hasMoreElements();) {
456                JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
457                Lecture another = (Lecture)jenrl.another(this);
458                if (another.getInitialAssignment()!=null)
459                    if (JenrlConstraint.isInConflict((Placement)value,(Placement)another.getInitialAssignment()))
460                        studentConflictsSum += jenrl.getJenrl();
461            }
462            return studentConflictsSum;
463        }
464    
465        /** Table of student conflicts caused by the initial assignment of this lecture in format (another lecture, number)*/
466        public Hashtable getInitialStudentConflicts() {
467            Value value = getInitialAssignment();
468            if (value==null) return null;
469            Hashtable ret = new Hashtable();
470            for (Enumeration i=jenrlConstraints();i.hasMoreElements();) {
471                JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
472                Lecture another = (Lecture)jenrl.another(this);
473                if (another.getInitialAssignment()!=null)
474                    if (JenrlConstraint.isInConflict((Placement)value,(Placement)another.getInitialAssignment()))
475                        ret.put(another,new Long(jenrl.getJenrl()));
476            }
477            return ret;
478        }
479    
480        /** List of student conflicts caused by the initial assignment of this lecture */
481        public Set initialStudentConflicts() {
482            Value value = getInitialAssignment();
483            if (value==null) return null;
484            HashSet ret = new HashSet();
485            for (Enumeration i=jenrlConstraints();i.hasMoreElements();) {
486                JenrlConstraint jenrl = (JenrlConstraint)i.nextElement();
487                Lecture another = (Lecture)jenrl.another(this);
488                if (another.getInitialAssignment()!=null)
489                    if (JenrlConstraint.isInConflict((Placement)value,(Placement)another.getInitialAssignment()))
490                        ret.addAll(sameStudents(another));
491            }
492            return ret;
493        }
494        
495        public void addContstraint(Constraint constraint) {
496            super.addContstraint(constraint);
497            
498            if (constraint instanceof WeakeningConstraint)
499                    iWeakeningConstraints.add(constraint);
500            
501            if (constraint instanceof JenrlConstraint) {
502                    JenrlConstraint jenrl = (JenrlConstraint)constraint;
503                    Lecture another = (Lecture)jenrl.another(this);
504                    if (another!=null) {
505                            iJenrlConstraints.add(jenrl);
506                            another.iJenrlConstraints.add(jenrl);
507                            iJenrlConstraintsHash.put(another,constraint);
508                            another.iJenrlConstraintsHash.put(this,constraint);
509                    }
510            } else if (constraint instanceof DepartmentSpreadConstraint)
511                iDeptSpreadConstraint = (DepartmentSpreadConstraint)constraint;
512            else if (constraint instanceof SpreadConstraint)
513                iSpreadConstraints.add(constraint);
514            else if (constraint instanceof InstructorConstraint) {
515                    InstructorConstraint ic = (InstructorConstraint)constraint;
516                    if (ic.getResourceId()!=null && ic.getResourceId().intValue()>0)
517                            iInstructorConstraints.add(ic);
518            } else if (constraint instanceof ClassLimitConstraint)
519                    iClassLimitConstraint = (ClassLimitConstraint)constraint;
520            else if (constraint instanceof GroupConstraint) {
521                    GroupConstraint gc = (GroupConstraint)constraint;
522                    if (GroupConstraint.canShareRooms(gc.getType())) {
523                            iCanShareRoomGroupConstraints.add(constraint);
524                    } else {
525                            iGroupConstraints.add(constraint);
526                            if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(gc.getPreference())) ||
527                            Constants.sPreferenceRequired.equals(Constants.preferenceLevel2preference(gc.getPreference())))
528                                            iHardGroupSoftConstraints.add(constraint);
529                    }
530            }
531        }
532        public void removeContstraint(Constraint constraint) {
533            super.removeContstraint(constraint);
534            
535            if (constraint instanceof WeakeningConstraint)
536                    iWeakeningConstraints.remove(constraint);
537            
538            if (constraint instanceof JenrlConstraint) {
539                    JenrlConstraint jenrl = (JenrlConstraint)constraint;
540                    Lecture another = (Lecture)jenrl.another(this);
541                    if (another!=null) {
542                            iJenrlConstraints.remove(jenrl);
543                            another.iJenrlConstraints.remove(jenrl);
544                            iJenrlConstraintsHash.remove(another);
545                            another.iJenrlConstraintsHash.remove(this);
546                    }
547            } else if (constraint instanceof GroupConstraint) {
548                    iCanShareRoomGroupConstraints.remove(constraint);
549                    iHardGroupSoftConstraints.remove(constraint);
550                    iGroupConstraints.remove(constraint);
551            } else if (constraint instanceof DepartmentSpreadConstraint)
552                    iDeptSpreadConstraint = null;
553            else if (constraint instanceof SpreadConstraint)
554                iSpreadConstraints.remove(constraint);
555            else if (constraint instanceof InstructorConstraint)
556                iInstructorConstraints.remove(constraint);
557            else if (constraint instanceof ClassLimitConstraint)
558                    iClassLimitConstraint = null;
559        }
560        
561        /** All JENRL constraints of this lecture */
562        public JenrlConstraint jenrlConstraint(Lecture another) {
563            /*
564            for (Enumeration e=iJenrlConstraints.elements();e.hasMoreElements();) {
565                    JenrlConstraint jenrl = (JenrlConstraint)e.nextElement();
566                    if (jenrl.another(this).equals(another)) return jenrl;
567            }
568            return null;
569            */
570            return (JenrlConstraint)iJenrlConstraintsHash.get(another);
571        }
572        public Enumeration jenrlConstraints() {
573            return iJenrlConstraints.elements();
574        }
575        
576        public int minClassLimit() {
577            return iMinClassLimit;
578        }
579        
580        public int maxClassLimit() {
581            return iMaxClassLimit;
582        }
583        
584        public int maxAchievableClassLimit() {
585            //if (iCacheMaxAchievableClassLimit!=null) return iCacheMaxAchievableClassLimit.intValue();
586            
587            int maxAchievableClassLimit = Math.min(maxClassLimit(),(int)Math.floor(maxRoomSize()/roomToLimitRatio()));
588            
589            if (hasAnyChildren()) {
590                    
591                    for (Enumeration e1=getChildrenSubpartIds();e1.hasMoreElements();) {
592                            Long subpartId = (Long) e1.nextElement();
593                            int maxAchievableChildrenLimit = 0;
594                            
595                            for (Enumeration e2=getChildren(subpartId).elements();e2.hasMoreElements();) {
596                                    Lecture child = (Lecture)e2.nextElement();
597                                    maxAchievableChildrenLimit += child.maxAchievableClassLimit();
598                            }
599                    
600                            maxAchievableClassLimit = Math.min(maxAchievableClassLimit, maxAchievableChildrenLimit);
601                    }
602            }
603            
604            maxAchievableClassLimit = Math.max(minClassLimit(),maxAchievableClassLimit);
605            iCacheMaxAchievableClassLimit = new Integer(maxAchievableClassLimit);
606            return maxAchievableClassLimit;
607        }
608        
609        public int classLimit() {
610            if (minClassLimit()==maxClassLimit()) return minClassLimit();
611            return classLimit(null, null);
612        }
613    
614        public int classLimit(Placement assignment, Set conflicts) {
615            Placement a = (Placement)getAssignment();
616            if (assignment!=null && assignment.variable().equals(this))
617                    a = assignment;
618            if (conflicts!=null && a!=null && conflicts.contains(a))
619                    a = null;
620            int classLimit = (a==null?maxAchievableClassLimit():Math.min(maxClassLimit(),(int)Math.floor(a.minRoomSize()/roomToLimitRatio())));
621            
622            if (!hasAnyChildren()) return classLimit;
623            
624                    for (Enumeration e1=getChildrenSubpartIds();e1.hasMoreElements();) {
625                            Long subpartId = (Long) e1.nextElement();
626                            int childrenClassLimit = 0;
627                            
628                            for (Enumeration e2=getChildren(subpartId).elements();e2.hasMoreElements();) {
629                                    Lecture child = (Lecture)e2.nextElement();
630                                    childrenClassLimit += child.classLimit(assignment, conflicts);
631                            }
632                    
633                            classLimit = Math.min(classLimit, childrenClassLimit);
634                    }
635            
636            return Math.max(minClassLimit(),classLimit);
637        }
638        
639        public double roomToLimitRatio() {
640            return iRoomToLimitRatio;
641        }
642        
643        public int minRoomUse() {
644            return (int)Math.ceil(iMinClassLimit*iRoomToLimitRatio);
645        }
646    
647        public int maxRoomUse() {
648            return (int)Math.ceil(iMaxClassLimit*iRoomToLimitRatio);
649        }
650        
651        public String toString() {
652            return getName();
653        }
654        
655        public String getValuesString() {
656            StringBuffer sb = new StringBuffer();
657            for (Enumeration e=values().elements();e.hasMoreElements();) {
658                Placement p = (Placement)e.nextElement();
659                sb.append(p.getName()).append(e.hasMoreElements()?", ":"");
660            }
661            return sb.toString();
662        }
663    
664        /** Controlling Course Offering Department */
665        public Long getDepartment() { return iDept;}
666        /** Controlling Course Offering Department */
667        public void setDepartment(Long dept) { iDept=dept; }
668        /** Scheduler (Managing Department) */
669        public Long getScheduler() { return iScheduler;}
670        /** Scheduler (Managing Department) */
671        public void setScheduler(Long scheduler) { iScheduler=scheduler; }
672        /** Departmental spreading constraint */
673        public DepartmentSpreadConstraint getDeptSpreadConstraint() { return iDeptSpreadConstraint; }
674        /** Instructor constraint */
675        public Vector getInstructorConstraints() { return iInstructorConstraints; }
676        public ClassLimitConstraint getClassLimitConstraint() { return iClassLimitConstraint; }
677        public Set getSpreadConstraints() { return iSpreadConstraints; }
678        public Set getWeakeningConstraints() { return iWeakeningConstraints; }
679    
680        /** All room locations */
681        public Vector roomLocations() { return iRoomLocations; }
682        /** All time locations */
683        public Vector timeLocations() { return iTimeLocations; }
684        public int nrTimeLocations() {
685            int ret = 0;
686            for (Enumeration e=iTimeLocations.elements();e.hasMoreElements();) {
687                    TimeLocation time = (TimeLocation)e.nextElement();
688                    if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(time.getPreference())))
689                            ret++;
690            }
691            return ret;
692        }
693        public int nrRoomLocations() {
694            int ret = 0;
695            for (Enumeration e=iRoomLocations.elements();e.hasMoreElements();) {
696                    RoomLocation room = (RoomLocation)e.nextElement();
697                    if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(room.getPreference())))
698                            ret++;
699            }
700            return ret;
701        }
702        public int nrValues() {
703            int ret = 0;
704            for (Enumeration e=values().elements();e.hasMoreElements();) {
705                    Placement placement = (Placement)e.nextElement();
706                    if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(placement.getRoomPreference())) &&
707                            !Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(placement.getTimeLocation().getPreference())))
708                            ret++;
709            }
710            return ret;
711        }
712        public int nrValues(TimeLocation time) {
713            int ret = 0;
714            for (Enumeration e=iRoomLocations.elements();e.hasMoreElements();) {
715                    RoomLocation room = (RoomLocation)e.nextElement();
716                    if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(room.getPreference())) && (room.getRoomConstraint()==null || room.getRoomConstraint().isAvailable(this, time, getScheduler())))
717                            ret++;
718            }
719            return ret;
720        }
721        public int nrValues(RoomLocation room) {
722            int ret = 0;
723            for (Enumeration e=iTimeLocations.elements();e.hasMoreElements();) {
724                    TimeLocation time = (TimeLocation)e.nextElement();
725                    if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(time.getPreference())) && (room.getRoomConstraint()==null || room.getRoomConstraint().isAvailable(this, time, getScheduler())))
726                            ret++;
727            }
728            return ret;
729        }
730        public int nrValues(Vector rooms) {
731            int ret = 0;
732            for (Enumeration e=iTimeLocations.elements();e.hasMoreElements();) {
733                    TimeLocation time = (TimeLocation)e.nextElement();
734                    boolean available = true;
735                    for (Enumeration f=rooms.elements();available && f.hasMoreElements();) {
736                            RoomLocation room = (RoomLocation)f.nextElement();
737                            if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(time.getPreference())) || (room.getRoomConstraint()!=null && !room.getRoomConstraint().isAvailable(this, time, getScheduler())))
738                                    available = false;
739                    }
740                    if (available) ret++;
741            }
742            return ret;
743        }
744        public boolean allowBreakHard() { return sAllowBreakHard; }
745        public int getNrRooms() { return iNrRooms; }
746        public Lecture getParent() { return iParent; }
747        public void setParent(Lecture parent) { iParent = parent; iParent.addChild(this); }
748        public boolean hasParent() { return (iParent!=null);}
749        public boolean hasChildren(Long subpartId) { return (iChildren!=null && iChildren.get(subpartId)!=null && !((Vector)iChildren.get(subpartId)).isEmpty()); }
750        public boolean hasAnyChildren() { return (iChildren!=null && !iChildren.isEmpty()); }
751        public Vector getChildren(Long subpartId) { return (Vector)iChildren.get(subpartId); }
752        public Enumeration getChildrenSubpartIds() { return (iChildren==null?null:iChildren.keys()); }
753        private void addChild(Lecture child) {
754            if (iChildren==null) iChildren = new Hashtable();
755            Vector childrenThisSubpart = (Vector)iChildren.get(child.getSchedulingSubpartId());
756            if (childrenThisSubpart==null) {
757                    childrenThisSubpart = new FastVector();
758                    iChildren.put(child.getSchedulingSubpartId(),childrenThisSubpart);
759            }
760            childrenThisSubpart.addElement(child);
761        }
762        public boolean isSingleSection() {
763            if (iParent==null) 
764                    return (iSameSubpartLectures==null || iSameSubpartLectures.size()<=1);
765            return (iParent.getChildren(getSchedulingSubpartId()).size()<=1);
766        }
767        public boolean areStudentConflictsHard(Lecture lecture) {
768            return isSingleSection() && lecture.isSingleSection();
769        }
770        public Vector sameStudentsLectures() {
771            //return (hasParent()?getParent().getChildren():sameSubpartLectures());
772            return (hasParent()?getParent().getChildren(getSchedulingSubpartId()):sameSubpartLectures());
773        }
774        public Lecture getChild(Student student, Long subpartId) {
775            if (!hasAnyChildren()) return null;
776            Vector children = getChildren(subpartId);
777            if (children==null) return null;
778            for (Enumeration e=children.elements();e.hasMoreElements();) {
779                    Lecture child = (Lecture)e.nextElement();
780                    if (child.students().contains(student)) 
781                            return child;
782            }
783            return null;
784        }
785        
786        public int getCommitedConflicts(Placement placement) {
787            Integer ret = (Integer)iCommitedConflicts.get(placement);
788            if (ret==null) {
789                    ret = new Integer(placement.getCommitedConflicts());
790                    iCommitedConflicts.put(placement,ret);
791            }
792            return ret.intValue();
793        }
794        
795        public void assign(long iteration, Value value) {
796            if (value!=null && getModel()!=null) {
797                ((TimetableModel)getModel()).getCommittedStudentConflictsCounter().inc(getCommitedConflicts((Placement)value));
798            }
799            super.assign(iteration, value);
800        }
801        
802        public void unassign(long iteration) {
803            if (getAssignment()!=null && isCommitted())
804                    throw new RuntimeException("Unable to unassign committed variable ("+getName()+" "+getAssignment().getName()+")");
805            if (getAssignment()!=null && getModel()!=null) {
806                    ((TimetableModel)getModel()).getCommittedStudentConflictsCounter().dec(getCommitedConflicts((Placement)getAssignment()));
807            }
808            super.unassign(iteration);
809        }
810        
811        public Set hardGroupSoftConstraints() {
812            return iHardGroupSoftConstraints;
813        }
814        public Set groupConstraints() {
815            return iGroupConstraints;
816        }
817        
818        public int minRoomSize() {
819            if (iCacheMinRoomSize!=null) return iCacheMinRoomSize.intValue();
820            if (getNrRooms()<=1) {
821                    int min = Integer.MAX_VALUE;
822                    for (Enumeration e=roomLocations().elements();e.hasMoreElements();) {
823                            RoomLocation r = (RoomLocation)e.nextElement();
824                            if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(r.getPreference()))) continue;
825                            min = Math.min(min, r.getRoomSize());
826                    }
827                    iCacheMinRoomSize = new Integer(min);
828                    return min;
829            } else {
830                    Vector rl = new Vector(roomLocations());
831                    Collections.sort(rl);
832                    int min = 0; int i = 0;
833                    for (Enumeration e=rl.elements();e.hasMoreElements() && i<getNrRooms();) {
834                            RoomLocation r = (RoomLocation)e.nextElement();
835                            if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(r.getPreference()))) continue;
836                            min += r.getRoomSize(); i++;
837                    }
838                    iCacheMinRoomSize = new Integer(min);
839                    return min;
840            }
841        }
842        
843        public int maxRoomSize() {
844            if (iCacheMaxRoomSize!=null) return iCacheMaxRoomSize.intValue();
845            if (getNrRooms()<=1) {
846                    int max = Integer.MIN_VALUE;
847                    for (Enumeration e=roomLocations().elements();e.hasMoreElements();) {
848                            RoomLocation r = (RoomLocation)e.nextElement();
849                            if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(r.getPreference()))) continue;
850                            max = Math.max(max, r.getRoomSize());
851                    }
852                    iCacheMaxRoomSize = new Integer(max);
853                    return max;
854            } else {
855                    Vector rl = new Vector(roomLocations());
856                    Collections.sort(rl, Collections.reverseOrder());
857                    int max = 0; int i = 0;
858                    for (Enumeration e=rl.elements();e.hasMoreElements() && i<getNrRooms();) {
859                            RoomLocation r = (RoomLocation)e.nextElement();
860                            if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(r.getPreference()))) continue;
861                            max += r.getRoomSize(); i++;
862                    }
863                    iCacheMaxRoomSize = new Integer(max);
864                    return max;
865            }
866        }    
867        
868        public long getDiscouragedRoomSize() {
869            return Math.round(1.25 * minRoomSize());
870        }
871        public long getStronglyDiscouragedRoomSize() {
872            return Math.round(1.5 * minRoomSize());
873        }
874        
875        public boolean canShareRoom() {
876            return (!iCanShareRoomGroupConstraints.isEmpty());
877        }
878        
879        public boolean canShareRoom(Lecture other) {
880            if (other.equals(this)) return true;
881            for (Iterator i=iCanShareRoomGroupConstraints.iterator();i.hasNext();) {
882                    GroupConstraint gc = (GroupConstraint)i.next();
883                    if (gc.variables().contains(other)) return true;
884            }
885            return false;
886        }
887        
888        public Set canShareRoomConstraints() {
889            return iCanShareRoomGroupConstraints;
890        }
891        
892        public boolean isSingleton() {
893            return values().size()==1;
894        }
895        
896        public boolean isValid(Placement placement) {
897            TimetableModel model = (TimetableModel)getModel();
898            if (model==null) return true;
899            if (model.hasConstantVariables()) {
900                    for (Iterator i=model.conflictValues(placement).iterator();i.hasNext();) {
901                            Placement confPlacement = (Placement)i.next();
902                            Lecture lecture = (Lecture)confPlacement.variable();
903                            if (lecture.isCommitted()) return false;
904                            if (confPlacement.equals(placement)) return false;
905                    }
906            } else {
907                    if (model.conflictValues(placement).contains(placement))
908                            return false;
909            }
910            return true;
911        }
912        
913        public String getNotValidReason(Placement placement) {
914            TimetableModel model = (TimetableModel)getModel();
915            if (model==null) return "no model for class "+getName();
916                    Hashtable conflictConstraints = model.conflictConstraints(placement);
917                    for (Iterator i=conflictConstraints.entrySet().iterator();i.hasNext();) {
918                            Map.Entry entry = (Map.Entry)i.next();
919                            Constraint constraint = (Constraint)entry.getKey();
920                            Collection conflicts = (Collection)entry.getValue();
921                            String cname = constraint.getName();
922                            if (constraint instanceof RoomConstraint) {
923                                    cname = "Room "+constraint.getName();
924                            } else if (constraint instanceof InstructorConstraint) {
925                                    cname = "Instructor "+constraint.getName();
926                            } else if (constraint instanceof GroupConstraint) {
927                                    cname = "Distribution "+constraint.getName();
928                            } else if (constraint instanceof DepartmentSpreadConstraint) {
929                                    cname = "Balancing of department "+constraint.getName();
930                            } else if (constraint instanceof SpreadConstraint) {
931                                    cname = "Same subpart spread "+constraint.getName();
932                            } else if (constraint instanceof ClassLimitConstraint) {
933                                    cname = "Class limit "+constraint.getName();
934                            }
935                    for (Iterator j=conflicts.iterator();j.hasNext();) {
936                            Placement confPlacement = (Placement)j.next();
937                            Lecture lecture = (Lecture)confPlacement.variable();
938                            if (lecture.isCommitted()) {
939                                    return placement.getLongName()+" conflicts with "+lecture.getName()+" "+confPlacement.getLongName()+" due to constraint "+cname;
940                            }
941                            if (confPlacement.equals(placement)) {
942                                    return placement.getLongName()+" is not valid due to constraint "+cname; 
943                            }
944                    }
945            }
946            return null;
947        }
948        
949        public void purgeInvalidValues(boolean interactiveMode) {
950            if (isCommitted() || Lecture.sSaveMemory) return;
951            TimetableModel model = (TimetableModel)getModel();
952            if (model==null) return;
953            if (!model.hasConstantVariables()) return;
954            Vector newValues = new FastVector(values().size());
955            for (Enumeration e=values().elements();e.hasMoreElements();) {
956                    Placement placement = (Placement)e.nextElement();
957                    if (placement.isValid())
958                            newValues.addElement(placement);
959            }
960            if (!interactiveMode && newValues.size()!=values().size()) {
961                    for (Iterator i=timeLocations().iterator();i.hasNext();) {
962                            TimeLocation timeLocation = (TimeLocation)i.next();
963                            boolean hasPlacement = false;
964                            for (Enumeration e=newValues.elements();e.hasMoreElements();) {
965                                    Placement placement = (Placement)e.nextElement();
966                                    if (timeLocation.equals(placement.getTimeLocation())) {
967                                            hasPlacement = true; break;
968                                    }
969                            }
970                            if (!hasPlacement) i.remove();
971                    }
972                    for (Iterator i=roomLocations().iterator();i.hasNext();) {
973                            RoomLocation roomLocation = (RoomLocation)i.next();
974                            boolean hasPlacement = false;
975                            for (Enumeration e=newValues.elements();e.hasMoreElements();) {
976                                    Placement placement = (Placement)e.nextElement();
977                                    if (placement.isMultiRoom()) {
978                                            if (placement.getRoomLocations().contains(roomLocation)) {
979                                                    hasPlacement = true; break;
980                                            }
981                                    } else {
982                                            if (roomLocation.equals(placement.getRoomLocation())) {
983                                                    hasPlacement = true; break;
984                                            }
985                                    }
986                            }
987                            if (!hasPlacement) i.remove();
988                    }
989            }
990            setValues(newValues);
991        }
992        
993        public void setCommitted(boolean committed) {
994            iCommitted = committed;
995        }
996        public boolean isCommitted() { return iCommitted; }
997        public boolean isConstant() { return iCommitted; }
998        
999        public int getSpreadPenalty() {
1000            int spread = 0;
1001            for (Iterator i=getSpreadConstraints().iterator();i.hasNext();) {
1002                    SpreadConstraint sc = (SpreadConstraint)i.next();
1003                    spread += sc.getPenalty();
1004            }
1005            return spread;
1006        }
1007        public int hashCode() {
1008            return getClassId().hashCode();
1009        }
1010    
1011        public Configuration getConfiguration() {
1012            Lecture lecture = this;
1013            while (lecture.getParent()!=null) lecture = lecture.getParent();
1014            return lecture.iParentConfiguration;
1015        }
1016        
1017        public void setConfiguration(Configuration configuration) {
1018            Lecture lecture = this;
1019            while (lecture.getParent()!=null) lecture = lecture.getParent();
1020            lecture.iParentConfiguration = configuration;
1021            configuration.addTopLecture(lecture);
1022        }
1023        
1024        private int[] iMinMaxRoomPreference = null;
1025        public int[] getMinMaxRoomPreference() {
1026            if (iMinMaxRoomPreference==null) {
1027                    if (getNrRooms()<=0 || roomLocations().isEmpty()) {
1028                            iMinMaxRoomPreference = new int[] {0,0};
1029                    } else {
1030                            int minRoomPref = Integer.MAX_VALUE;
1031                            int maxRoomPref = Integer.MIN_VALUE;
1032                            for (Enumeration e=roomLocations().elements();e.hasMoreElements();) {
1033                                    RoomLocation r = (RoomLocation)e.nextElement();
1034                                    int pref = r.getPreference();
1035                                    if (pref>Constants.sPreferenceLevelRequired/2)
1036                                            minRoomPref = Math.min(minRoomPref,pref);
1037                                    if (pref<Constants.sPreferenceLevelProhibited/2)
1038                                            maxRoomPref = Math.max(maxRoomPref,pref);
1039                            }
1040                            iMinMaxRoomPreference = new int[] {minRoomPref, maxRoomPref};
1041                    }
1042            }
1043            return iMinMaxRoomPreference;
1044        }
1045    
1046        private double[] iMinMaxTimePreference = null;
1047        public double[] getMinMaxTimePreference() {
1048            if (iMinMaxTimePreference==null) {
1049                    double minTimePref = Double.MAX_VALUE; 
1050                    double maxTimePref = -Double.MAX_VALUE;
1051                    for (Enumeration e=timeLocations().elements();e.hasMoreElements();) {
1052                            TimeLocation t = (TimeLocation)e.nextElement();
1053                            double pref = t.getNormalizedPreference();
1054                            if (pref>Constants.sPreferenceLevelRequired/2)
1055                                    minTimePref = Math.min(minTimePref,pref);
1056                            if (pref<Constants.sPreferenceLevelProhibited/2)
1057                                    maxTimePref = Math.max(maxTimePref,pref);
1058                    }
1059                    iMinMaxTimePreference = new double[] {minTimePref, maxTimePref};
1060            }
1061            return iMinMaxTimePreference;
1062        }
1063        
1064        public void setOrd(int ord) { iOrd = ord; }
1065        public int getOrd() { return iOrd; }
1066        public int compareTo(Object o) {
1067            if (o==null || !(o instanceof Lecture)) return -1;
1068            int cmp = Double.compare(getOrd(),((Lecture)o).getOrd());
1069            if (cmp!=0) return cmp;
1070            return super.compareTo(o);
1071        }
1072        
1073        public String getNote() { return iNote; }
1074        public void setNote(String note) { iNote = note; }
1075    }