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}