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