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