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