001    package net.sf.cpsolver.coursett.model;
002    
003    
004    import java.util.Enumeration;
005    import java.util.HashSet;
006    import java.util.Hashtable;
007    import java.util.Locale;
008    import java.util.Set;
009    import java.util.Vector;
010    
011    import net.sf.cpsolver.coursett.Constants;
012    import net.sf.cpsolver.coursett.constraint.ClassLimitConstraint;
013    import net.sf.cpsolver.coursett.constraint.DepartmentSpreadConstraint;
014    import net.sf.cpsolver.coursett.constraint.GroupConstraint;
015    import net.sf.cpsolver.coursett.constraint.InstructorConstraint;
016    import net.sf.cpsolver.coursett.constraint.JenrlConstraint;
017    import net.sf.cpsolver.coursett.constraint.RoomConstraint;
018    import net.sf.cpsolver.coursett.constraint.SpreadConstraint;
019    import net.sf.cpsolver.coursett.heuristics.TimetableComparator;
020    import net.sf.cpsolver.coursett.heuristics.UniversalPerturbationsCounter;
021    import net.sf.cpsolver.ifs.constant.ConstantModel;
022    import net.sf.cpsolver.ifs.model.Constraint;
023    import net.sf.cpsolver.ifs.model.Value;
024    import net.sf.cpsolver.ifs.model.Variable;
025    import net.sf.cpsolver.ifs.perturbations.PerturbationsCounter;
026    import net.sf.cpsolver.ifs.solver.Solver;
027    import net.sf.cpsolver.ifs.util.Counter;
028    import net.sf.cpsolver.ifs.util.DataProperties;
029    import net.sf.cpsolver.ifs.util.FastVector;
030    
031    
032    /**
033     * Timetable model.
034     * 
035     * @version
036     * CourseTT 1.1 (University Course Timetabling)<br>
037     * Copyright (C) 2006 Tomáš Müller<br>
038     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
039     * Lazenska 391, 76314 Zlin, Czech Republic<br>
040     * <br>
041     * This library is free software; you can redistribute it and/or
042     * modify it under the terms of the GNU Lesser General Public
043     * License as published by the Free Software Foundation; either
044     * version 2.1 of the License, or (at your option) any later version.
045     * <br><br>
046     * This library is distributed in the hope that it will be useful,
047     * but WITHOUT ANY WARRANTY; without even the implied warranty of
048     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
049     * Lesser General Public License for more details.
050     * <br><br>
051     * You should have received a copy of the GNU Lesser General Public
052     * License along with this library; if not, write to the Free Software
053     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
054     */
055    
056    public class TimetableModel extends ConstantModel {
057        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(TimetableModel.class);
058        private static java.text.DecimalFormat sDoubleFormat = new java.text.DecimalFormat("0.00",new java.text.DecimalFormatSymbols(Locale.US));
059        private long iGlobalRoomPreference = 0;
060        private double iGlobalTimePreference = 0;
061        private long iMinRoomPreference = 0;
062        private long iMaxRoomPreference = 0;
063        private long iBestInstructorDistancePreference = 0;
064        private double iMinTimePreference = 0;
065        private double iMaxTimePreference = 0;
066        private int iBestDepartmentSpreadPenalty = 0;
067        private int iBestSpreadPenalty = 0;
068        private int iBestCommitedStudentConflicts = 0;
069        private int iMaxGroupConstraintPreference = 0;
070        private int iMinGroupConstraintPreference = 0;
071        private Counter iGlobalGroupConstraintPreference = new Counter();
072        private Counter iViolatedStudentConflicts = new Counter();
073        private Counter iViolatedHardStudentConflicts = new Counter();
074        private Counter iViolatedDistanceStudentConflicts = new Counter();
075        private Counter iCommittedStudentConflictsCounter = new Counter();
076        private FastVector iInstructorConstraints = new FastVector();
077        private FastVector iJenrlConstraints = new FastVector();
078        private FastVector iRoomConstraints = new FastVector();
079        private FastVector iDepartmentSpreadConstraints = new FastVector();
080        private FastVector iSpreadConstraints = new FastVector();
081        private FastVector iGroupConstraints = new FastVector();
082        private FastVector iClassLimitConstraints = new FastVector();
083        private DataProperties iProperties = null;
084        private TimetableComparator iCmp = null;
085        private UniversalPerturbationsCounter iPertCnt = null;
086        private int iYear = -1;
087        
088        private HashSet iAllStudents = new HashSet();
089        
090        /** Back-to-back classes: maximal distance for no prefernce */
091            private double iInstructorNoPreferenceLimit = 0.0;
092        /** Back-to-back classes: maximal distance for discouraged prefernce */
093            private double iInstructorDiscouragedLimit = 5.0;
094        /** Back-to-back classes: maximal distance for strongly discouraged prefernce (everything above is prohibited) */
095            private double iInstructorProhibitedLimit = 20.0;
096            
097            private double iStudentDistanceLimit = 67.0;
098            private double iStudentDistanceLimit75min = 100.0;
099        
100        public TimetableModel(DataProperties properties) {
101            super();
102            iProperties = properties;
103            iInstructorNoPreferenceLimit = iProperties.getPropertyDouble("Instructor.NoPreferenceLimit", iInstructorNoPreferenceLimit);
104            iInstructorDiscouragedLimit = iProperties.getPropertyDouble("Instructor.DiscouragedLimit",iInstructorDiscouragedLimit);
105            iInstructorProhibitedLimit = iProperties.getPropertyDouble("Instructor.ProhibitedLimit",iInstructorProhibitedLimit);
106            iStudentDistanceLimit = iProperties.getPropertyDouble("Student.DistanceLimit",iStudentDistanceLimit);
107            iStudentDistanceLimit75min = iProperties.getPropertyDouble("Student.DistanceLimit75min",iStudentDistanceLimit75min);
108            iCmp = new TimetableComparator(properties);
109            iPertCnt = new UniversalPerturbationsCounter(properties);
110            if (properties.getPropertyBoolean("OnFlySectioning.Enabled", false)) addModelListener(new OnFlySectioning(this));
111        }
112        
113        public double getInstructorNoPreferenceLimit() {
114            return iInstructorNoPreferenceLimit;
115        }
116        
117        public double getInstructorDiscouragedLimit() {
118            return iInstructorDiscouragedLimit;
119        }
120        
121        public double getInstructorProhibitedLimit() {
122            return iInstructorProhibitedLimit;
123        }
124        
125        public double getStudentDistanceLimit() {
126            return iStudentDistanceLimit;
127        }
128        
129        public double getStudentDistanceLimit75min() {
130            return iStudentDistanceLimit75min;
131        }
132        
133        public boolean init(Solver solver) {
134            iCmp = new TimetableComparator(solver.getProperties());
135            iPertCnt = new UniversalPerturbationsCounter(solver.getProperties());
136            return super.init(solver);
137        }
138        
139        public void addVariable(Variable variable) {
140            super.addVariable(variable);
141            Lecture lecture = (Lecture)variable;
142            if (lecture.isCommitted()) return;
143            double[] minMaxTimePref = lecture.getMinMaxTimePreference();
144            iMinTimePreference += minMaxTimePref[0];
145            iMaxTimePreference += minMaxTimePref[1];
146            int[] minMaxRoomPref = lecture.getMinMaxRoomPreference();
147            iMinRoomPreference += minMaxRoomPref[0];
148            iMaxRoomPreference += minMaxRoomPref[1];
149        }
150        
151        public void removeVariable(Variable variable) {
152            super.removeVariable(variable);
153            Lecture lecture = (Lecture)variable;
154            if (lecture.isCommitted()) return;
155            double[] minMaxTimePref = lecture.getMinMaxTimePreference();
156            iMinTimePreference -= minMaxTimePref[0];
157            iMaxTimePreference -= minMaxTimePref[1];
158            int[] minMaxRoomPref = lecture.getMinMaxRoomPreference();
159            iMinRoomPreference -= minMaxRoomPref[0];
160            iMaxRoomPreference -= minMaxRoomPref[1];
161        }
162        
163        
164        public DataProperties getProperties() { return iProperties; }
165    
166        /** Overall room preference */
167        public long getGlobalRoomPreference() { return iGlobalRoomPreference; }
168        /** Overall time preference */
169        public double getGlobalTimePreference() { return iGlobalTimePreference; }
170        /** Number of student conflicts */
171        public long getViolatedStudentConflicts() { return iViolatedStudentConflicts.get(); }
172        /** Number of student conflicts */
173        public long countViolatedStudentConflicts() {
174            long studentConflicts = 0;
175            for (Enumeration it1=iJenrlConstraints.elements();it1.hasMoreElements();) {
176                JenrlConstraint jenrl = (JenrlConstraint)it1.nextElement();
177                Lecture a = (Lecture)jenrl.first();
178                Lecture b = (Lecture)jenrl.second();
179                //if (a.getAssignment()!=null && b.getAssignment()!=null && JenrlConstraint.isInConflict((Placement)a.getAssignment(),(Placement)b.getAssignment()))
180                if (jenrl.isInConflict())
181                    studentConflicts+=jenrl.getJenrl();
182            }
183            return studentConflicts;
184        }
185        /** Number of student conflicts */
186        public Counter getViolatedStudentConflictsCounter() { return iViolatedStudentConflicts; }
187        public Counter getViolatedHardStudentConflictsCounter() { return iViolatedHardStudentConflicts; }
188        public Counter getViolatedDistanceStudentConflictsCounter() { return iViolatedDistanceStudentConflicts; }
189        
190        /** Overall group constraint preference */
191        public long getGlobalGroupConstraintPreference() { return iGlobalGroupConstraintPreference.get(); }
192        /** Overall group constraint preference */
193        public Counter getGlobalGroupConstraintPreferenceCounter() { return iGlobalGroupConstraintPreference; }
194        /** Overall instructor distance (back-to-back) preference */
195        public long getInstructorDistancePreference() {
196            long pref = 0;
197            for (Enumeration it1=iInstructorConstraints.elements();it1.hasMoreElements();) {
198                InstructorConstraint constraint = (InstructorConstraint)it1.nextElement();
199                pref+=constraint.getPreference();
200            }
201            return pref;
202        }
203        /** The worst instructor distance (back-to-back) preference */
204        public long getInstructorWorstDistancePreference() {
205            long pref = 0;
206            for (Enumeration it1=iInstructorConstraints.elements();it1.hasMoreElements();) {
207                InstructorConstraint constraint = (InstructorConstraint)it1.nextElement();
208                pref+=constraint.getWorstPreference();
209            }
210            return pref;
211        }
212        /** Overall number of useless time slots */
213        public long getUselessSlots() {
214            long uselessSlots = 0;
215            for (Enumeration it1=iRoomConstraints.elements();it1.hasMoreElements();) {
216                RoomConstraint constraint = (RoomConstraint)it1.nextElement();
217                uselessSlots+=((RoomConstraint)constraint).countUselessSlots();
218            }
219            return uselessSlots;
220        }
221        /** Overall number of useless time slots */
222        public long getUselessHalfHours() {
223            long uselessSlots = 0;
224            for (Enumeration it1=iRoomConstraints.elements();it1.hasMoreElements();) {
225                RoomConstraint constraint = (RoomConstraint)it1.nextElement();
226                uselessSlots+=((RoomConstraint)constraint).countUselessSlotsHalfHours();
227            }
228            return uselessSlots;
229        }
230        /** Overall number of useless time slots */
231        public long getBrokenTimePatterns() {
232            long uselessSlots = 0;
233            for (Enumeration it1=iRoomConstraints.elements();it1.hasMoreElements();) {
234                RoomConstraint constraint = (RoomConstraint)it1.nextElement();
235                uselessSlots+=((RoomConstraint)constraint).countUselessSlotsBrokenTimePatterns();
236            }
237            return uselessSlots;
238        }
239        /** Overall number of student conflicts caused by distancies (back-to-back classes are too far)*/
240        public long getStudentDistanceConflicts() {
241            /*
242            if (iViolatedDistanceStudentConflicts.get()!=countStudentDistanceConflicts()) {
243                    System.err.println("TimetableModel.getStudentDistanceConflicts() is not working properly");
244            }
245            */
246            return iViolatedDistanceStudentConflicts.get();
247        }
248        public long countStudentDistanceConflicts() {
249            long nrConflicts = 0;
250            for (Enumeration it1=iJenrlConstraints.elements();it1.hasMoreElements();) {
251                JenrlConstraint jenrl = (JenrlConstraint)it1.nextElement();
252                if (jenrl.isInConflict() && 
253                        !((Placement)jenrl.first().getAssignment()).getTimeLocation().hasIntersection(((Placement)jenrl.second().getAssignment()).getTimeLocation()))
254                        nrConflicts += jenrl.getJenrl();
255            }
256            return nrConflicts;
257        }
258        /** Overall hard student conflicts (student conflict between single section classes) */ 
259        public long getHardStudentConflicts() {
260            /*
261            if (iViolatedHardStudentConflicts.get()!=countHardStudentConflicts()) {
262                    System.err.println("TimetableModel.getHardStudentConflicts() is not working properly");
263            }
264            */
265            return iViolatedHardStudentConflicts.get(); 
266        }
267        public long countHardStudentConflicts() {
268            long hardStudentConflicts = 0;
269            for (Enumeration it1=iJenrlConstraints.elements();it1.hasMoreElements();) {
270                JenrlConstraint jenrl = (JenrlConstraint)it1.nextElement();
271                if (jenrl.isInConflict()) {
272                    Lecture l1 = (Lecture)jenrl.first();
273                    Lecture l2 = (Lecture)jenrl.second();
274                    if (l1.areStudentConflictsHard(l2))
275                        hardStudentConflicts+=jenrl.getJenrl();
276                }
277            }
278            return hardStudentConflicts;
279        }
280        public Counter getCommittedStudentConflictsCounter() { return iCommittedStudentConflictsCounter; }
281        public int getCommitedStudentConflicts() {
282            /*
283            if (iCommittedStudentConflictsCounter.get()!=countCommitedStudentConflicts()) {
284                    System.err.println("TimetableModel.getCommitedStudentConflicts() is not working properly");
285            }
286            */
287            return (int)iCommittedStudentConflictsCounter.get(); 
288        }
289        public int countCommitedStudentConflicts() {
290            int commitedStudentConflicts = 0;
291            for (Enumeration e=assignedVariables().elements();e.hasMoreElements();) {
292                    Lecture lecture = (Lecture)e.nextElement();
293                    commitedStudentConflicts+=lecture.getCommitedConflicts((Placement)lecture.getAssignment());
294            }
295            return commitedStudentConflicts;
296        }
297        
298        /** When a value is assigned to a variable -- update gloval preferences */
299        public void afterAssigned(long iteration, Value value) {
300            super.afterAssigned(iteration, value);
301            if (value==null) return;
302            Placement placement = (Placement)value;
303            iGlobalRoomPreference += placement.sumRoomPreference();
304            iGlobalTimePreference += placement.getTimeLocation().getNormalizedPreference();
305        }
306        /** When a value is unassigned from a variable -- update gloval preferences */
307        public void afterUnassigned(long iteration, Value value) {
308            super.afterUnassigned(iteration, value);
309            if (value==null) return;
310            Placement placement = (Placement)value;
311            iGlobalRoomPreference -= placement.sumRoomPreference();
312            iGlobalTimePreference -= placement.getTimeLocation().getNormalizedPreference();
313        }
314    
315        /** Student final sectioning (switching students between sections of the same class in order to minimize overall number of student conflicts) */
316        public void switchStudents() {
317            FinalSectioning sect = new FinalSectioning(this);
318            sect.run();
319        }
320    
321        public String toString() {
322            return "TimetableModel{"+
323            "\n  super="+super.toString()+
324            "\n  studentConflicts="+iViolatedStudentConflicts.get()+
325    //        "\n  studentConflicts(violated room distance)="+iViolatedRoomDistanceStudentConflicts.get()+
326    //        "\n  studentPreferences="+iRoomDistanceStudentPreference.get()+
327            "\n  roomPreferences="+iGlobalRoomPreference+
328            "\n  timePreferences="+iGlobalTimePreference+
329            "\n  groupConstraintPreferences="+iGlobalGroupConstraintPreference.get()+
330            "\n}";
331        }
332        
333        /** Overall number of too big rooms (rooms with more than 3/2 seats than needed) */
334        public int countTooBigRooms() {
335            int tooBigRooms=0;
336            for (Enumeration it1=assignedVariables().elements();it1.hasMoreElements();) {
337                Lecture lecture = (Lecture)it1.nextElement();
338                if (lecture.getAssignment()==null) continue;
339                Placement placement = (Placement)lecture.getAssignment();
340                tooBigRooms += placement.getTooBigRoomPreference();
341            }
342            return tooBigRooms;
343        }
344    
345        /** Overall departmental spread penalty */
346        public int getDepartmentSpreadPenalty() {
347            if (iDepartmentSpreadConstraints.isEmpty()) return 0;
348            int penalty = 0;
349            for (Enumeration e=iDepartmentSpreadConstraints.elements();e.hasMoreElements();) {
350                DepartmentSpreadConstraint c = (DepartmentSpreadConstraint)e.nextElement();
351                penalty += ((DepartmentSpreadConstraint)c).getPenalty();
352            }
353            return penalty;
354        }
355        
356        /** Overall spread penalty */
357        public int getSpreadPenalty() {
358            if (iSpreadConstraints.isEmpty()) return 0;
359            int penalty = 0;
360            for (Enumeration e=iSpreadConstraints.elements();e.hasMoreElements();) {
361                SpreadConstraint c = (SpreadConstraint)e.nextElement();
362                penalty += ((SpreadConstraint)c).getPenalty();
363            }
364            return penalty;
365        }
366        
367        public java.util.Hashtable getBounds() {
368            Hashtable ret = new Hashtable();
369            ret.put("Room preferences min", ""+iMinRoomPreference);
370            ret.put("Room preferences max", ""+iMaxRoomPreference);
371            ret.put("Time preferences min", ""+iMinTimePreference);
372            ret.put("Time preferences max", ""+iMaxTimePreference);
373            ret.put("Distribution preferences min", ""+iMinGroupConstraintPreference);
374            ret.put("Distribution preferences max", ""+iMaxGroupConstraintPreference);
375            if (getProperties().getPropertyBoolean("General.UseDistanceConstraints",false)) {
376                ret.put("Back-to-back instructor preferences max", ""+getInstructorWorstDistancePreference());
377            }
378            ret.put("Too big rooms max", ""+(Constants.sPreferenceLevelStronglyDiscouraged*variables().size()));
379            ret.put("Useless half-hours", ""+(Constants.sPreferenceLevelStronglyDiscouraged*getRoomConstraints().size()*Constants.SLOTS_PER_DAY_NO_EVENINGS*Constants.NR_DAYS_WEEK));
380            return ret;
381        }
382        
383        /** Global info */
384        public java.util.Hashtable getInfo() {
385            Hashtable ret = super.getInfo();
386            ret.put("Memory usage", getMem());
387            ret.put("Room preferences", getPerc(iGlobalRoomPreference,iMinRoomPreference,iMaxRoomPreference)+"% ("+iGlobalRoomPreference+")");
388            ret.put("Time preferences", getPerc(iGlobalTimePreference,iMinTimePreference,iMaxTimePreference)+"% ("+sDoubleFormat.format(iGlobalTimePreference)+")");
389            ret.put("Distribution preferences", getPerc(iGlobalGroupConstraintPreference.get(),iMinGroupConstraintPreference,iMaxGroupConstraintPreference)+"% ("+iGlobalGroupConstraintPreference.get()+")");
390            int commitedStudentConflicts = getCommitedStudentConflicts(); 
391            ret.put("Student conflicts", (commitedStudentConflicts+getViolatedStudentConflicts())+" [committed:"+commitedStudentConflicts+", hard:"+getHardStudentConflicts()+"]");
392            if (getProperties().getPropertyBoolean("General.UseDistanceConstraints",false)) {
393                    ret.put("Student conflicts", (commitedStudentConflicts+getViolatedStudentConflicts())+" [committed:"+commitedStudentConflicts+", distance:"+getStudentDistanceConflicts()+", hard:"+getHardStudentConflicts()+"]");
394                ret.put("Back-to-back instructor preferences", getPerc(getInstructorDistancePreference(),0,getInstructorWorstDistancePreference())+"% ("+getInstructorDistancePreference()+")");
395            }
396            if (getProperties().getPropertyBoolean("General.DeptBalancing", false)) {
397                ret.put("Department balancing penalty", sDoubleFormat.format(((double)getDepartmentSpreadPenalty())/12.0));
398            }
399            ret.put("Same subpart balancing penalty", sDoubleFormat.format(((double)getSpreadPenalty())/12.0));
400            ret.put("Too big rooms", getPercRev(countTooBigRooms(),0,Constants.sPreferenceLevelStronglyDiscouraged*variables().size())+"% ("+countTooBigRooms()+")");
401            ret.put("Useless half-hours", getPercRev(getUselessSlots(),0,Constants.sPreferenceLevelStronglyDiscouraged*getRoomConstraints().size()*Constants.SLOTS_PER_DAY_NO_EVENINGS*Constants.NR_DAYS_WEEK)+"% ("+getUselessHalfHours()+" + "+getBrokenTimePatterns()+")");
402            return ret;
403        }
404        
405        public java.util.Hashtable getInfo(Vector variables) {
406            Hashtable ret = super.getInfo(variables);
407            ret.put("Memory usage", getMem());
408            
409            int roomPref = 0, minRoomPref = 0, maxRoomPref = 0;
410            double timePref = 0, minTimePref = 0, maxTimePref = 0;
411            double grPref = 0, minGrPref = 0, maxGrPref = 0;
412            long allSC = 0, hardSC = 0, distSC = 0;
413            int instPref = 0, worstInstrPref = 0;
414            int spreadPen = 0, deptSpreadPen = 0;
415            int tooBigRooms = 0;
416            int rcs = 0, uselessSlots = 0, uselessSlotsHH = 0, uselessSlotsBTP = 0;
417            
418            HashSet used = new HashSet();
419            
420            for (Enumeration e=variables.elements();e.hasMoreElements();) {
421                    Lecture lecture = (Lecture)e.nextElement();
422                    if (lecture.isCommitted()) continue;
423                    Placement placement = (Placement)lecture.getAssignment();
424                    
425                    int[] minMaxRoomPref = lecture.getMinMaxRoomPreference();
426                    minRoomPref += minMaxRoomPref[0];
427                    maxRoomPref += minMaxRoomPref[1];
428                    
429                    double[] minMaxTimePref = lecture.getMinMaxTimePreference();
430                    minTimePref += minMaxTimePref[0];
431                    maxTimePref += minMaxTimePref[1];
432                    
433                    if (placement!=null) {
434                            roomPref += placement.getRoomPreference();
435                            timePref += placement.getTimeLocation().getNormalizedPreference();
436                            tooBigRooms += placement.getTooBigRoomPreference();
437                    }
438                    
439                    for (Enumeration f=lecture.constraints().elements();f.hasMoreElements();) {
440                            Constraint c = (Constraint)f.nextElement();
441                            if (!used.add(c)) continue;
442                            
443                            if (c instanceof InstructorConstraint) {
444                                    InstructorConstraint ic = (InstructorConstraint)c;
445                                    instPref += ic.getPreference();
446                                    worstInstrPref += ic.getWorstPreference();
447                            }
448                            
449                            if (c instanceof DepartmentSpreadConstraint) {
450                                    DepartmentSpreadConstraint dsc = (DepartmentSpreadConstraint)c;
451                                    deptSpreadPen += dsc.getPenalty();
452                            } else if (c instanceof SpreadConstraint) {
453                                    SpreadConstraint sc = (SpreadConstraint)c;
454                                    spreadPen += sc.getPenalty();
455                            }
456                            
457                            if (c instanceof GroupConstraint) {
458                                    GroupConstraint gc = (GroupConstraint)c;
459                                    if (gc.isHard()) continue;
460                            minGrPref += Math.min(gc.getPreference(),0);
461                            maxGrPref += Math.max(gc.getPreference(),0);
462                            grPref += gc.getCurrentPreference();
463                            }
464                            
465                            if (c instanceof JenrlConstraint) {
466                                    JenrlConstraint jc = (JenrlConstraint)c;
467                                    if (!jc.isInConflict() || !jc.isOfTheSameProblem()) continue;
468                            Lecture l1 = (Lecture)jc.first();
469                            Lecture l2 = (Lecture)jc.second();
470                            allSC += jc.getJenrl();
471                            if (l1.areStudentConflictsHard(l2))
472                                    hardSC += jc.getJenrl();
473                            Placement p1 = (Placement)l1.getAssignment();
474                            Placement p2 = (Placement)l2.getAssignment();
475                            if (!p1.getTimeLocation().hasIntersection(p2.getTimeLocation()))
476                                    distSC += jc.getJenrl();
477                            }
478                            
479                            if (c instanceof RoomConstraint) {
480                                    RoomConstraint rc = (RoomConstraint)c;
481                                    uselessSlots+=rc.countUselessSlots();
482                                    uselessSlotsHH+=rc.countUselessSlotsHalfHours();
483                                    uselessSlotsBTP+=rc.countUselessSlotsBrokenTimePatterns();
484                                    rcs ++;
485                            }
486                    }
487            }
488            
489            
490            ret.put("Room preferences", getPerc(roomPref,minRoomPref,maxRoomPref)+"% ("+roomPref+")");
491            ret.put("Time preferences", getPerc(timePref,minTimePref,maxTimePref)+"% ("+sDoubleFormat.format(timePref)+")");
492            ret.put("Distribution preferences", getPerc(grPref,minGrPref,maxGrPref)+"% ("+grPref+")");
493            ret.put("Student conflicts", allSC+" [committed:"+0+", hard:"+hardSC+"]");
494            if (getProperties().getPropertyBoolean("General.UseDistanceConstraints",false)) {
495                    ret.put("Student conflicts", allSC+" [committed:"+0+", distance:"+distSC+", hard:"+hardSC+"]");
496                ret.put("Back-to-back instructor preferences", getPerc(instPref,0,worstInstrPref)+"% ("+instPref+")");
497            }
498            if (getProperties().getPropertyBoolean("General.DeptBalancing", false)) {
499                ret.put("Department balancing penalty", sDoubleFormat.format(((double)deptSpreadPen)/12.0));
500            }
501            ret.put("Same subpart balancing penalty", sDoubleFormat.format(((double)spreadPen)/12.0));
502            ret.put("Too big rooms", getPercRev(tooBigRooms,0,Constants.sPreferenceLevelStronglyDiscouraged*variables.size())+"% ("+tooBigRooms+")");
503            ret.put("Useless half-hours", getPercRev(uselessSlots,0,Constants.sPreferenceLevelStronglyDiscouraged*rcs*Constants.SLOTS_PER_DAY_NO_EVENINGS*Constants.NR_DAYS_WEEK)+"% ("+uselessSlotsHH+" + "+uselessSlotsBTP+")");
504    
505            return ret;
506        }
507    
508        private int iBestTooBigRooms;
509        private long iBestUselessSlots;
510        private double iBestGlobalTimePreference;
511        private long iBestGlobalRoomPreference;
512        private long iBestGlobalGroupConstraintPreference;
513        private long iBestViolatedStudentConflicts;
514        private long iBestHardStudentConflicts;
515    
516        /** Overall number of too big rooms of the best solution ever found */
517        public int bestTooBigRooms() { return iBestTooBigRooms; }
518        /** Overall number of useless slots of the best solution ever found */
519        public long bestUselessSlots() { return iBestUselessSlots;}
520        /** Overall time preference of the best solution ever found */
521        public double bestGlobalTimePreference() { return iBestGlobalTimePreference;}
522        /** Overall room preference of the best solution ever found */
523        public long bestGlobalRoomPreference() { return iBestGlobalRoomPreference;}
524        /** Overall group constraint preference of the best solution ever found */
525        public long bestGlobalGroupConstraintPreference() { return iBestGlobalGroupConstraintPreference;}
526        /** Overall number of student conflicts of the best solution ever found */
527        public long bestViolatedStudentConflicts() { return iBestViolatedStudentConflicts;}
528        /** Overall number of student conflicts between single section classes of the best solution ever found */
529        public long bestHardStudentConflicts() { return iBestHardStudentConflicts;}
530        /** Overall instructor distance preference of the best solution ever found */
531        public long bestInstructorDistancePreference() { return iBestInstructorDistancePreference; }
532        /** Overall departmental spread penalty of the best solution ever found */
533        public int bestDepartmentSpreadPenalty() { return iBestDepartmentSpreadPenalty; }
534        public int bestSpreadPenalty() { return iBestSpreadPenalty; }
535        public int bestCommitedStudentConflicts() { return iBestCommitedStudentConflicts; }
536        
537        public void saveBest() {
538            super.saveBest();
539            iBestTooBigRooms = countTooBigRooms();
540            iBestUselessSlots = getUselessSlots();
541            iBestGlobalTimePreference = getGlobalTimePreference();
542            iBestGlobalRoomPreference = getGlobalRoomPreference();
543            iBestGlobalGroupConstraintPreference = getGlobalGroupConstraintPreference();
544            iBestViolatedStudentConflicts = getViolatedStudentConflicts();
545            iBestHardStudentConflicts = getHardStudentConflicts();
546            iBestInstructorDistancePreference = getInstructorDistancePreference();
547            iBestDepartmentSpreadPenalty = getDepartmentSpreadPenalty();
548            iBestSpreadPenalty = getSpreadPenalty();
549            iBestCommitedStudentConflicts = getCommitedStudentConflicts();
550        }
551    
552        public void addConstraint(Constraint constraint) {
553            super.addConstraint(constraint);
554            if (constraint instanceof InstructorConstraint) {
555                iInstructorConstraints.addElement(constraint);
556            } else if (constraint instanceof JenrlConstraint) {
557                iJenrlConstraints.addElement(constraint);
558            } else if (constraint instanceof RoomConstraint) {
559                iRoomConstraints.addElement(constraint);
560            } else if (constraint instanceof DepartmentSpreadConstraint) {
561                iDepartmentSpreadConstraints.addElement(constraint);
562            } else if (constraint instanceof SpreadConstraint) {
563                iSpreadConstraints.addElement(constraint);
564            } else if (constraint instanceof ClassLimitConstraint) {
565                    iClassLimitConstraints.addElement(constraint);
566            } else if (constraint instanceof GroupConstraint) {
567                iGroupConstraints.addElement(constraint);
568                if (!constraint.isHard()) {
569                    GroupConstraint gc = (GroupConstraint)constraint;
570                    iMinGroupConstraintPreference += Math.min(gc.getPreference(),0);
571                    iMaxGroupConstraintPreference += Math.max(gc.getPreference(),0);
572                }
573            }
574        }
575        public void removeConstraint(Constraint constraint) {
576            super.removeConstraint(constraint);
577            if (constraint instanceof InstructorConstraint) {
578                iInstructorConstraints.removeElement(constraint);
579            } else if (constraint instanceof JenrlConstraint) {
580                iJenrlConstraints.removeElement(constraint);
581            } else if (constraint instanceof RoomConstraint) {
582                iRoomConstraints.removeElement(constraint);
583            } else if (constraint instanceof DepartmentSpreadConstraint) {
584                iDepartmentSpreadConstraints.removeElement(constraint);
585            } else if (constraint instanceof SpreadConstraint) {
586                iSpreadConstraints.removeElement(constraint);
587            } else if (constraint instanceof ClassLimitConstraint) {
588                    iClassLimitConstraints.removeElement(constraint);
589            } else if (constraint instanceof GroupConstraint) {
590                iGroupConstraints.removeElement(constraint);
591            }
592        }
593    
594        /** The list of all instructor constraints */
595        public Vector getInstructorConstraints() { return iInstructorConstraints; }
596        /** The list of all group constraints */
597        public Vector getGroupConstraints() { return iGroupConstraints; }
598        /** The list of all jenrl constraints */
599        public Vector getJenrlConstraints() { return iJenrlConstraints; }
600        /** The list of all room constraints */
601        public Vector getRoomConstraints() { return iRoomConstraints; }
602        /** The list of all departmental spread constraints */
603        public Vector getDepartmentSpreadConstraints() { return iDepartmentSpreadConstraints;  }
604        public Vector getSpreadConstraints() { return iSpreadConstraints;  }
605        public Vector getClassLimitConstraints() { return iClassLimitConstraints; }
606        /** Max capacity for too big rooms (3/2 of the number of students) */
607        public double getTotalValue() {
608            return iCmp.currentValue(this,iPertCnt);
609        }
610        public double getTotalValue(Vector variables) {
611            return iCmp.currentValue(this,iPertCnt, variables);
612        }
613        
614        public int getYear() { return iYear; }
615        public void setYear(int year) { iYear=year; }
616        
617        public TimetableComparator getTimetableComparator() { return iCmp;}
618        public PerturbationsCounter getPerturbationsCounter() { return iPertCnt; }
619        
620        public Set getAllStudents() {
621            return iAllStudents;
622        }
623        public void addStudent(Student student) {
624            iAllStudents.add(student);
625        }
626        public void removeStudent(Student student) {
627            iAllStudents.remove(student);
628        }
629        
630        /** Returns amount of allocated memory.
631         * @return amount of allocated memory to be written in the log
632         */
633        public static synchronized String getMem() {
634            Runtime rt = Runtime.getRuntime();
635            return sDoubleFormat.format(((double)(rt.totalMemory()-rt.freeMemory()))/1048576)+"M";
636        }
637    }