001package org.cpsolver.exam.model;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.List;
006import java.util.Set;
007
008import org.cpsolver.coursett.IdConvertor;
009import org.cpsolver.ifs.model.Model;
010import org.cpsolver.ifs.util.DataProperties;
011import org.cpsolver.ifs.util.ToolBox;
012import org.dom4j.Element;
013
014
015/**
016 * Abstract room sharing model. Defines when and under what conditions two or more exams can share a room.<br>
017 * <br>
018 * 
019 * @author  Tomáš Müller
020 * @version ExamTT 1.3 (Examination Timetabling)<br>
021 *          Copyright (C) 2008 - 2014 Tomáš Müller<br>
022 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
023 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
024 * <br>
025 *          This library is free software; you can redistribute it and/or modify
026 *          it under the terms of the GNU Lesser General Public License as
027 *          published by the Free Software Foundation; either version 3 of the
028 *          License, or (at your option) any later version. <br>
029 * <br>
030 *          This library is distributed in the hope that it will be useful, but
031 *          WITHOUT ANY WARRANTY; without even the implied warranty of
032 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
033 *          Lesser General Public License for more details. <br>
034 * <br>
035 *          You should have received a copy of the GNU Lesser General Public
036 *          License along with this library; if not see
037 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
038 */
039public abstract class ExamRoomSharing  {
040    
041    public ExamRoomSharing(Model<Exam, ExamPlacement> model, DataProperties config) {}
042        
043    /**
044     * True if given examination can not be placed in the same room at the same period as the other examinations
045     * @param exam examination placement in question
046     * @param other exams currently assigned in the room at the requested period
047     * @param room examination room in questions
048     * @return true if there is a conflict
049     */
050    public boolean inConflict(ExamPlacement exam, Collection<ExamPlacement> other, ExamRoom room) {
051        if (exam.getRoomPlacements().size() != 1)
052            return !other.isEmpty();
053        
054        return inConflict(exam.variable(), other, room);
055    }
056    
057    /**
058     * True if given examination can not be placed in the same room at the same period as the other examinations
059     * @param exam examination in question
060     * @param other exams currently assigned in the room at the requested period
061     * @param room examination room in questions
062     * @return true if there is a conflict
063     */
064    public boolean inConflict(Exam exam, Collection<ExamPlacement> other, ExamRoom room) {
065        int total = exam.getSize();
066        boolean altSeating = exam.hasAltSeating();
067        for (ExamPlacement x: other) {
068            if (x.variable().equals(exam)) continue;
069            if (x.getRoomPlacements().size() != 1) return true; // already split into multiple rooms
070            if (!canShareRoom(exam, x.variable())) return true; // sharing not allowed between the pair
071            total += x.variable().getSize();
072            if (x.variable().hasAltSeating()) altSeating = true;
073        }
074        
075        return total > (altSeating ? room.getAltSize() : room.getSize()); // check size limit
076    }
077    
078    /**
079     * Compute conflicting placement for the case when a given examination needs to be placed in the same room at the same period as the other examinations
080     * @param exam examination placement in question
081     * @param other exams currently assigned in the room at the requested period
082     * @param room examination room in questions
083     * @param conflicts set of conflicting assignments
084     */
085    public void computeConflicts(ExamPlacement exam, Collection<ExamPlacement> other, ExamRoom room, Set<ExamPlacement> conflicts) {
086        // more than one room is required -> no sharing
087        if (exam.getRoomPlacements().size() != 1) {
088            conflicts.addAll(other);
089            return;
090        }
091        
092        computeConflicts(exam.variable(), other, room, conflicts);
093    }
094    
095    /**
096     * Compute conflicting placement for the case when a given examination needs to be placed in the same room at the same period as the other examinations
097     * @param exam examination in question
098     * @param other exams currently assigned in the room at the requested period
099     * @param room examination room in questions
100     * @param conflicts set of conflicting assignments
101     */
102    public void computeConflicts(Exam exam, Collection<ExamPlacement> other, ExamRoom room, Set<ExamPlacement> conflicts) {
103        int total = exam.getSize();
104        boolean altSeating = exam.hasAltSeating();
105        List<ExamPlacement> adepts = new ArrayList<ExamPlacement>();
106        for (ExamPlacement x: other) {
107            if (x.variable().equals(exam)) continue;
108            // already a conflict -> do not count
109            if (conflicts.contains(x)) continue;
110            // already split into multiple rooms
111            if (x.getRoomPlacements().size() != 1) {
112                conflicts.add(x); continue;
113            }
114            // sharing not allowed between the pair
115            if (!canShareRoom(exam, x.variable())) {
116                conflicts.add(x); continue;
117            }
118            if (x.variable().hasAltSeating()) altSeating = true;
119            total += x.variable().getSize();
120            adepts.add(x);
121        }
122        
123        // fix the total size if needed
124        while (total > (altSeating ? room.getAltSize() : room.getSize()) && !adepts.isEmpty()) {
125            ExamPlacement x = ToolBox.random(adepts);
126            adepts.remove(x);
127            conflicts.add(x);
128            total -= x.variable().getSize();
129        }
130    }
131
132    
133    /**
134     * True if given two exams can share a room
135     * @param x1 first exam
136     * @param x2 second exam
137     * @return true if the two exams can share a room
138     */
139    public abstract boolean canShareRoom(Exam x1, Exam x2);
140    
141    /**
142     * Save sharing information (if needed) for a given exam
143     * @param exam exam in question
144     * @param element XML exam element to include sharing information
145     * @param idConvertor id converter
146     */
147    public void save(Exam exam, Element element, IdConvertor idConvertor) {}
148    
149    /**
150     * Load sharing information (if needed) for a given exam
151     * @param exam exam in question
152     * @param element XML exam element including sharing information
153     */
154    public void load(Exam exam, Element element) {}
155}