001    package net.sf.cpsolver.coursett.model;
002    
003    import java.util.ArrayList;
004    import java.util.BitSet;
005    import java.util.Collection;
006    import java.util.HashSet;
007    import java.util.HashMap;
008    import java.util.List;
009    import java.util.Locale;
010    import java.util.Map;
011    import java.util.Set;
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.FlexibleConstraint;
020    import net.sf.cpsolver.coursett.constraint.RoomConstraint;
021    import net.sf.cpsolver.coursett.constraint.SpreadConstraint;
022    import net.sf.cpsolver.coursett.criteria.BackToBackInstructorPreferences;
023    import net.sf.cpsolver.coursett.criteria.BrokenTimePatterns;
024    import net.sf.cpsolver.coursett.criteria.DepartmentBalancingPenalty;
025    import net.sf.cpsolver.coursett.criteria.DistributionPreferences;
026    import net.sf.cpsolver.coursett.criteria.FlexibleConstraintCriterion;
027    import net.sf.cpsolver.coursett.criteria.Perturbations;
028    import net.sf.cpsolver.coursett.criteria.RoomPreferences;
029    import net.sf.cpsolver.coursett.criteria.RoomViolations;
030    import net.sf.cpsolver.coursett.criteria.SameSubpartBalancingPenalty;
031    import net.sf.cpsolver.coursett.criteria.StudentCommittedConflict;
032    import net.sf.cpsolver.coursett.criteria.StudentConflict;
033    import net.sf.cpsolver.coursett.criteria.StudentDistanceConflict;
034    import net.sf.cpsolver.coursett.criteria.StudentHardConflict;
035    import net.sf.cpsolver.coursett.criteria.StudentOverlapConflict;
036    import net.sf.cpsolver.coursett.criteria.TimePreferences;
037    import net.sf.cpsolver.coursett.criteria.TimeViolations;
038    import net.sf.cpsolver.coursett.criteria.TooBigRooms;
039    import net.sf.cpsolver.coursett.criteria.UselessHalfHours;
040    import net.sf.cpsolver.coursett.criteria.placement.AssignmentCount;
041    import net.sf.cpsolver.coursett.criteria.placement.DeltaTimePreference;
042    import net.sf.cpsolver.coursett.criteria.placement.HardConflicts;
043    import net.sf.cpsolver.coursett.criteria.placement.PotentialHardConflicts;
044    import net.sf.cpsolver.coursett.criteria.placement.WeightedHardConflicts;
045    import net.sf.cpsolver.ifs.constant.ConstantModel;
046    import net.sf.cpsolver.ifs.criteria.Criterion;
047    import net.sf.cpsolver.ifs.model.Constraint;
048    import net.sf.cpsolver.ifs.model.GlobalConstraint;
049    import net.sf.cpsolver.ifs.model.WeakeningConstraint;
050    import net.sf.cpsolver.ifs.util.DataProperties;
051    import net.sf.cpsolver.ifs.util.DistanceMetric;
052    
053    /**
054     * Timetable model.
055     * 
056     * @version CourseTT 1.2 (University Course Timetabling)<br>
057     *          Copyright (C) 2006 - 2010 Tomas Muller<br>
058     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
059     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
060     * <br>
061     *          This library is free software; you can redistribute it and/or modify
062     *          it under the terms of the GNU Lesser General Public License as
063     *          published by the Free Software Foundation; either version 3 of the
064     *          License, or (at your option) any later version. <br>
065     * <br>
066     *          This library is distributed in the hope that it will be useful, but
067     *          WITHOUT ANY WARRANTY; without even the implied warranty of
068     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
069     *          Lesser General Public License for more details. <br>
070     * <br>
071     *          You should have received a copy of the GNU Lesser General Public
072     *          License along with this library; if not see
073     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
074     */
075    
076    public class TimetableModel extends ConstantModel<Lecture, Placement> {
077        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(TimetableModel.class);
078        private static java.text.DecimalFormat sDoubleFormat = new java.text.DecimalFormat("0.00",
079                new java.text.DecimalFormatSymbols(Locale.US));
080    
081        private List<InstructorConstraint> iInstructorConstraints = new ArrayList<InstructorConstraint>();
082        private List<JenrlConstraint> iJenrlConstraints = new ArrayList<JenrlConstraint>();
083        private List<RoomConstraint> iRoomConstraints = new ArrayList<RoomConstraint>();
084        private List<DepartmentSpreadConstraint> iDepartmentSpreadConstraints = new ArrayList<DepartmentSpreadConstraint>();
085        private List<SpreadConstraint> iSpreadConstraints = new ArrayList<SpreadConstraint>();
086        private List<GroupConstraint> iGroupConstraints = new ArrayList<GroupConstraint>();
087        private List<ClassLimitConstraint> iClassLimitConstraints = new ArrayList<ClassLimitConstraint>();
088        private List<FlexibleConstraint> iFlexibleConstraints = new ArrayList<FlexibleConstraint>();
089        private DataProperties iProperties = null;
090        private int iYear = -1;
091        private List<BitSet> iWeeks = null;
092    
093        private HashSet<Student> iAllStudents = new HashSet<Student>();
094        
095        private DistanceMetric iDistanceMetric = null;
096        
097        private StudentSectioning iStudentSectioning = null;
098    
099        public TimetableModel(DataProperties properties) {
100            super();
101            iProperties = properties;
102            iDistanceMetric = new DistanceMetric(properties);
103            if (properties.getPropertyBoolean("OnFlySectioning.Enabled", false))
104                addModelListener(new OnFlySectioning(this));
105            String criteria = properties.getProperty("General.Criteria",
106                    // Objectives
107                    StudentConflict.class.getName() + ";" +
108                    StudentDistanceConflict.class.getName() + ";" +
109                    StudentHardConflict.class.getName() + ";" +
110                    StudentCommittedConflict.class.getName() + ";" +
111                    StudentOverlapConflict.class.getName() + ";" +
112                    UselessHalfHours.class.getName() + ";" +
113                    BrokenTimePatterns.class.getName() + ";" +
114                    TooBigRooms.class.getName() + ";" +
115                    TimePreferences.class.getName() + ";" +
116                    RoomPreferences.class.getName() + ";" +
117                    DistributionPreferences.class.getName() + ";" +
118                    SameSubpartBalancingPenalty.class.getName() + ";" +
119                    DepartmentBalancingPenalty.class.getName() + ";" +
120                    BackToBackInstructorPreferences.class.getName() + ";" +
121                    Perturbations.class.getName() + ";" +
122                    // Additional placement selection criteria
123                    AssignmentCount.class.getName() + ";" +
124                    DeltaTimePreference.class.getName() + ";" +
125                    HardConflicts.class.getName() + ";" +
126                    PotentialHardConflicts.class.getName() + ";" +
127                    FlexibleConstraintCriterion.class.getName() + ";" +
128                    WeightedHardConflicts.class.getName());                
129            // Interactive mode -- count time / room violations
130            if (properties.getPropertyBoolean("General.InteractiveMode", false))
131                criteria += ";" + TimeViolations.class.getName() + ";" + RoomViolations.class.getName();
132            // Additional (custom) criteria
133            criteria += ";" + properties.getProperty("General.AdditionalCriteria", "");
134            for (String criterion: criteria.split("\\;")) {
135                if (criterion == null || criterion.isEmpty()) continue;
136                try {
137                    @SuppressWarnings("unchecked")
138                    Class<Criterion<Lecture, Placement>> clazz = (Class<Criterion<Lecture, Placement>>)Class.forName(criterion);
139                    addCriterion(clazz.newInstance());
140                } catch (Exception e) {
141                    sLogger.error("Unable to use " + criterion + ": " + e.getMessage());
142                }
143            }
144            try {
145                String studentSectioningClassName = properties.getProperty("StudentSectioning.Class", DefaultStudentSectioning.class.getName());
146                Class<?> studentSectioningClass = Class.forName(studentSectioningClassName);
147                iStudentSectioning = (StudentSectioning)studentSectioningClass.getConstructor(TimetableModel.class).newInstance(this);
148            } catch (Exception e) {
149                sLogger.error("Failed to load custom student sectioning class: " + e.getMessage());
150                iStudentSectioning = new DefaultStudentSectioning(this);
151            }
152        }
153    
154        public DistanceMetric getDistanceMetric() {
155            return iDistanceMetric;
156        }
157        
158        /**
159         * Returns interface to the student sectioning functions needed during course timetabling.
160         * Defaults to an instance of {@link DefaultStudentSectioning}, can be changed using the StudentSectioning.Class parameter.
161         */
162        public StudentSectioning getStudentSectioning() {
163            return iStudentSectioning;
164        }
165    
166        public DataProperties getProperties() {
167            return iProperties;
168        }
169    
170        /**
171         * Student final sectioning (switching students between sections of the same
172         * class in order to minimize overall number of student conflicts)
173         */
174        public void switchStudents() {
175            getStudentSectioning().switchStudents(this);
176        }
177    
178        @Override
179        public String toString() {
180            return "TimetableModel{" + "\n  super=" + super.toString()
181                    + "\n  studentConflicts=" + sDoubleFormat.format(getCriterion(StudentConflict.class).getValue())
182                    + "\n  roomPreferences=" + sDoubleFormat.format(getCriterion(RoomPreferences.class).getValue())
183                    + "\n  timePreferences=" + sDoubleFormat.format(getCriterion(TimePreferences.class).getValue())
184                    + "\n  groupConstraintPreferences=" + sDoubleFormat.format(getCriterion(DistributionPreferences.class).getValue())
185                    + "\n}";
186        }
187    
188        public Map<String, String> getBounds() {
189            Map<String, String> ret = new HashMap<String, String>();
190            ret.put("Room preferences min", "" + getCriterion(RoomPreferences.class).getBounds()[0]);
191            ret.put("Room preferences max", "" + getCriterion(RoomPreferences.class).getBounds()[1]);
192            ret.put("Time preferences min", "" + getCriterion(TimePreferences.class).getBounds()[0]);
193            ret.put("Time preferences max", "" + getCriterion(TimePreferences.class).getBounds()[1]);
194            ret.put("Distribution preferences min", "" + getCriterion(DistributionPreferences.class).getBounds()[0]);
195            ret.put("Distribution preferences max", "" + getCriterion(DistributionPreferences.class).getBounds()[1]);
196            if (getProperties().getPropertyBoolean("General.UseDistanceConstraints", false)) {
197                ret.put("Back-to-back instructor preferences max", "" + getCriterion(BackToBackInstructorPreferences.class).getBounds()[1]);
198            }
199            ret.put("Too big rooms max", "" + getCriterion(TooBigRooms.class).getBounds()[0]);
200            ret.put("Useless half-hours", "" + getCriterion(UselessHalfHours.class).getBounds()[0]);
201            return ret;
202        }
203    
204        /** Global info */
205        @Override
206        public Map<String, String> getInfo() {
207            Map<String, String> ret = super.getInfo();
208            ret.put("Memory usage", getMem());
209            
210            Criterion<Lecture, Placement> rp = getCriterion(RoomPreferences.class);
211            Criterion<Lecture, Placement> rv = getCriterion(RoomViolations.class);
212            ret.put("Room preferences", getPerc(rp.getValue(), rp.getBounds()[0], rp.getBounds()[1]) + "% (" + Math.round(rp.getValue()) + ")"
213                    + (rv != null && rv.getValue() >= 0.5 ? " [hard:" + Math.round(rv.getValue()) + "]" : ""));
214            
215            Criterion<Lecture, Placement> tp = getCriterion(TimePreferences.class);
216            Criterion<Lecture, Placement> tv = getCriterion(TimeViolations.class);
217            ret.put("Time preferences", getPerc(tp.getValue(), tp.getBounds()[0], tp.getBounds()[1]) + "% (" + sDoubleFormat.format(tp.getValue()) + ")"
218                    + (tv != null && tv.getValue() >= 0.5 ? " [hard:" + Math.round(tv.getValue()) + "]" : ""));
219    
220            Criterion<Lecture, Placement> dp = getCriterion(DistributionPreferences.class);
221            ret.put("Distribution preferences", getPerc(dp.getValue(), dp.getBounds()[0], dp.getBounds()[1]) + "% (" + sDoubleFormat.format(dp.getValue()) + ")");
222            
223            Criterion<Lecture, Placement> sc = getCriterion(StudentConflict.class);
224            Criterion<Lecture, Placement> shc = getCriterion(StudentHardConflict.class);
225            Criterion<Lecture, Placement> sdc = getCriterion(StudentDistanceConflict.class);
226            Criterion<Lecture, Placement> scc = getCriterion(StudentCommittedConflict.class);
227            ret.put("Student conflicts", Math.round(scc.getValue() + sc.getValue()) +
228                    " [committed:" + Math.round(scc.getValue()) +
229                    ", distance:" + Math.round(sdc.getValue()) +
230                    ", hard:" + Math.round(shc.getValue()) + "]");
231            
232            if (!getSpreadConstraints().isEmpty()) {
233                Criterion<Lecture, Placement> ip = getCriterion(BackToBackInstructorPreferences.class);
234                ret.put("Back-to-back instructor preferences", getPerc(ip.getValue(), ip.getBounds()[0], ip.getBounds()[1]) + "% (" + Math.round(ip.getValue()) + ")");
235            }
236    
237            if (!getDepartmentSpreadConstraints().isEmpty()) {
238                Criterion<Lecture, Placement> dbp = getCriterion(DepartmentBalancingPenalty.class);
239                ret.put("Department balancing penalty", sDoubleFormat.format(dbp.getValue()));
240            }
241            
242            Criterion<Lecture, Placement> sbp = getCriterion(SameSubpartBalancingPenalty.class);
243            ret.put("Same subpart balancing penalty", sDoubleFormat.format(sbp.getValue()));
244            
245            Criterion<Lecture, Placement> tbr = getCriterion(TooBigRooms.class);
246            ret.put("Too big rooms", getPercRev(tbr.getValue(), tbr.getBounds()[1], tbr.getBounds()[0]) + "% (" + Math.round(tbr.getValue()) + ")");
247            
248            Criterion<Lecture, Placement> uh = getCriterion(UselessHalfHours.class);
249            Criterion<Lecture, Placement> bt = getCriterion(BrokenTimePatterns.class);
250    
251            ret.put("Useless half-hours", getPercRev(uh.getValue() + bt.getValue(), 0, Constants.sPreferenceLevelStronglyDiscouraged * bt.getBounds()[0]) +
252                    "% (" + Math.round(uh.getValue()) + " + " + Math.round(bt.getValue()) + ")");
253            return ret;
254        }
255    
256        @Override
257        public Map<String, String> getInfo(Collection<Lecture> variables) {
258            Map<String, String> ret = super.getInfo(variables);
259            
260            ret.put("Memory usage", getMem());
261            
262            Criterion<Lecture, Placement> rp = getCriterion(RoomPreferences.class);
263            ret.put("Room preferences", getPerc(rp.getValue(variables), rp.getBounds(variables)[0], rp.getBounds(variables)[1]) + "% (" + Math.round(rp.getValue(variables)) + ")");
264            
265            Criterion<Lecture, Placement> tp = getCriterion(TimePreferences.class);
266            ret.put("Time preferences", getPerc(tp.getValue(variables), tp.getBounds(variables)[0], tp.getBounds(variables)[1]) + "% (" + sDoubleFormat.format(tp.getValue(variables)) + ")"); 
267    
268            Criterion<Lecture, Placement> dp = getCriterion(DistributionPreferences.class);
269            ret.put("Distribution preferences", getPerc(dp.getValue(variables), dp.getBounds(variables)[0], dp.getBounds(variables)[1]) + "% (" + sDoubleFormat.format(dp.getValue(variables)) + ")");
270            
271            Criterion<Lecture, Placement> sc = getCriterion(StudentConflict.class);
272            Criterion<Lecture, Placement> shc = getCriterion(StudentHardConflict.class);
273            Criterion<Lecture, Placement> sdc = getCriterion(StudentDistanceConflict.class);
274            Criterion<Lecture, Placement> scc = getCriterion(StudentCommittedConflict.class);
275            ret.put("Student conflicts", Math.round(scc.getValue(variables) + sc.getValue(variables)) +
276                    " [committed:" + Math.round(scc.getValue(variables)) +
277                    ", distance:" + Math.round(sdc.getValue(variables)) +
278                    ", hard:" + Math.round(shc.getValue(variables)) + "]");
279            
280            if (!getSpreadConstraints().isEmpty()) {
281                Criterion<Lecture, Placement> ip = getCriterion(BackToBackInstructorPreferences.class);
282                ret.put("Back-to-back instructor preferences", getPerc(ip.getValue(variables), ip.getBounds(variables)[0], ip.getBounds(variables)[1]) + "% (" + Math.round(ip.getValue(variables)) + ")");
283            }
284    
285            if (!getDepartmentSpreadConstraints().isEmpty()) {
286                Criterion<Lecture, Placement> dbp = getCriterion(DepartmentBalancingPenalty.class);
287                ret.put("Department balancing penalty", sDoubleFormat.format(dbp.getValue(variables)));
288            }
289            
290            Criterion<Lecture, Placement> sbp = getCriterion(SameSubpartBalancingPenalty.class);
291            ret.put("Same subpart balancing penalty", sDoubleFormat.format(sbp.getValue(variables)));
292            
293            Criterion<Lecture, Placement> tbr = getCriterion(TooBigRooms.class);
294            ret.put("Too big rooms", getPercRev(tbr.getValue(variables), tbr.getBounds(variables)[1], tbr.getBounds(variables)[0]) + "% (" + Math.round(tbr.getValue(variables)) + ")");
295            
296            Criterion<Lecture, Placement> uh = getCriterion(UselessHalfHours.class);
297            Criterion<Lecture, Placement> bt = getCriterion(BrokenTimePatterns.class);
298    
299            ret.put("Useless half-hours", getPercRev(uh.getValue(variables) + bt.getValue(variables), 0, Constants.sPreferenceLevelStronglyDiscouraged * bt.getBounds(variables)[0]) +
300                    "% (" + Math.round(uh.getValue(variables)) + " + " + Math.round(bt.getValue(variables)) + ")");
301            return ret;
302        }
303    
304        @Override
305        public void addConstraint(Constraint<Lecture, Placement> constraint) {
306            super.addConstraint(constraint);
307            if (constraint instanceof InstructorConstraint) {
308                iInstructorConstraints.add((InstructorConstraint) constraint);
309            } else if (constraint instanceof JenrlConstraint) {
310                iJenrlConstraints.add((JenrlConstraint) constraint);
311            } else if (constraint instanceof RoomConstraint) {
312                iRoomConstraints.add((RoomConstraint) constraint);
313            } else if (constraint instanceof DepartmentSpreadConstraint) {
314                iDepartmentSpreadConstraints.add((DepartmentSpreadConstraint) constraint);
315            } else if (constraint instanceof SpreadConstraint) {
316                iSpreadConstraints.add((SpreadConstraint) constraint);
317            } else if (constraint instanceof ClassLimitConstraint) {
318                iClassLimitConstraints.add((ClassLimitConstraint) constraint);
319            } else if (constraint instanceof GroupConstraint) {
320                iGroupConstraints.add((GroupConstraint) constraint);
321            } else if (constraint instanceof FlexibleConstraint) {
322                iFlexibleConstraints.add((FlexibleConstraint) constraint);
323            }
324        }
325    
326        @Override
327        public void removeConstraint(Constraint<Lecture, Placement> constraint) {
328            super.removeConstraint(constraint);
329            if (constraint instanceof InstructorConstraint) {
330                iInstructorConstraints.remove(constraint);
331            } else if (constraint instanceof JenrlConstraint) {
332                iJenrlConstraints.remove(constraint);
333            } else if (constraint instanceof RoomConstraint) {
334                iRoomConstraints.remove(constraint);
335            } else if (constraint instanceof DepartmentSpreadConstraint) {
336                iDepartmentSpreadConstraints.remove(constraint);
337            } else if (constraint instanceof SpreadConstraint) {
338                iSpreadConstraints.remove(constraint);
339            } else if (constraint instanceof ClassLimitConstraint) {
340                iClassLimitConstraints.remove(constraint);
341            } else if (constraint instanceof GroupConstraint) {
342                iGroupConstraints.remove(constraint);
343            } else if (constraint instanceof FlexibleConstraint) {
344                iFlexibleConstraints.remove(constraint);
345            }
346        }
347    
348        /** The list of all instructor constraints */
349        public List<InstructorConstraint> getInstructorConstraints() {
350            return iInstructorConstraints;
351        }
352    
353        /** The list of all group constraints */
354        public List<GroupConstraint> getGroupConstraints() {
355            return iGroupConstraints;
356        }
357    
358        /** The list of all jenrl constraints */
359        public List<JenrlConstraint> getJenrlConstraints() {
360            return iJenrlConstraints;
361        }
362    
363        /** The list of all room constraints */
364        public List<RoomConstraint> getRoomConstraints() {
365            return iRoomConstraints;
366        }
367    
368        /** The list of all departmental spread constraints */
369        public List<DepartmentSpreadConstraint> getDepartmentSpreadConstraints() {
370            return iDepartmentSpreadConstraints;
371        }
372    
373        public List<SpreadConstraint> getSpreadConstraints() {
374            return iSpreadConstraints;
375        }
376    
377        public List<ClassLimitConstraint> getClassLimitConstraints() {
378            return iClassLimitConstraints;
379        }
380        
381        public List<FlexibleConstraint> getFlexibleConstraints() {
382            return iFlexibleConstraints;
383        }
384        
385        @Override
386        public double getTotalValue() {
387            double ret = 0;
388            for (Criterion<Lecture, Placement> criterion: getCriteria())
389                ret += criterion.getWeightedValue();
390            return ret;
391        }
392    
393        @Override
394        public double getTotalValue(Collection<Lecture> variables) {
395            double ret = 0;
396            for (Criterion<Lecture, Placement> criterion: getCriteria())
397                ret += criterion.getWeightedValue(variables);
398            return ret;
399        }
400    
401        public int getYear() {
402            return iYear;
403        }
404    
405        public void setYear(int year) {
406            iYear = year;
407        }
408    
409        public Set<Student> getAllStudents() {
410            return iAllStudents;
411        }
412    
413        public void addStudent(Student student) {
414            iAllStudents.add(student);
415        }
416    
417        public void removeStudent(Student student) {
418            iAllStudents.remove(student);
419        }
420    
421        /**
422         * Returns amount of allocated memory.
423         * 
424         * @return amount of allocated memory to be written in the log
425         */
426        public static synchronized String getMem() {
427            Runtime rt = Runtime.getRuntime();
428            return sDoubleFormat.format(((double) (rt.totalMemory() - rt.freeMemory())) / 1048576) + "M";
429        }
430        
431        
432        /**
433         * Returns the set of conflicting variables with this value, if it is
434         * assigned to its variable. Conflicts with constraints that implement
435         * {@link WeakeningConstraint} are ignored.
436         */
437        public Set<Placement> conflictValuesSkipWeakeningConstraints(Placement value) {
438            Set<Placement> conflictValues = new HashSet<Placement>();
439            for (Constraint<Lecture, Placement> constraint : value.variable().hardConstraints()) {
440                if (constraint instanceof WeakeningConstraint) continue;
441                constraint.computeConflicts(value, conflictValues);
442            }
443            for (GlobalConstraint<Lecture, Placement> constraint : globalConstraints()) {
444                if (constraint instanceof WeakeningConstraint) continue;
445                constraint.computeConflicts(value, conflictValues);
446            }
447            return conflictValues;
448        }
449        
450        /**
451         * The method creates date patterns (bitsets) which represent the weeks of a
452         * semester.
453         *      
454         * @return a list of BitSets which represents the weeks of a semester.
455         */
456        public List<BitSet> getWeeks() {
457            if (iWeeks == null) {
458                String defaultDatePattern = getProperties().getProperty("DatePattern.CustomDatePattern", null);
459                if (defaultDatePattern == null){                
460                    defaultDatePattern = getProperties().getProperty("DatePattern.Default");
461                }
462                if (defaultDatePattern == null) return null;
463                
464                // Create default date pattern
465                BitSet fullTerm = new BitSet(defaultDatePattern.length());
466                for (int i = 0; i < defaultDatePattern.length(); i++) {
467                    if (defaultDatePattern.charAt(i) == 49) {
468                        fullTerm.set(i);
469                    }
470                }
471                
472                // Cut date pattern into weeks (every week contains 7 positive bits)
473                iWeeks = new ArrayList<BitSet>();
474                int cnt = 0;
475                for (int i = 0; i < fullTerm.length(); i++) {
476                    if (fullTerm.get(i)) {
477                        int w = (cnt++) / 7;
478                        if (iWeeks.size() == w) {
479                            iWeeks.add(new BitSet(fullTerm.length()));
480                        }
481                        iWeeks.get(w).set(i);
482                    }
483                }
484            }
485            return iWeeks;            
486        }
487    }