    001package org.cpsolver.coursett.model;
    002
    003import java.util.ArrayList;
    004import java.util.Enumeration;
    005import java.util.List;
    006
    007import org.cpsolver.coursett.Constants;
    008import org.cpsolver.coursett.constraint.GroupConstraint;
    009import org.cpsolver.coursett.constraint.InstructorConstraint;
    010import org.cpsolver.coursett.constraint.SpreadConstraint;
    011import org.cpsolver.coursett.preference.PreferenceCombination;
    012import org.cpsolver.ifs.assignment.Assignment;
    013import org.cpsolver.ifs.criteria.Criterion;
    014import org.cpsolver.ifs.model.Value;
    015import org.cpsolver.ifs.util.DistanceMetric;
    016import org.cpsolver.ifs.util.ToolBox;
    017
    018
    019/**
    020 * Placement (value). <br>
    021 * <br>
    022 * It combines room and time location
    023 *
    024 * @author  Tomáš Müller
    025 * @version CourseTT 1.3 (University Course Timetabling)<br>
    026 *          Copyright (C) 2006 - 2014 Tomáš Müller<br>
    027 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    028 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    029 * <br>
    030 *          This library is free software; you can redistribute it and/or modify
    031 *          it under the terms of the GNU Lesser General Public License as
    032 *          published by the Free Software Foundation; either version 3 of the
    033 *          License, or (at your option) any later version. <br>
    034 * <br>
    035 *          This library is distributed in the hope that it will be useful, but
    036 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    037 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    038 *          Lesser General Public License for more details. <br>
    039 * <br>
    040 *          You should have received a copy of the GNU Lesser General Public
    041 *          License along with this library; if not see
    042 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
    043 */
    044
    045public class Placement extends Value<Lecture, Placement> {
    046    private TimeLocation iTimeLocation;
    047    private RoomLocation iRoomLocation;
    048    private List<RoomLocation> iRoomLocations = null;
    049    private Long iAssignmentId = null;
    050    private int iHashCode = 0;
    051    private Double iTimePenalty = null;
    052    private Integer iRoomPenalty = null;
    053
    054    /**
    055     * Constructor
    056     *
    057     * @param lecture
    058     *            lecture
    059     * @param timeLocation
    060     *            time location
    061     * @param roomLocation
    062     *            room location
    063     */
    064    public Placement(Lecture lecture, TimeLocation timeLocation, RoomLocation roomLocation) {
    065        super(lecture);
    066        iTimeLocation = timeLocation;
    067        iRoomLocation = roomLocation;
    068        if (iRoomLocation == null) {
    069            iRoomLocations = new ArrayList<RoomLocation>(0);
    070        }
    071        iHashCode = getName().hashCode();
    072    }
    073
    074    public Placement(Lecture lecture, TimeLocation timeLocation, java.util.List<RoomLocation> roomLocations) {
    075        super(lecture);
    076        iTimeLocation = timeLocation;
    077        iRoomLocation = (roomLocations.isEmpty() ? null : (RoomLocation) roomLocations.get(0));
    078        if (roomLocations.size() != 1) {
    079            iRoomLocations = new ArrayList<RoomLocation>(roomLocations);
    080        }
    081        if (iRoomLocations != null && iRoomLocations.size() > 1) {
    082            boolean hasPreferenceByIndex = false;
    083            for (RoomLocation r: iRoomLocations)
    084                if (r.hasPreferenceByIndex()) { hasPreferenceByIndex = true; break; }
    085            if (hasPreferenceByIndex)
    086                fixRoomOrder(0, roomLocations, new RoomLocation[iRoomLocations.size()], PreferenceCombination.getDefault(), null);
    087        }
    088        iHashCode = getName().hashCode();
    089    }
    090
    091    private Integer fixRoomOrder(int idx, List<RoomLocation> rooms, RoomLocation[] current, PreferenceCombination preference, Integer bestSoFar) {
    092        if (idx == current.length) {
    093            if (bestSoFar == null || preference.getPreferenceInt() < bestSoFar) {
    094                iRoomLocations.clear();
    095                for (RoomLocation r: current)
    096                    iRoomLocations.add(r);
    097                return preference.getPreferenceInt();
    098            }
    099        } else {
    100            r: for (RoomLocation r: rooms) {
    101                for (int i = 0; i < idx; i++)
    102                    if (r.equals(current[i])) continue r;
    103                PreferenceCombination pc = preference.clonePreferenceCombination();
    104                pc.addPreferenceInt(r.getPreference(idx));
    105                if (!pc.isProhibited()) {
    106                    current[idx] = r;
    107                    bestSoFar = fixRoomOrder(idx + 1, rooms, current, pc, bestSoFar);
    108                }
    109            }
    110        }
    111        return bestSoFar;
    112    }
    113
    114    /** Time location
    115     * @return time of this placement
    116     **/
    117    public TimeLocation getTimeLocation() {
    118        return iTimeLocation;
    119    }
    120
    121    /** Room location
    122     * @return room of this placement
    123     **/
    124    public RoomLocation getRoomLocation() {
    125        return iRoomLocation;
    126    }
    127
    128    /** Room locations (multi-room placement)
    129     * @return rooms of this placement (if there are more than one)
    130     **/
    131    public List<RoomLocation> getRoomLocations() {
    132        return iRoomLocations;
    133    }
    134
    135    public List<Long> getBuildingIds() {
    136        if (isMultiRoom()) {
    137            List<Long> ret = new ArrayList<Long>(iRoomLocations.size());
    138            for (RoomLocation r : iRoomLocations) {
    139                ret.add(r.getBuildingId());
    140            }
    141            return ret;
    142        } else {
    143            List<Long> ret = new ArrayList<Long>(1);
    144            ret.add(iRoomLocation.getBuildingId());
    145            return ret;
    146        }
    147    }
    148
    149    public List<Long> getRoomIds() {
    150        if (isMultiRoom()) {
    151            List<Long> ret = new ArrayList<Long>(iRoomLocations.size());
    152            for (RoomLocation r : iRoomLocations) {
    153                ret.add(r.getId());
    154            }
    155            return ret;
    156        } else {
    157            List<Long> ret = new ArrayList<Long>(1);
    158            ret.add(iRoomLocation.getId());
    159            return ret;
    160        }
    161    }
    162
    163    public List<String> getRoomNames() {
    164        if (isMultiRoom()) {
    165            List<String> ret = new ArrayList<String>(iRoomLocations.size());
    166            for (RoomLocation r : iRoomLocations) {
    167                ret.add(r.getName());
    168            }
    169            return ret;
    170        } else {
    171            List<String> ret = new ArrayList<String>(1);
    172            if (iRoomLocation != null)
    173                ret.add(iRoomLocation.getName());
    174            return ret;
    175        }
    176    }
    177
    178    public List<Integer> getRoomPrefs() {
    179        if (isMultiRoom()) {
    180            List<Integer> ret = new ArrayList<Integer>(iRoomLocations.size());
    181            int roomIndex = 0;
    182            for (RoomLocation r : iRoomLocations) {
    183                ret.add(r.getPreference(roomIndex++));
    184            }
    185            return ret;
    186        } else {
    187            List<Integer> ret = new ArrayList<Integer>(1);
    188            if (iRoomLocation != null)
    189                ret.add(iRoomLocation.getPreference());
    190            return ret;
    191        }
    192    }
    193
    194    public boolean isMultiRoom() {
    195        return (iRoomLocations != null && iRoomLocations.size() != 1);
    196    }
    197
    198    public RoomLocation getRoomLocation(Long roomId) {
    199        if (isMultiRoom()) {
    200            for (RoomLocation r : iRoomLocations) {
    201                if (r.getId().equals(roomId))
    202                    return r;
    203            }
    204        } else if (iRoomLocation != null && iRoomLocation.getId().equals(roomId))
    205            return iRoomLocation;
    206        return null;
    207    }
    208
    209    public int getRoomLocationIndex(Long roomId) {
    210        if (isMultiRoom()) {
    211            int idx = 0;
    212            for (RoomLocation r : iRoomLocations) {
    213                if (r.getId().equals(roomId))
    214                    return idx;
    215                idx ++;
    216            }
    217        } else if (iRoomLocation != null && iRoomLocation.getId().equals(roomId))
    218            return 0;
    219        return -1;
    220    }
    221
    222    public boolean hasRoomLocation(Long roomId) {
    223        if (isMultiRoom()) {
    224            for (RoomLocation r : iRoomLocations) {
    225                if (r.getId().equals(roomId))
    226                    return true;
    227            }
    228            return false;
    229        } else
    230            return iRoomLocation != null && iRoomLocation.getId().equals(roomId);
    231    }
    232
    233    public String getRoomName(String delim) {
    234        if (isMultiRoom()) {
    235            StringBuffer sb = new StringBuffer();
    236            for (RoomLocation r : iRoomLocations) {
    237                if (sb.length() > 0)
    238                    sb.append(delim);
    239                sb.append(r.getName());
    240            }
    241            return sb.toString();
    242        } else {
    243            return (getRoomLocation() == null ? "" : getRoomLocation().getName());
    244        }
    245    }
    246
    247    @Override
    248    public String getName() {
    249        return getName(true);
    250    }
    251
    252    public String getName(boolean useAmPm) {
    253        Lecture lecture = variable();
    254        return getTimeLocation().getName(useAmPm) + " " + getRoomName(", ")
    255                + (lecture != null && lecture.getInstructorName() != null ? " " + lecture.getInstructorName() : "");
    256    }
    257
    258    public String getLongName(boolean useAmPm) {
    259        Lecture lecture = variable();
    260        if (isMultiRoom()) {
    261            StringBuffer sb = new StringBuffer();
    262            for (RoomLocation r : iRoomLocations) {
    263                if (sb.length() > 0)
    264                    sb.append(", ");
    265                sb.append(r.getName());
    266            }
    267            return getTimeLocation().getLongName(useAmPm) + " " + sb
    268                    + (lecture != null &&  lecture.getInstructorName() != null ? " " + lecture.getInstructorName() : "");
    269        } else
    270            return getTimeLocation().getLongName(useAmPm)
    271                    + (getRoomLocation() == null ? "" : " " + getRoomLocation().getName())
    272                    + (lecture != null && lecture.getInstructorName() != null ? " " + lecture.getInstructorName() : "");
    273    }
    274
    275    @Deprecated
    276    public String getLongName() {
    277        return getLongName(true);
    278    }
    279
    280    public boolean sameRooms(Placement placement) {
    281        if (placement.isMultiRoom() != isMultiRoom())
    282            return false;
    283        if (isMultiRoom()) {
    284            if (placement.getRoomLocations().size() != getRoomLocations().size())
    285                return false;
    286            return placement.getRoomLocations().containsAll(getRoomLocations());
    287        } else {
    288            if (placement.getRoomLocation() == null)
    289                return getRoomLocation() == null;
    290            return placement.getRoomLocation().equals(getRoomLocation());
    291        }
    292    }
    293
    294    public boolean shareRooms(Placement placement) {
    295        if (isMultiRoom()) {
    296            if (placement.isMultiRoom()) {
    297                for (RoomLocation rl : getRoomLocations()) {
    298                    if (rl.getRoomConstraint() == null || !rl.getRoomConstraint().getConstraint())
    299                        continue;
    300                    if (placement.getRoomLocations().contains(rl))
    301                        return true;
    302                }
    303                return false;
    304            } else {
    305                if (placement.getRoomLocation().getRoomConstraint() == null || !placement.getRoomLocation().getRoomConstraint().getConstraint())
    306                    return false;
    307                return getRoomLocations().contains(placement.getRoomLocation());
    308            }
    309        } else {
    310            if (getRoomLocation().getRoomConstraint() == null || !getRoomLocation().getRoomConstraint().getConstraint())
    311                return false;
    312            if (placement.isMultiRoom()) {
    313                return placement.getRoomLocations().contains(getRoomLocation());
    314            } else {
    315                return getRoomLocation().equals(placement.getRoomLocation());
    316            }
    317        }
    318    }
    319
    320    public int nrDifferentRooms(Placement placement) {
    321        if (isMultiRoom()) {
    322            int ret = 0;
    323            for (RoomLocation r : getRoomLocations()) {
    324                if (!placement.getRoomLocations().contains(r))
    325                    ret++;
    326            }
    327            return ret;
    328        } else {
    329            return (placement.getRoomLocation().equals(getRoomLocation()) ? 0 : 1);
    330        }
    331    }
    332
    333    public int nrDifferentBuildings(Placement placement) {
    334        if (isMultiRoom()) {
    335            int ret = 0;
    336            for (RoomLocation r : getRoomLocations()) {
    337                boolean contains = false;
    338                for (RoomLocation q : placement.getRoomLocations()) {
    339                    if (ToolBox.equals(r.getBuildingId(), q.getBuildingId()))
    340                        contains = true;
    341                }
    342                if (!contains)
    343                    ret++;
    344            }
    345            return ret;
    346        } else {
    347            return (ToolBox.equals(placement.getRoomLocation().getBuildingId(), getRoomLocation().getBuildingId()) ? 0
    348                    : 1);
    349        }
    350    }
    351
    352    public int sumRoomPreference() {
    353        if (isMultiRoom()) {
    354            int ret = 0;
    355            int roomIndex = 0;
    356            for (RoomLocation r : getRoomLocations()) {
    357                ret += r.getPreference(roomIndex ++);
    358            }
    359            return ret;
    360        } else {
    361            return getRoomLocation().getPreference();
    362        }
    363    }
    364
    365    public int getRoomPreference() {
    366        if (isMultiRoom()) {
    367            PreferenceCombination p = PreferenceCombination.getDefault();
    368            int roomIndex = 0;
    369            for (RoomLocation r : getRoomLocations()) {
    370                p.addPreferenceInt(r.getPreference(roomIndex++));
    371            }
    372            return p.getPreferenceInt();
    373        } else {
    374            return getRoomLocation().getPreference();
    375        }
    376    }
    377
    378    public int getRoomSize() {
    379        if (isMultiRoom()) {
    380            if (getRoomLocations().isEmpty()) return 0;
    381            if (variable() != null && variable().isSplitAttendance()) {
    382                int roomSize = 0;
    383                for (RoomLocation r : getRoomLocations())
    384                    roomSize += r.getRoomSize();
    385                return roomSize;
    386            } else {
    387                int roomSize = Integer.MAX_VALUE;
    388                for (RoomLocation r : getRoomLocations()) {
    389                    roomSize = Math.min(roomSize, r.getRoomSize());
    390                }
    391                return roomSize;
    392            }
    393        } else {
    394            return getRoomLocation().getRoomSize();
    395        }
    396    }
    397
    398    public boolean isHard(Assignment<Lecture, Placement> assignment) {
    399        if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(getTimeLocation().getPreference())))
    400            return true;
    401        if (isRoomProhibited()) return true;
    402        Lecture lecture = variable();
    403        for (GroupConstraint gc : lecture.hardGroupSoftConstraints()) {
    404            if (gc.isSatisfied(assignment))
    405                continue;
    406            if (Constants.sPreferenceProhibited.equals(gc.getPrologPreference()))
    407                return true;
    408            if (Constants.sPreferenceRequired.equals(gc.getPrologPreference()))
    409                return true;
    410        }
    411        return false;
    412    }
    413
    414    public boolean isRoomProhibited() {
    415        if (isMultiRoom()) {
    416            int roomIndex = 0;
    417            for (RoomLocation r : getRoomLocations()) {
    418                if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(r.getPreference(roomIndex++))))
    419                    return true;
    420            }
    421        } else {
    422            if (getRoomLocation() != null && Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(getRoomLocation().getPreference())))
    423                return true;
    424        }
    425        return false;
    426    }
    427
    428    public boolean sameTime(Placement placement) {
    429        return placement.getTimeLocation().equals(getTimeLocation());
    430    }
    431
    432    @Override
    433    public boolean equals(Object object) {
    434        if (object == null || !(object instanceof Placement))
    435            return false;
    436        Placement placement = (Placement) object;
    437        if (placement.getId() == getId())
    438            return true; // quick check
    439        Lecture lecture = placement.variable();
    440        Lecture thisLecture = variable();
    441        if (lecture != null && thisLecture != null && !lecture.getClassId().equals(thisLecture.getClassId()))
    442            return false;
    443        if (!sameRooms(placement))
    444            return false;
    445        if (!sameTime(placement))
    446            return false;
    447        return true;
    448    }
    449
    450    @Override
    451    public int hashCode() {
    452        return iHashCode;
    453    }
    454
    455    @Override
    456    public String toString() {
    457        return variable().getName() + " " + getName();
    458    }
    459
    460    /** Distance between two placements
    461     * @param m distance matrix
    462     * @param p1 first placement
    463     * @param p2 second placement
    464     * @return maximal distance in meters between the two placement
    465     **/
    466    public static double getDistanceInMeters(DistanceMetric m, Placement p1, Placement p2) {
    467        if (p1.isMultiRoom()) {
    468            if (p2.isMultiRoom()) {
    469                double dist = 0.0;
    470                for (RoomLocation r1 : p1.getRoomLocations()) {
    471                    for (RoomLocation r2 : p2.getRoomLocations()) {
    472                        dist = Math.max(dist, r1.getDistanceInMeters(m, r2));
    473                    }
    474                }
    475                return dist;
    476            } else {
    477                if (p2.getRoomLocation() == null)
    478                    return 0.0;
    479                double dist = 0.0;
    480                for (RoomLocation r1 : p1.getRoomLocations()) {
    481                    dist = Math.max(dist, r1.getDistanceInMeters(m, p2.getRoomLocation()));
    482                }
    483                return dist;
    484            }
    485        } else if (p2.isMultiRoom()) {
    486            if (p1.getRoomLocation() == null)
    487                return 0.0;
    488            double dist = 0.0;
    489            for (RoomLocation r2 : p2.getRoomLocations()) {
    490                dist = Math.max(dist, p1.getRoomLocation().getDistanceInMeters(m, r2));
    491            }
    492            return dist;
    493        } else {
    494            if (p1.getRoomLocation() == null || p2.getRoomLocation() == null)
    495                return 0.0;
    496            return p1.getRoomLocation().getDistanceInMeters(m, p2.getRoomLocation());
    497        }
    498    }
    499
    500    /** Distance between two placements
    501     * @param m distance matrix
    502     * @param p1 first placement
    503     * @param p2 second placement
    504     * @return maximal distance in minutes between the two placement
    505     **/
    506    public static int getDistanceInMinutes(DistanceMetric m, Placement p1, Placement p2) {
    507        if (p1.isMultiRoom()) {
    508            if (p2.isMultiRoom()) {
    509                int dist = 0;
    510                for (RoomLocation r1 : p1.getRoomLocations()) {
    511                    for (RoomLocation r2 : p2.getRoomLocations()) {
    512                        dist = Math.max(dist, r1.getDistanceInMinutes(m, r2));
    513                    }
    514                }
    515                return dist;
    516            } else {
    517                if (p2.getRoomLocation() == null)
    518                    return 0;
    519                int dist = 0;
    520                for (RoomLocation r1 : p1.getRoomLocations()) {
    521                    dist = Math.max(dist, r1.getDistanceInMinutes(m, p2.getRoomLocation()));
    522                }
    523                return dist;
    524            }
    525        } else if (p2.isMultiRoom()) {
    526            if (p1.getRoomLocation() == null)
    527                return 0;
    528            int dist = 0;
    529            for (RoomLocation r2 : p2.getRoomLocations()) {
    530                dist = Math.max(dist, p1.getRoomLocation().getDistanceInMinutes(m, r2));
    531            }
    532            return dist;
    533        } else {
    534            if (p1.getRoomLocation() == null || p2.getRoomLocation() == null)
    535                return 0;
    536            return p1.getRoomLocation().getDistanceInMinutes(m, p2.getRoomLocation());
    537        }
    538    }
    539
    540    public int getCommitedConflicts() {
    541        int ret = 0;
    542        Lecture lecture = variable();
    543        for (Student student : lecture.students()) {
    544            ret += student.countConflictPlacements(this);
    545        }
    546        return ret;
    547    }
    548
    549    public Long getAssignmentId() {
    550        return iAssignmentId;
    551    }
    552
    553    public void setAssignmentId(Long assignmentId) {
    554        iAssignmentId = assignmentId;
    555    }
    556
    557    public boolean canShareRooms(Placement other) {
    558        return (variable()).canShareRoom(other.variable());
    559    }
    560
    561    public boolean isValid() {
    562        Lecture lecture = variable();
    563        if (!lecture.isValid(this))
    564            return false;
    565        for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
    566            if (!ic.isAvailable(lecture, this) && ic.isHard())
    567                return false;
    568        }
    569        if (lecture.getNrRooms() > 0) {
    570            if (isMultiRoom()) {
    571                for (RoomLocation roomLocation : getRoomLocations()) {
    572                    if (roomLocation.getRoomConstraint() != null
    573                            && !roomLocation.getRoomConstraint().isAvailable(lecture, getTimeLocation(),
    574                                    lecture.getScheduler()))
    575                        return false;
    576                }
    577            } else {
    578                if (getRoomLocation().getRoomConstraint() != null
    579                        && !getRoomLocation().getRoomConstraint().isAvailable(lecture, getTimeLocation(),
    580                                lecture.getScheduler()))
    581                    return false;
    582            }
    583        }
    584        return true;
    585    }
    586
    587    public String getNotValidReason(Assignment<Lecture, Placement> assignment, boolean useAmPm) {
    588        Lecture lecture = variable();
    589        String reason = lecture.getNotValidReason(assignment, this, useAmPm);
    590        if (reason != null)
    591            return reason;
    592        for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
    593            if (!ic.isAvailable(lecture, this) && ic.isHard()) {
    594                if (!ic.isAvailable(lecture, getTimeLocation())) {
    595                    for (Placement c: ic.getUnavailabilities()) {
    596                        if (c.variable().getId() < 0 && lecture.getDepartment() != null && c.variable().getDepartment() != null
    597                                && !c.variable().getDepartment().equals(lecture.getDepartment())) continue;
    598                        if (c.getTimeLocation().hasIntersection(getTimeLocation()) && !lecture.canShareRoom(c.variable()))
    599                            return "instructor " + ic.getName() + " not available at " + getTimeLocation().getLongName(useAmPm) + " due to " + c.variable().getName();
    600                    }
    601                    return "instructor " + ic.getName() + " not available at " + getTimeLocation().getLongName(useAmPm);
    602                } else
    603                    return "placement " + getTimeLocation().getLongName(useAmPm) + " " + getRoomName(", ") + " is too far for instructor " + ic.getName();
    604            }
    605        }
    606        if (lecture.getNrRooms() > 0) {
    607            if (isMultiRoom()) {
    608                for (RoomLocation roomLocation : getRoomLocations()) {
    609                    if (roomLocation.getRoomConstraint() != null && !roomLocation.getRoomConstraint().isAvailable(lecture, getTimeLocation(), lecture.getScheduler())) {
    610                        if (roomLocation.getRoomConstraint().getAvailableArray() != null) {
    611                            for (Enumeration<Integer> e = getTimeLocation().getSlots(); e.hasMoreElements();) {
    612                                int slot = e.nextElement();
    613                                if (roomLocation.getRoomConstraint().getAvailableArray()[slot] != null) {
    614                                    for (Placement c : roomLocation.getRoomConstraint().getAvailableArray()[slot]) {
    615                                        if (c.getTimeLocation().hasIntersection(getTimeLocation()) && !lecture.canShareRoom(c.variable())) {
    616                                            return "room " + roomLocation.getName() + " not available at " + getTimeLocation().getLongName(useAmPm) + " due to " + c.variable().getName();
    617                                        }
    618                                    }
    619                                }
    620                            }
    621                        }
    622                        return "room " + roomLocation.getName() + " not available at " + getTimeLocation().getLongName(useAmPm);
    623                    }
    624                }
    625            } else {
    626                if (getRoomLocation().getRoomConstraint() != null && !getRoomLocation().getRoomConstraint().isAvailable(lecture, getTimeLocation(), lecture.getScheduler()))
    627                    if (getRoomLocation().getRoomConstraint().getAvailableArray() != null) {
    628                        for (Enumeration<Integer> e = getTimeLocation().getSlots(); e.hasMoreElements();) {
    629                            int slot = e.nextElement();
    630                            if (getRoomLocation().getRoomConstraint().getAvailableArray()[slot] != null) {
    631                                for (Placement c : getRoomLocation().getRoomConstraint().getAvailableArray()[slot]) {
    632                                    if (c.getTimeLocation().hasIntersection(getTimeLocation()) && !lecture.canShareRoom(c.variable())) {
    633                                        return "room " + getRoomLocation().getName() + " not available at " + getTimeLocation().getLongName(useAmPm) + " due to " + c.variable().getName();
    634                                    }
    635                                }
    636                            }
    637                        }
    638                    }
    639                    return "room " + getRoomLocation().getName() + " not available at " + getTimeLocation().getLongName(useAmPm);
    640            }
    641        }
    642        return reason;
    643    }
    644
    645    @Deprecated
    646    public String getNotValidReason(Assignment<Lecture, Placement> assignment) {
    647        return getNotValidReason(assignment, true);
    648    }
    649
    650    public int getNrRooms() {
    651        if (iRoomLocations != null)
    652            return iRoomLocations.size();
    653        return (iRoomLocation == null ? 0 : 1);
    654    }
    655
    656    public int getSpreadPenalty(Assignment<Lecture, Placement> assignment) {
    657        int spread = 0;
    658        for (SpreadConstraint sc : variable().getSpreadConstraints()) {
    659            spread += sc.getPenalty(assignment, this);
    660        }
    661        return spread;
    662    }
    663
    664    public int getMaxSpreadPenalty(Assignment<Lecture, Placement> assignment) {
    665        int spread = 0;
    666        for (SpreadConstraint sc : variable().getSpreadConstraints()) {
    667            spread += sc.getMaxPenalty(assignment, this);
    668        }
    669        return spread;
    670    }
    671
    672    @Override
    673    public double toDouble(Assignment<Lecture, Placement> assignment) {
    674        double ret = 0.0;
    675        for (Criterion<Lecture, Placement> criterion: variable().getModel().getCriteria())
    676            ret += criterion.getWeightedValue(assignment, this, null);
    677        return ret;
    678    }
    679
    680    private transient Object iAssignment = null;
    681
    682    public Object getAssignment() {
    683        return iAssignment;
    684    }
    685
    686    public void setAssignment(Object assignment) {
    687        iAssignment = assignment;
    688    }
    689
    690    public double getTimePenalty() {
    691        if (iTimeLocation == null) return 0.0;
    692        if (iTimePenalty == null) {
    693            double[] bounds = variable().getMinMaxTimePreference();
    694            double npref = iTimeLocation.getNormalizedPreference();
    695            if (iTimeLocation.getPreference() < Constants.sPreferenceLevelRequired / 2) npref = bounds[0];
    696            else if (iTimeLocation.getPreference() > Constants.sPreferenceLevelProhibited / 2) npref = bounds[1];
    697            iTimePenalty = npref - bounds[0];
    698        }
    699        return iTimePenalty;
    700    }
    701
    702    public int getRoomPenalty() {
    703        if (getNrRooms() == 0) return 0;
    704        if (iRoomPenalty == null) {
    705            int pref = getRoomPreference();
    706            int[] bounds = variable().getMinMaxRoomPreference();
    707            if (pref < Constants.sPreferenceLevelRequired / 2) pref = bounds[0];
    708            if (pref > Constants.sPreferenceLevelProhibited / 2) pref = bounds[1];
    709            iRoomPenalty = pref - bounds[0];
    710        }
    711        return iRoomPenalty;
    712    }
    713}