    001package org.cpsolver.instructor.model;
    002
    003import java.text.DecimalFormat;
    004import java.util.ArrayList;
    005import java.util.BitSet;
    006import java.util.Collection;
    007import java.util.Date;
    008import java.util.HashMap;
    009import java.util.HashSet;
    010import java.util.Iterator;
    011import java.util.List;
    012import java.util.Map;
    013import java.util.Set;
    014import java.util.TreeSet;
    015
    016import org.apache.logging.log4j.Logger;
    017import org.cpsolver.coursett.Constants;
    018import org.cpsolver.coursett.model.TimeLocation;
    019import org.cpsolver.ifs.assignment.Assignment;
    020import org.cpsolver.ifs.criteria.Criterion;
    021import org.cpsolver.ifs.model.Constraint;
    022import org.cpsolver.ifs.model.Model;
    023import org.cpsolver.ifs.util.DataProperties;
    024import org.cpsolver.ifs.util.ToolBox;
    025import org.cpsolver.instructor.constraints.GroupConstraint;
    026import org.cpsolver.instructor.constraints.GroupConstraint.Distribution;
    027import org.cpsolver.instructor.constraints.InstructorConstraint;
    028import org.cpsolver.instructor.constraints.SameInstructorConstraint;
    029import org.cpsolver.instructor.constraints.SameLinkConstraint;
    030import org.cpsolver.instructor.criteria.UnusedInstructorLoad;
    031import org.cpsolver.instructor.criteria.AttributePreferences;
    032import org.cpsolver.instructor.criteria.BackToBack;
    033import org.cpsolver.instructor.criteria.CoursePreferences;
    034import org.cpsolver.instructor.criteria.InstructorPreferences;
    035import org.cpsolver.instructor.criteria.SameInstructor;
    036import org.cpsolver.instructor.criteria.DifferentLecture;
    037import org.cpsolver.instructor.criteria.Distributions;
    038import org.cpsolver.instructor.criteria.OriginalInstructor;
    039import org.cpsolver.instructor.criteria.SameCommon;
    040import org.cpsolver.instructor.criteria.SameCourse;
    041import org.cpsolver.instructor.criteria.SameDays;
    042import org.cpsolver.instructor.criteria.SameLink;
    043import org.cpsolver.instructor.criteria.SameRoom;
    044import org.cpsolver.instructor.criteria.TeachingPreferences;
    045import org.cpsolver.instructor.criteria.TimeOverlaps;
    046import org.cpsolver.instructor.criteria.TimePreferences;
    047import org.dom4j.Document;
    048import org.dom4j.DocumentHelper;
    049import org.dom4j.Element;
    050
    051/**
    052 * Instructor Scheduling Model. Variables are {@link org.cpsolver.instructor.model.TeachingRequest}, values are {@link org.cpsolver.instructor.model.TeachingAssignment}.
    053 * Each teaching request has a course (see {@link org.cpsolver.instructor.model.Course}) and one or more sections (see {link {@link org.cpsolver.instructor.model.Section}}).
    054 * Each assignment assigns one instructor (see {@link org.cpsolver.instructor.model.Instructor}) to a single teaching request.
    055 *
    056 * @author  Tomáš Müller
    057 * @version IFS 1.3 (Instructor Sectioning)<br>
    058 *          Copyright (C) 2016 Tomáš Müller<br>
    059 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    060 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    061 * <br>
    062 *          This library is free software; you can redistribute it and/or modify
    063 *          it under the terms of the GNU Lesser General Public License as
    064 *          published by the Free Software Foundation; either version 3 of the
    065 *          License, or (at your option) any later version. <br>
    066 * <br>
    067 *          This library is distributed in the hope that it will be useful, but
    068 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    069 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    070 *          Lesser General Public License for more details. <br>
    071 * <br>
    072 *          You should have received a copy of the GNU Lesser General Public
    073 *          License along with this library; if not see
    074 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
    075 */
    076public class InstructorSchedulingModel extends Model<TeachingRequest.Variable, TeachingAssignment> {
    077    private static Logger sLog = org.apache.logging.log4j.LogManager.getLogger(InstructorSchedulingModel.class);
    078    private DataProperties iProperties;
    079    private Set<Attribute.Type> iTypes = new HashSet<Attribute.Type>();
    080    private List<Instructor> iInstructors = new ArrayList<Instructor>();
    081    private List<TeachingRequest> iRequests = new ArrayList<TeachingRequest>();
    082
    083    /**
    084     * Constructor
    085     * @param properties data properties
    086     */
    087    public InstructorSchedulingModel(DataProperties properties) {
    088        super();
    089        iProperties = properties;
    090        addCriterion(new AttributePreferences());
    091        addCriterion(new InstructorPreferences());
    092        addCriterion(new TeachingPreferences());
    093        addCriterion(new TimePreferences());
    094        addCriterion(new CoursePreferences());
    095        addCriterion(new BackToBack());
    096        addCriterion(new SameInstructor());
    097        addCriterion(new TimeOverlaps());
    098        addCriterion(new DifferentLecture());
    099        addCriterion(new SameLink());
    100        addCriterion(new OriginalInstructor());
    101        addCriterion(new UnusedInstructorLoad());
    102        addCriterion(new SameCourse());
    103        addCriterion(new SameCommon());
    104        addCriterion(new SameDays());
    105        addCriterion(new SameRoom());
    106        addCriterion(new Distributions());
    107        addGlobalConstraint(new InstructorConstraint());
    108        addGlobalConstraint(new GroupConstraint());
    109    }
    110
    111    /**
    112     * Return solver configuration
    113     * @return data properties given in the constructor
    114     */
    115    public DataProperties getProperties() {
    116        return iProperties;
    117    }
    118
    119    /**
    120     * Add instructor
    121     * @param instructor an instructor
    122     */
    123    public void addInstructor(Instructor instructor) {
    124        instructor.setModel(this);
    125        iInstructors.add(instructor);
    126        for (Attribute attribute: instructor.getAttributes())
    127            addAttributeType(attribute.getType());
    128    }
    129
    130    /**
    131     * All instructors
    132     * @return all instructors in the model
    133     */
    134    public List<Instructor> getInstructors() {
    135        return iInstructors;
    136    }
    137
    138    /**
    139     * Add teaching request and the related variables
    140     * @param request teaching request
    141     */
    142    public void addRequest(TeachingRequest request) {
    143        iRequests.add(request);
    144        for (TeachingRequest.Variable variable: request.getVariables())
    145            addVariable(variable);
    146    }
    147
    148    /**
    149     * All teaching requests
    150     * @return all teaching requests in the model
    151     */
    152    public List<TeachingRequest> getRequests() {
    153        return iRequests;
    154    }
    155
    156
    157    /**
    158     * Return registered attribute types
    159     * @return attribute types in the problem
    160     */
    161    public Set<Attribute.Type> getAttributeTypes() { return iTypes; }
    162
    163    /**
    164     * Register an attribute type
    165     * @param type attribute type
    166     */
    167    public void addAttributeType(Attribute.Type type) { iTypes.add(type); }
    168
    169    @Override
    170    public Map<String, String> getInfo(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) {
    171        Map<String, String> info = super.getInfo(assignment);
    172
    173        double totalLoad = 0.0;
    174        double assignedLoad = 0.0;
    175        for (TeachingRequest.Variable clazz : variables()) {
    176            totalLoad += clazz.getRequest().getLoad();
    177            if (assignment.getValue(clazz) != null)
    178                assignedLoad += clazz.getRequest().getLoad();
    179        }
    180        info.put("Assigned Load", getPerc(assignedLoad, totalLoad, 0) + "% (" + sDoubleFormat.format(assignedLoad) + " / " + sDoubleFormat.format(totalLoad) + ")");
    181
    182        return info;
    183    }
    184
    185    @Override
    186    public double getTotalValue(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) {
    187        double ret = 0;
    188        for (Criterion<TeachingRequest.Variable, TeachingAssignment> criterion : getCriteria())
    189            ret += criterion.getWeightedValue(assignment);
    190        return ret;
    191    }
    192
    193    @Override
    194    public double getTotalValue(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment, Collection<TeachingRequest.Variable> variables) {
    195        double ret = 0;
    196        for (Criterion<TeachingRequest.Variable, TeachingAssignment> criterion : getCriteria())
    197            ret += criterion.getWeightedValue(assignment, variables);
    198        return ret;
    199    }
    200
    201    /**
    202     * Store the problem (together with its solution) in an XML format
    203     * @param assignment current assignment
    204     * @return XML document with the problem
    205     */
    206    public Document save(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) {
    207        DecimalFormat sDF7 = new DecimalFormat("0000000");
    208        boolean saveInitial = getProperties().getPropertyBoolean("Xml.SaveInitial", false);
    209        boolean saveBest = getProperties().getPropertyBoolean("Xml.SaveBest", false);
    210        boolean saveSolution = getProperties().getPropertyBoolean("Xml.SaveSolution", true);
    211        Document document = DocumentHelper.createDocument();
    212        if (assignment != null && assignment.nrAssignedVariables() > 0) {
    213            StringBuffer comments = new StringBuffer("Solution Info:\n");
    214            Map<String, String> solutionInfo = (getProperties().getPropertyBoolean("Xml.ExtendedInfo", true) ? getExtendedInfo(assignment) : getInfo(assignment));
    215            for (String key : new TreeSet<String>(solutionInfo.keySet())) {
    216                String value = solutionInfo.get(key);
    217                comments.append("    " + key + ": " + value + "\n");
    218            }
    219            document.addComment(comments.toString());
    220        }
    221        Element root = document.addElement("instructor-schedule");
    222        root.addAttribute("version", "1.0");
    223        root.addAttribute("created", String.valueOf(new Date()));
    224        Element typesEl = root.addElement("attributes");
    225        for (Attribute.Type type: getAttributeTypes()) {
    226            Element typeEl = typesEl.addElement("type");
    227            if (type.getTypeId() != null)
    228                typeEl.addAttribute("id", String.valueOf(type.getTypeId()));
    229            typeEl.addAttribute("name", type.getTypeName());
    230            typeEl.addAttribute("conjunctive", type.isConjunctive() ? "true" : "false");
    231            typeEl.addAttribute("required", type.isRequired() ? "true": "false");
    232            Set<Attribute> attributes = new HashSet<Attribute>();
    233            for (TeachingRequest request: getRequests()) {
    234                for (Preference<Attribute> pref: request.getAttributePreferences()) {
    235                    Attribute attribute = pref.getTarget();
    236                    if (type.equals(attribute.getType()) && attributes.add(attribute)) {
    237                        Element attributeEl = typeEl.addElement("attribute");
    238                        if (attribute.getAttributeId() != null)
    239                            attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId()));
    240                        attributeEl.addAttribute("name", attribute.getAttributeName());
    241                        if (attribute.getParentAttribute() != null && attribute.getParentAttribute().getAttributeId() != null)
    242                            attributeEl.addAttribute("parent", String.valueOf(attribute.getParentAttribute().getAttributeId()));
    243                    }
    244                }
    245                for (Instructor instructor: getInstructors()) {
    246                    for (Attribute attribute: instructor.getAttributes()) {
    247                        if (type.equals(attribute.getType()) && attributes.add(attribute)) {
    248                            Element attributeEl = typeEl.addElement("attribute");
    249                            if (attribute.getAttributeId() != null)
    250                                attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId()));
    251                            attributeEl.addAttribute("name", attribute.getAttributeName());
    252                            if (attribute.getParentAttribute() != null && attribute.getParentAttribute().getAttributeId() != null)
    253                                attributeEl.addAttribute("parent", String.valueOf(attribute.getParentAttribute().getAttributeId()));
    254                        }
    255                    }
    256                }
    257            }
    258        }
    259        Element requestsEl = root.addElement("teaching-requests");
    260        for (TeachingRequest request: getRequests()) {
    261            Element requestEl = requestsEl.addElement("request");
    262            requestEl.addAttribute("id", String.valueOf(request.getRequestId()));
    263            if (request.getNrInstructors() != 1)
    264                requestEl.addAttribute("nrInstructors", String.valueOf(request.getNrInstructors()));
    265            Course course = request.getCourse();
    266            Element courseEl = requestEl.addElement("course");
    267            if (course.getCourseId() != null)
    268                courseEl.addAttribute("id", String.valueOf(course.getCourseId()));
    269            if (course.getCourseName() != null)
    270                courseEl.addAttribute("name", String.valueOf(course.getCourseName()));
    271            for (Section section: request.getSections()) {
    272                Element sectionEl = requestEl.addElement("section");
    273                sectionEl.addAttribute("id", String.valueOf(section.getSectionId()));
    274                if (section.getExternalId() != null && !section.getExternalId().isEmpty()) sectionEl.addAttribute("externalId", section.getExternalId());
    275                if (section.getSectionType() != null && !section.getSectionType().isEmpty()) sectionEl.addAttribute("type", section.getSectionType());
    276                if (section.getSectionName() != null && !section.getSectionName().isEmpty()) sectionEl.addAttribute("name", section.getSectionName());
    277                if (section.hasTime()) {
    278                    TimeLocation tl = section.getTime();
    279                    Element timeEl = sectionEl.addElement("time");
    280                    timeEl.addAttribute("days", sDF7.format(Long.parseLong(Integer.toBinaryString(tl.getDayCode()))));
    281                    timeEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
    282                    timeEl.addAttribute("length", String.valueOf(tl.getLength()));
    283                    if (tl.getBreakTime() != 0)
    284                        timeEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime()));
    285                    if (tl.getTimePatternId() != null)
    286                        timeEl.addAttribute("pattern", tl.getTimePatternId().toString());
    287                    if (tl.getDatePatternId() != null)
    288                        timeEl.addAttribute("datePattern", tl.getDatePatternId().toString());
    289                    if (tl.getDatePatternName() != null && !tl.getDatePatternName().isEmpty())
    290                        timeEl.addAttribute("datePatternName", tl.getDatePatternName());
    291                    if (tl.getWeekCode() != null)
    292                        timeEl.addAttribute("dates", bitset2string(tl.getWeekCode()));
    293                    timeEl.setText(tl.getLongName(false));
    294                }
    295                if (section.hasRoom()) sectionEl.addAttribute("room", section.getRoom());
    296                if (section.isAllowOverlap()) sectionEl.addAttribute("canOverlap", "true");
    297                if (section.isCommon()) sectionEl.addAttribute("common", "true");
    298            }
    299            requestEl.addAttribute("load", sDoubleFormat.format(request.getLoad()));
    300            requestEl.addAttribute("sameCourse", Constants.preferenceLevel2preference(request.getSameCoursePreference()));
    301            requestEl.addAttribute("sameCommon", Constants.preferenceLevel2preference(request.getSameCommonPreference()));
    302            for (Preference<Attribute> pref: request.getAttributePreferences()) {
    303                Element attributeEl = requestEl.addElement("attribute");
    304                if (pref.getTarget().getAttributeId() != null)
    305                    attributeEl.addAttribute("id", String.valueOf(pref.getTarget().getAttributeId()));
    306                attributeEl.addAttribute("name", pref.getTarget().getAttributeName());
    307                attributeEl.addAttribute("type", pref.getTarget().getType().getTypeName());
    308                attributeEl.addAttribute("preference", (pref.isRequired() ? "R" : pref.isProhibited() ? "P" : String.valueOf(pref.getPreference())));
    309                if (pref.getTarget().getParentAttribute() != null && pref.getTarget().getParentAttribute().getAttributeId() != null)
    310                    attributeEl.addAttribute("parent", String.valueOf(pref.getTarget().getParentAttribute().getAttributeId()));
    311            }
    312            for (Preference<Instructor> pref: request.getInstructorPreferences()) {
    313                Element instructorEl = requestEl.addElement("instructor");
    314                instructorEl.addAttribute("id", String.valueOf(pref.getTarget().getInstructorId()));
    315                if (pref.getTarget().hasExternalId())
    316                    instructorEl.addAttribute("externalId", pref.getTarget().getExternalId());
    317                if (pref.getTarget().hasName())
    318                    instructorEl.addAttribute("name", pref.getTarget().getName());
    319                instructorEl.addAttribute("preference", (pref.isRequired() ? "R" : pref.isProhibited() ? "P" : String.valueOf(pref.getPreference())));
    320            }
    321            if (saveBest)
    322                for (TeachingRequest.Variable variable: request.getVariables()) {
    323                    if (variable.getBestAssignment() != null) {
    324                        Instructor instructor = variable.getBestAssignment().getInstructor();
    325                        Element instructorEl = requestEl.addElement("best-instructor");
    326                        instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId()));
    327                        if (request.getNrInstructors() != 1)
    328                            instructorEl.addAttribute("index", String.valueOf(variable.getInstructorIndex()));
    329                        if (instructor.hasExternalId())
    330                            instructorEl.addAttribute("externalId", instructor.getExternalId());
    331                        if (instructor.hasName())
    332                            instructorEl.addAttribute("name", instructor.getName());
    333                    }
    334                }
    335            if (saveInitial)
    336                for (TeachingRequest.Variable variable: request.getVariables()) {
    337                    if (variable.getInitialAssignment() != null) {
    338                        Instructor instructor = variable.getInitialAssignment().getInstructor();
    339                        Element instructorEl = requestEl.addElement("initial-instructor");
    340                        instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId()));
    341                        if (request.getNrInstructors() != 1)
    342                            instructorEl.addAttribute("index", String.valueOf(variable.getInstructorIndex()));
    343                        if (instructor.hasExternalId())
    344                            instructorEl.addAttribute("externalId", instructor.getExternalId());
    345                        if (instructor.hasName())
    346                            instructorEl.addAttribute("name", instructor.getName());
    347                    }
    348                }
    349            if (saveSolution)
    350                for (TeachingRequest.Variable variable: request.getVariables()) {
    351                    TeachingAssignment ta = assignment.getValue(variable);
    352                    if (ta != null) {
    353                        Instructor instructor = ta.getInstructor();
    354                        Element instructorEl = requestEl.addElement("assigned-instructor");
    355                        instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId()));
    356                        if (request.getNrInstructors() != 1)
    357                            instructorEl.addAttribute("index", String.valueOf(variable.getInstructorIndex()));
    358                        if (instructor.hasExternalId())
    359                            instructorEl.addAttribute("externalId", instructor.getExternalId());
    360                        if (instructor.hasName())
    361                            instructorEl.addAttribute("name", instructor.getName());
    362                    }
    363                }
    364        }
    365        Element instructorsEl = root.addElement("instructors");
    366        for (Instructor instructor: getInstructors()) {
    367            Element instructorEl = instructorsEl.addElement("instructor");
    368            instructorEl.addAttribute("id", String.valueOf(instructor.getInstructorId()));
    369            if (instructor.hasExternalId())
    370                instructorEl.addAttribute("externalId", instructor.getExternalId());
    371            if (instructor.hasName())
    372                instructorEl.addAttribute("name", instructor.getName());
    373            if (instructor.getPreference() != 0)
    374                instructorEl.addAttribute("preference", String.valueOf(instructor.getPreference()));
    375            if (instructor.getBackToBackPreference() != 0)
    376                instructorEl.addAttribute("btb", String.valueOf(instructor.getBackToBackPreference()));
    377            if (instructor.getSameDaysPreference() != 0)
    378                instructorEl.addAttribute("same-days", String.valueOf(instructor.getSameDaysPreference()));
    379            if (instructor.getSameRoomPreference() != 0)
    380                instructorEl.addAttribute("same-room", String.valueOf(instructor.getSameRoomPreference()));
    381            for (Attribute attribute: instructor.getAttributes()) {
    382                Element attributeEl = instructorEl.addElement("attribute");
    383                if (attribute.getAttributeId() != null)
    384                    attributeEl.addAttribute("id", String.valueOf(attribute.getAttributeId()));
    385                attributeEl.addAttribute("name", attribute.getAttributeName());
    386                attributeEl.addAttribute("type", attribute.getType().getTypeName());
    387                if (attribute.getParentAttribute() != null && attribute.getParentAttribute().getAttributeId() != null)
    388                    attributeEl.addAttribute("parent", String.valueOf(attribute.getParentAttribute().getAttributeId()));
    389            }
    390            instructorEl.addAttribute("maxLoad", sDoubleFormat.format(instructor.getMaxLoad()));
    391            for (Distribution d: instructor.getDistributions()) {
    392                Element dEl = instructorEl.addElement("distribution");
    393                dEl.addAttribute("type", d.getType().reference());
    394                dEl.addAttribute("preference", d.getPreference());
    395                dEl.addAttribute("name", d.getType().getName());
    396            }
    397            for (Preference<TimeLocation> tp: instructor.getTimePreferences()) {
    398
    399                Element timeEl = instructorEl.addElement("time");
    400                TimeLocation tl = tp.getTarget();
    401                timeEl.addAttribute("days", sDF7.format(Long.parseLong(Integer.toBinaryString(tl.getDayCode()))));
    402                timeEl.addAttribute("start", String.valueOf(tl.getStartSlot()));
    403                timeEl.addAttribute("length", String.valueOf(tl.getLength()));
    404                if (tl.getBreakTime() != 0)
    405                    timeEl.addAttribute("breakTime", String.valueOf(tl.getBreakTime()));
    406                if (tl.getTimePatternId() != null)
    407                    timeEl.addAttribute("pattern", tl.getTimePatternId().toString());
    408                if (tl.getDatePatternId() != null)
    409                    timeEl.addAttribute("datePattern", tl.getDatePatternId().toString());
    410                if (tl.getDatePatternName() != null && !tl.getDatePatternName().isEmpty())
    411                    timeEl.addAttribute("datePatternName", tl.getDatePatternName());
    412                if (tl.getWeekCode() != null)
    413                    timeEl.addAttribute("dates", bitset2string(tl.getWeekCode()));
    414                timeEl.addAttribute("preference", tp.isProhibited() ? "P" : tp.isRequired() ? "R" : String.valueOf(tp.getPreference()));
    415                if (tp.getTarget() instanceof EnrolledClass) {
    416                    Element classEl = timeEl.addElement("section");
    417                    Element courseEl = null;
    418                    EnrolledClass ec = (EnrolledClass)tp.getTarget();
    419                    if (ec.getCourseId() != null || ec.getCourse() != null) {
    420                        courseEl = timeEl.addElement("course");
    421                        if (ec.getCourseId() != null) courseEl.addAttribute("id", String.valueOf(ec.getCourseId()));
    422                        if (ec.getCourse() != null) courseEl.addAttribute("name", ec.getCourse());
    423                    }
    424                    if (ec.getClassId() != null) classEl.addAttribute("id", String.valueOf(ec.getClassId()));
    425                    if (ec.getType() != null) classEl.addAttribute("type", ec.getType());
    426                    if (ec.getSection() != null) classEl.addAttribute("name", ec.getSection());
    427                    if (ec.getExternalId() != null) classEl.addAttribute("externalId", ec.getExternalId());
    428                    if (ec.getRoom() != null) classEl.addAttribute("room", ec.getRoom());
    429                    classEl.addAttribute("role", ec.isInstructor() ? "instructor" : "student");
    430                } else {
    431                    timeEl.setText(tl.getLongName(false));
    432                }
    433            }
    434            for (Preference<Course> cp: instructor.getCoursePreferences()) {
    435                Element courseEl = instructorEl.addElement("course");
    436                Course course = cp.getTarget();
    437                if (course.getCourseId() != null)
    438                    courseEl.addAttribute("id", String.valueOf(course.getCourseId()));
    439                if (course.getCourseName() != null)
    440                    courseEl.addAttribute("name", String.valueOf(course.getCourseName()));
    441                courseEl.addAttribute("preference", cp.isProhibited() ? "P" : cp.isRequired() ? "R" : String.valueOf(cp.getPreference()));
    442            }
    443        }
    444        Element constraintsEl = root.addElement("constraints");
    445        for (Constraint<TeachingRequest.Variable, TeachingAssignment> c: constraints()) {
    446            if (c instanceof SameInstructorConstraint) {
    447                SameInstructorConstraint si = (SameInstructorConstraint) c;
    448                Element sameInstEl = constraintsEl.addElement("same-instructor");
    449                if (si.getConstraintId() != null)
    450                    sameInstEl.addAttribute("id", String.valueOf(si.getConstraintId()));
    451                if (si.getName() != null)
    452                    sameInstEl.addAttribute("name", si.getName());
    453                sameInstEl.addAttribute("preference", Constants.preferenceLevel2preference(si.getPreference()));
    454                for (TeachingRequest.Variable request: c.variables()) {
    455                    Element assignmentEl = sameInstEl.addElement("request");
    456                    assignmentEl.addAttribute("id", String.valueOf(request.getRequest().getRequestId()));
    457                    if (request.getRequest().getNrInstructors() != 1)
    458                        assignmentEl.addAttribute("index", String.valueOf(request.getInstructorIndex()));
    459                }
    460            } else if (c instanceof SameLinkConstraint) {
    461                SameLinkConstraint si = (SameLinkConstraint) c;
    462                Element sameInstEl = constraintsEl.addElement("same-link");
    463                if (si.getConstraintId() != null)
    464                    sameInstEl.addAttribute("id", String.valueOf(si.getConstraintId()));
    465                if (si.getName() != null)
    466                    sameInstEl.addAttribute("name", si.getName());
    467                sameInstEl.addAttribute("preference", Constants.preferenceLevel2preference(si.getPreference()));
    468                for (TeachingRequest.Variable request: c.variables()) {
    469                    Element assignmentEl = sameInstEl.addElement("request");
    470                    assignmentEl.addAttribute("id", String.valueOf(request.getRequest().getRequestId()));
    471                    if (request.getRequest().getNrInstructors() != 1)
    472                        assignmentEl.addAttribute("index", String.valueOf(request.getInstructorIndex()));
    473                }
    474            }
    475        }
    476        return document;
    477    }
    478
    479    /**
    480     * Load the problem (and its solution) from an XML format
    481     * @param document XML document
    482     * @param assignment current assignment
    483     * @return true, if the problem was successfully loaded in
    484     */
    485    public boolean load(Document document, Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) {
    486        boolean loadInitial = getProperties().getPropertyBoolean("Xml.LoadInitial", true);
    487        boolean loadBest = getProperties().getPropertyBoolean("Xml.LoadBest", true);
    488        boolean loadSolution = getProperties().getPropertyBoolean("Xml.LoadSolution", true);
    489        String defaultBtb = getProperties().getProperty("Defaults.BackToBack", "0");
    490        String defaultSameDays = getProperties().getProperty("Defaults.SameDays", "0");
    491        String defaultSameRoom = getProperties().getProperty("Defaults.SameRoom", "0");
    492        String defaultConjunctive = getProperties().getProperty("Defaults.Conjunctive", "false");
    493        String defaultRequired = getProperties().getProperty("Defaults.Required", "false");
    494        String defaultSameCourse = getProperties().getProperty("Defaults.SameCourse", "R");
    495        String defaultSameCommon = getProperties().getProperty("Defaults.SameCommon", "R");
    496        Element root = document.getRootElement();
    497        if (!"instructor-schedule".equals(root.getName()))
    498            return false;
    499        Map<String, Attribute.Type> types = new HashMap<String, Attribute.Type>();
    500        Map<Long, Attribute> attributes = new HashMap<Long, Attribute>();
    501        Map<Long, Long> parents = new HashMap<Long, Long>();
    502        if (root.element("attributes") != null) {
    503            for (Iterator<?> i = root.element("attributes").elementIterator("type"); i.hasNext();) {
    504                Element typeEl = (Element) i.next();
    505                Attribute.Type type = new Attribute.Type(
    506                        Long.parseLong(typeEl.attributeValue("id")),
    507                        typeEl.attributeValue("name"),
    508                        "true".equalsIgnoreCase(typeEl.attributeValue("conjunctive", defaultConjunctive)),
    509                        "true".equalsIgnoreCase(typeEl.attributeValue("required", defaultRequired)));
    510                addAttributeType(type);
    511                if (type.getTypeName() != null)
    512                    types.put(type.getTypeName(), type);
    513                for (Iterator<?> j = typeEl.elementIterator("attribute"); j.hasNext();) {
    514                    Element attributeEl = (Element) j.next();
    515                    Attribute attribute = new Attribute(
    516                            Long.parseLong(attributeEl.attributeValue("id")),
    517                            attributeEl.attributeValue("name"),
    518                            type);
    519                    attributes.put(attribute.getAttributeId(), attribute);
    520                    if (attributeEl.attributeValue("parent") != null)
    521                        parents.put(attribute.getAttributeId(), Long.parseLong(attributeEl.attributeValue("parent")));
    522                }
    523            }
    524        }
    525        Map<Long, Course> courses = new HashMap<Long, Course>();
    526        if (root.element("courses") != null) {
    527            for (Iterator<?> i = root.element("courses").elementIterator("course"); i.hasNext();) {
    528                Element courseEl = (Element) i.next();
    529                Course course = new Course(
    530                        Long.parseLong(courseEl.attributeValue("id")),
    531                        courseEl.attributeValue("name"));
    532                courses.put(course.getCourseId(), course);
    533            }
    534        }
    535        Map<Long, Instructor> instructors = new HashMap<Long, Instructor>();
    536        for (Iterator<?> i = root.element("instructors").elementIterator("instructor"); i.hasNext();) {
    537            Element instructorEl = (Element) i.next();
    538            Instructor instructor = new Instructor(
    539                    Long.parseLong(instructorEl.attributeValue("id")),
    540                    instructorEl.attributeValue("externalId"),
    541                    instructorEl.attributeValue("name"),
    542                    string2preference(instructorEl.attributeValue("preference")),
    543                    Float.parseFloat(instructorEl.attributeValue("maxLoad", "0")));
    544            instructor.setBackToBackPreference(Integer.valueOf(instructorEl.attributeValue("btb", defaultBtb)));
    545            instructor.setSameDaysPreference(Integer.valueOf(instructorEl.attributeValue("same-days", defaultSameDays)));
    546            instructor.setSameRoomPreference(Integer.valueOf(instructorEl.attributeValue("same-room", defaultSameRoom)));
    547            for (Iterator<?> j = instructorEl.elementIterator("attribute"); j.hasNext();) {
    548                Element f = (Element) j.next();
    549                Long attributeId = Long.valueOf(f.attributeValue("id"));
    550                Attribute attribute = attributes.get(attributeId);
    551                if (attribute == null) {
    552                    Attribute.Type type = types.get(f.attributeValue("type"));
    553                    if (type == null) {
    554                        type = new Attribute.Type(types.size(), f.attributeValue("type"),
    555                                "true".equalsIgnoreCase(f.attributeValue("conjunctive", defaultConjunctive)),
    556                                "true".equalsIgnoreCase(f.attributeValue("required", defaultRequired)));
    557                        types.put(type.getTypeName(), type);
    558                    }
    559                    attribute = new Attribute(attributeId, f.attributeValue("name"), type);
    560                    attributes.put(attributeId, attribute);
    561                    if (f.attributeValue("parent") != null)
    562                        parents.put(attribute.getAttributeId(), Long.parseLong(f.attributeValue("parent")));
    563                }
    564                instructor.addAttribute(attribute);
    565            }
    566            for (Iterator<?> j = instructorEl.elementIterator("distribution"); j.hasNext();) {
    567                Element dEl = (Element) j.next();
    568                instructor.addDistribution(dEl.attributeValue("type"), dEl.attributeValue("preference", "0"), dEl.attributeValue("name"));
    569            }
    570            for (Iterator<?> j = instructorEl.elementIterator("time"); j.hasNext();) {
    571                Element f = (Element) j.next();
    572                Element classEl = f.element("section");
    573                Element courseEl = f.element("course");
    574                TimeLocation time = null;
    575                if (classEl != null) {
    576                    time = new EnrolledClass(
    577                            courseEl == null || courseEl.attributeValue("id") == null ? null : Long.valueOf(courseEl.attributeValue("id")),
    578                            classEl.attributeValue("id") == null ? null : Long.valueOf(classEl.attributeValue("id")),
    579                            courseEl == null ? null : courseEl.attributeValue("name"),
    580                            classEl.attributeValue("type"),
    581                            classEl.attributeValue("name"),
    582                            classEl.attributeValue("externalId"),
    583                            Integer.parseInt(f.attributeValue("days"), 2),
    584                            Integer.parseInt(f.attributeValue("start")),
    585                            Integer.parseInt(f.attributeValue("length")),
    586                            f.attributeValue("datePattern") == null ? null : Long.valueOf(f.attributeValue("datePattern")),
    587                            f.attributeValue("datePatternName", ""),
    588                            createBitSet(f.attributeValue("dates")),
    589                            Integer.parseInt(f.attributeValue("breakTime", "0")),
    590                            classEl.attributeValue("room"),
    591                            "instructor".equalsIgnoreCase(classEl.attributeValue("role", "instructor")));
    592                } else {
    593                    time = new TimeLocation(
    594                            Integer.parseInt(f.attributeValue("days"), 2),
    595                            Integer.parseInt(f.attributeValue("start")),
    596                            Integer.parseInt(f.attributeValue("length")), 0, 0,
    597                            f.attributeValue("datePattern") == null ? null : Long.valueOf(f.attributeValue("datePattern")),
    598                            f.attributeValue("datePatternName", ""),
    599                            createBitSet(f.attributeValue("dates")),
    600                            Integer.parseInt(f.attributeValue("breakTime", "0")));
    601                }
    602                if (f.attributeValue("pattern") != null)
    603                    time.setTimePatternId(Long.valueOf(f.attributeValue("pattern")));
    604                instructor.addTimePreference(new Preference<TimeLocation>(time, string2preference(f.attributeValue("preference"))));
    605            }
    606            for (Iterator<?> j = instructorEl.elementIterator("course"); j.hasNext();) {
    607                Element f = (Element) j.next();
    608                instructor.addCoursePreference(new Preference<Course>(new Course(Long.parseLong(f.attributeValue("id")), f.attributeValue("name")), string2preference(f.attributeValue("preference"))));
    609            }
    610            addInstructor(instructor);
    611            instructors.put(instructor.getInstructorId(), instructor);
    612        }
    613        Map<Long, TeachingRequest> requests = new HashMap<Long, TeachingRequest>();
    614        Map<TeachingRequest, Map<Integer, Instructor>> current = new HashMap<TeachingRequest, Map<Integer, Instructor>>();
    615        Map<TeachingRequest, Map<Integer, Instructor>> best = new HashMap<TeachingRequest, Map<Integer, Instructor>>();
    616        Map<TeachingRequest, Map<Integer, Instructor>> initial = new HashMap<TeachingRequest, Map<Integer, Instructor>>();
    617        for (Iterator<?> i = root.element("teaching-requests").elementIterator("request"); i.hasNext();) {
    618            Element requestEl = (Element) i.next();
    619            Element courseEl = requestEl.element("course");
    620            Course course = null;
    621            if (courseEl != null) {
    622                Long courseId = Long.valueOf(courseEl.attributeValue("id"));
    623                course = courses.get(courseId);
    624                if (course == null) {
    625                    course = new Course(courseId, courseEl.attributeValue("name"));
    626                }
    627            } else {
    628                course = courses.get(Long.valueOf(requestEl.attributeValue("course")));
    629            }
    630            List<Section> sections = new ArrayList<Section>();
    631            for (Iterator<?> j = requestEl.elementIterator("section"); j.hasNext();) {
    632                Element f = (Element) j.next();
    633                TimeLocation time = null;
    634                Element timeEl = f.element("time");
    635                if (timeEl != null) {
    636                    time = new TimeLocation(
    637                            Integer.parseInt(timeEl.attributeValue("days"), 2),
    638                            Integer.parseInt(timeEl.attributeValue("start")),
    639                            Integer.parseInt(timeEl.attributeValue("length")), 0, 0,
    640                            timeEl.attributeValue("datePattern") == null ? null : Long.valueOf(timeEl.attributeValue("datePattern")),
    641                            timeEl.attributeValue("datePatternName", ""),
    642                            createBitSet(timeEl.attributeValue("dates")),
    643                            Integer.parseInt(timeEl.attributeValue("breakTime", "0")));
    644                    if (timeEl.attributeValue("pattern") != null)
    645                        time.setTimePatternId(Long.valueOf(timeEl.attributeValue("pattern")));
    646                }
    647                Section section = new Section(
    648                        Long.valueOf(f.attributeValue("id")),
    649                        f.attributeValue("externalId"),
    650                        f.attributeValue("type"),
    651                        f.attributeValue("name"),
    652                        time,
    653                        f.attributeValue("room"),
    654                        "true".equalsIgnoreCase(f.attributeValue("canOverlap", "false")),
    655                        "true".equalsIgnoreCase(f.attributeValue("common", "false")));
    656                sections.add(section);
    657            }
    658            TeachingRequest request = new TeachingRequest(
    659                    Long.parseLong(requestEl.attributeValue("id")),
    660                    Integer.parseInt(requestEl.attributeValue("nrInstructors", "1")),
    661                    course,
    662                    Float.valueOf(requestEl.attributeValue("load", "0")),
    663                    sections,
    664                    Constants.preference2preferenceLevel(requestEl.attributeValue("sameCourse", defaultSameCourse)),
    665                    Constants.preference2preferenceLevel(requestEl.attributeValue("sameCommon", defaultSameCommon)));
    666            requests.put(request.getRequestId(), request);
    667            for (Iterator<?> j = requestEl.elementIterator("attribute"); j.hasNext();) {
    668                Element f = (Element) j.next();
    669                Long attributeId = Long.valueOf(f.attributeValue("id"));
    670                Attribute attribute = attributes.get(attributeId);
    671                if (attribute == null) {
    672                    Attribute.Type type = types.get(f.attributeValue("type"));
    673                    if (type == null) {
    674                        type = new Attribute.Type(types.size(), f.attributeValue("type"),
    675                                "true".equalsIgnoreCase(f.attributeValue("conjunctive", defaultConjunctive)),
    676                                "true".equalsIgnoreCase(f.attributeValue("required", defaultRequired)));
    677                        types.put(type.getTypeName(), type);
    678                    }
    679                    attribute = new Attribute(attributeId, f.attributeValue("name"), type);
    680                    attributes.put(attributeId, attribute);
    681                    if (f.attributeValue("parent") != null)
    682                        parents.put(attribute.getAttributeId(), Long.parseLong(f.attributeValue("parent")));
    683                }
    684                request.addAttributePreference(new Preference<Attribute>(attribute, string2preference(f.attributeValue("preference"))));
    685            }
    686            for (Iterator<?> j = requestEl.elementIterator("instructor"); j.hasNext();) {
    687                Element f = (Element) j.next();
    688                Long instructorId = Long.valueOf(f.attributeValue("id"));
    689                Instructor instructor = instructors.get(instructorId);
    690                if (instructor != null)
    691                    request.addInstructorPreference(new Preference<Instructor>(instructor, string2preference(f.attributeValue("preference"))));
    692            }
    693            if (loadBest) {
    694                for (Iterator<?> j = requestEl.elementIterator("best-instructor"); j.hasNext();) {
    695                    Element f = (Element) j.next();
    696                    Map<Integer, Instructor> idx2inst = best.get(request);
    697                    if (idx2inst == null) {
    698                        idx2inst = new HashMap<Integer, Instructor>();
    699                        best.put(request, idx2inst);
    700                    }
    701                    int index = 1 + Integer.parseInt(f.attributeValue("index", String.valueOf(idx2inst.size())));
    702                    Instructor instructor = instructors.get(Long.valueOf(f.attributeValue("id")));
    703                    if (instructor != null)
    704                        idx2inst.put(index, instructor);
    705                }
    706            }
    707            if (loadInitial) {
    708                for (Iterator<?> j = requestEl.elementIterator("initial-instructor"); j.hasNext();) {
    709                    Element f = (Element) j.next();
    710                    Map<Integer, Instructor> idx2inst = initial.get(request);
    711                    if (idx2inst == null) {
    712                        idx2inst = new HashMap<Integer, Instructor>();
    713                        initial.put(request, idx2inst);
    714                    }
    715                    int index = 1 + Integer.parseInt(f.attributeValue("index", String.valueOf(idx2inst.size())));
    716                    Instructor instructor = instructors.get(Long.valueOf(f.attributeValue("id")));
    717                    if (instructor != null)
    718                        idx2inst.put(index, instructor);
    719                }
    720            }
    721            if (loadSolution) {
    722                for (Iterator<?> j = requestEl.elementIterator("assigned-instructor"); j.hasNext();) {
    723                    Element f = (Element) j.next();
    724                    Map<Integer, Instructor> idx2inst = current.get(request);
    725                    if (idx2inst == null) {
    726                        idx2inst = new HashMap<Integer, Instructor>();
    727                        current.put(request, idx2inst);
    728                    }
    729                    int index = Integer.parseInt(f.attributeValue("index", String.valueOf(idx2inst.size())));
    730                    Instructor instructor = instructors.get(Long.valueOf(f.attributeValue("id")));
    731                    if (instructor != null)
    732                        idx2inst.put(index, instructor);
    733                }
    734            }
    735            addRequest(request);
    736        }
    737        if (root.element("constraints") != null) {
    738            for (Iterator<?> i = root.element("constraints").elementIterator(); i.hasNext();) {
    739                Element constraintEl = (Element) i.next();
    740                Constraint<TeachingRequest.Variable, TeachingAssignment> constraint = null;
    741                if ("same-link".equals(constraintEl.getName())) {
    742                    constraint = new SameLinkConstraint(
    743                            (constraintEl.attributeValue("id") == null ? null : Long.valueOf(constraintEl.attributeValue("id"))),
    744                            constraintEl.attributeValue("name"),
    745                            constraintEl.attributeValue("preference"));
    746                } else if ("same-instructor".equals(constraintEl.getName())) {
    747                    constraint = new SameInstructorConstraint(
    748                            (constraintEl.attributeValue("id") == null ? null : Long.valueOf(constraintEl.attributeValue("id"))),
    749                            constraintEl.attributeValue("name"),
    750                            constraintEl.attributeValue("preference"));
    751                }
    752                if (constraint != null) {
    753                    for (Iterator<?> j = constraintEl.elementIterator("request"); j.hasNext();) {
    754                        Element f = (Element) j.next();
    755                        TeachingRequest request = requests.get(Long.valueOf(f.attributeValue("id")));
    756                        if (request != null) {
    757                            int index = Integer.valueOf(f.attributeValue("index", "0"));
    758                            if (index >= 0 && index < request.getNrInstructors())
    759                                constraint.addVariable(request.getVariables()[index]);
    760                        }
    761                    }
    762                    addConstraint(constraint);
    763                }
    764            }
    765        }
    766        for (Map.Entry<Long, Long> e: parents.entrySet())
    767            attributes.get(e.getKey()).setParentAttribute(attributes.get(e.getValue()));
    768        for (Map.Entry<TeachingRequest, Map<Integer, Instructor>> e1: best.entrySet())
    769            for (Map.Entry<Integer, Instructor> e2: e1.getValue().entrySet())
    770                if (e2.getKey() >= 0 && e2.getKey() < e1.getKey().getNrInstructors()) {
    771                    TeachingRequest.Variable variable = e1.getKey().getVariables()[e2.getKey()];
    772                    variable.setBestAssignment(new TeachingAssignment(variable, e2.getValue()), 0l);
    773                }
    774
    775        for (Map.Entry<TeachingRequest, Map<Integer, Instructor>> e1: initial.entrySet())
    776            for (Map.Entry<Integer, Instructor> e2: e1.getValue().entrySet())
    777                if (e2.getKey() >= 0 && e2.getKey() < e1.getKey().getNrInstructors()) {
    778                    TeachingRequest.Variable variable = e1.getKey().getVariables()[e2.getKey()];
    779                    variable.setInitialAssignment(new TeachingAssignment(variable, e2.getValue()));
    780                }
    781
    782        if (!current.isEmpty()) {
    783            for (Map.Entry<TeachingRequest, Map<Integer, Instructor>> e1: current.entrySet())
    784                for (Map.Entry<Integer, Instructor> e2: e1.getValue().entrySet())
    785                    if (e2.getKey() >= 0 && e2.getKey() < e1.getKey().getNrInstructors()) {
    786                        TeachingRequest.Variable variable = e1.getKey().getVariables()[e2.getKey()];
    787                        TeachingAssignment ta = new TeachingAssignment(variable, e2.getValue());
    788                        Set<TeachingAssignment> conf = conflictValues(assignment, ta);
    789                        if (conf.isEmpty()) {
    790                            assignment.assign(0, ta);
    791                        } else {
    792                            sLog.error("Unable to assign " + ta.getName() + " to " + variable.getName());
    793                            sLog.error("Conflicts:" + ToolBox.dict2string(conflictConstraints(assignment, ta), 2));
    794                        }
    795                    }
    796        }
    797
    798        return true;
    799    }
    800
    801    /** Convert bitset to a bit string */
    802    protected static String bitset2string(BitSet b) {
    803        StringBuffer sb = new StringBuffer();
    804        for (int i = 0; i < b.length(); i++)
    805            sb.append(b.get(i) ? "1" : "0");
    806        return sb.toString();
    807    }
    808
    809    /** Create BitSet from a bit string */
    810    protected static BitSet createBitSet(String bitString) {
    811        if (bitString == null) return null;
    812        BitSet ret = new BitSet(bitString.length());
    813        for (int i = 0; i < bitString.length(); i++)
    814            if (bitString.charAt(i) == '1')
    815                ret.set(i);
    816        return ret;
    817    }
    818
    819    /** Convert preference string to a preference value */
    820    protected static int string2preference(String pref) {
    821        if (pref == null || pref.isEmpty()) return 0;
    822        if (Constants.sPreferenceRequired.equals(pref))
    823            return Constants.sPreferenceLevelRequired;
    824        if (Constants.sPreferenceProhibited.equals(pref))
    825            return Constants.sPreferenceLevelProhibited;
    826        return Integer.valueOf(pref);
    827    }
    828
    829    private List<BitSet> iWeeks = null;
    830    /**
    831     * The method creates date patterns (bitsets) which represent the weeks of a
    832     * semester.
    833     *
    834     * @return a list of BitSets which represents the weeks of a semester.
    835     */
    836    public List<BitSet> getWeeks() {
    837        if (iWeeks == null) {
    838            String defaultDatePattern = getProperties().getProperty("DatePattern.CustomDatePattern", null);
    839            if (defaultDatePattern == null){
    840                defaultDatePattern = getProperties().getProperty("DatePattern.Default");
    841            }
    842            BitSet fullTerm = null;
    843            if (defaultDatePattern == null) {
    844                // Take the date pattern that is being used most often
    845                Map<Long, Integer> counter = new HashMap<Long, Integer>();
    846                int max = 0; String name = null; Long id = null;
    847                for (TeachingRequest.Variable tr: variables()) {
    848                    for (Section section: tr.getSections()) {
    849                        TimeLocation time = section.getTime();
    850                        if (time.getWeekCode() != null && time.getDatePatternId() != null) {
    851                            int count = 1;
    852                            if (counter.containsKey(time.getDatePatternId()))
    853                                count += counter.get(time.getDatePatternId());
    854                            counter.put(time.getDatePatternId(), count);
    855                            if (count > max) {
    856                                max = count; fullTerm = time.getWeekCode(); name = time.getDatePatternName(); id = time.getDatePatternId();
    857                            }
    858                        }
    859                    }
    860                }
    861                sLog.info("Using date pattern " + name + " (id " + id + ") as the default.");
    862            } else {
    863                // Create default date pattern
    864                fullTerm = new BitSet(defaultDatePattern.length());
    865                for (int i = 0; i < defaultDatePattern.length(); i++) {
    866                    if (defaultDatePattern.charAt(i) == 49) {
    867                        fullTerm.set(i);
    868                    }
    869                }
    870            }
    871
    872            if (fullTerm == null) return null;
    873
    874            iWeeks = new ArrayList<BitSet>();
    875            if (getProperties().getPropertyBoolean("DatePattern.ShiftWeeks", false)) {
    876                // Cut date pattern into weeks (each week takes 7 consecutive bits, starting on the next positive bit)
    877                for (int i = fullTerm.nextSetBit(0); i < fullTerm.length(); ) {
    878                    if (!fullTerm.get(i)) {
    879                        i++; continue;
    880                    }
    881                    BitSet w = new BitSet(i + 7);
    882                    for (int j = 0; j < 7; j++)
    883                        if (fullTerm.get(i + j)) w.set(i + j);
    884                    iWeeks.add(w);
    885                    i += 7;
    886                }
    887            } else {
    888                // Cut date pattern into weeks (each week takes 7 consecutive bits starting on the first bit of the default date pattern, no pauses between weeks)
    889                for (int i = fullTerm.nextSetBit(0); i < fullTerm.length(); ) {
    890                    BitSet w = new BitSet(i + 7);
    891                    for (int j = 0; j < 7; j++)
    892                        if (fullTerm.get(i + j)) w.set(i + j);
    893                    iWeeks.add(w);
    894                    i += 7;
    895                }
    896            }
    897        }
    898        return iWeeks;
    899    }
    900}