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