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