    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}