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