001package org.cpsolver.exam.model;
002
003/**
004 * Representation of an examination period. Examination timetabling model
005 * contains a list of non-overlapping examination periods. Each period has a
006 * day, starting time and a length (in minutes) defined. Each exam is to be
007 * assigned to one period that is available for the exam and that is of the same
008 * of greater length than the exam. <br>
009 * <br>
010 * A penalty weight ({@link ExamPeriod#getPenalty()}) can be assigned to each
011 * period. It is used to penalize unpopular examination times (e.g., evening or
012 * last-day). <br>
013 * <br>
014 * A list of periods is to be defined using
015 * {@link ExamModel#addPeriod(Long, String, String, int, int)}, inserting
016 * periods in the order of increasing days and times. <br>
017 * <br>
018 * 
019 * @version ExamTT 1.3 (Examination Timetabling)<br>
020 *          Copyright (C) 2007 - 2014 Tomáš Müller<br>
021 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
022 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
023 * <br>
024 *          This library is free software; you can redistribute it and/or modify
025 *          it under the terms of the GNU Lesser General Public License as
026 *          published by the Free Software Foundation; either version 3 of the
027 *          License, or (at your option) any later version. <br>
028 * <br>
029 *          This library is distributed in the hope that it will be useful, but
030 *          WITHOUT ANY WARRANTY; without even the implied warranty of
031 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
032 *          Lesser General Public License for more details. <br>
033 * <br>
034 *          You should have received a copy of the GNU Lesser General Public
035 *          License along with this library; if not see
036 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
037 */
038public class ExamPeriod implements Comparable<ExamPeriod> {
039    private int iIndex = -1;
040    private Long iId = null;
041    private String iTimeStr;
042    private String iDayStr;
043    private int iLength;
044    private int iDay, iTime;
045    private int iPenalty;
046    private Integer iStart = null;
047    private ExamPeriod iPrev, iNext;
048
049    /**
050     * Constructor
051     * 
052     * @param id
053     *            period unique identifier
054     * @param day
055     *            day (e.g., 07/12/10)
056     * @param time
057     *            (e.g., 8:00am-10:00am)
058     * @param length
059     *            length of period in minutes
060     * @param penalty
061     *            penalization of using this period
062     */
063    public ExamPeriod(Long id, String day, String time, int length, int penalty) {
064        iId = id;
065        iDayStr = day;
066        iTimeStr = time;
067        iLength = length;
068        iPenalty = penalty;
069    }
070
071    /** Period unique identifier 
072     * @return period unique id
073     **/
074    public Long getId() {
075        return iId;
076    }
077
078    /** Period unique identifier 
079     * @param id period unique id
080     **/
081    public void setId(Long id) {
082        iId = id;
083    }
084
085    /**
086     * Day string, e.g., 07/12/10
087     * @return day of the period
088     */
089    public String getDayStr() {
090        return iDayStr;
091    }
092
093    /**
094     * Day index
095     * 
096     * @return index of the day within all days that are used for examination
097     */
098    public int getDay() {
099        return iDay;
100    }
101
102    /**
103     * Time string, e.g., 8:00am-10:00am
104     * @return time of the period
105     */
106    public String getTimeStr() {
107        return iTimeStr;
108    }
109
110    /**
111     * Time index
112     * 
113     * @return index of the time within all time that are used for examination
114     *         on the same day
115     */
116    public int getTime() {
117        return iTime;
118    }
119
120    /**
121     * Length of period in minutes
122     * 
123     * @return period length
124     */
125    public int getLength() {
126        return iLength;
127    }
128
129    /**
130     * Period index
131     * 
132     * @return index of the period within all examination periods
133     */
134    public int getIndex() {
135        return iIndex;
136    }
137
138    /**
139     * Period weight to be used to penalize unpopular periods
140     * 
141     * @return period weight
142     */
143    public int getPenalty() {
144        return iPenalty;
145    }
146
147    /**
148     * Previous period
149     * 
150     * @return period with index equal to index-1, null if this is the first
151     *         period
152     */
153    public ExamPeriod prev() {
154        return iPrev;
155    }
156
157    /**
158     * Next period
159     * 
160     * @return period with index equal to index+1, null if this is the last
161     *         period
162     */
163    public ExamPeriod next() {
164        return iNext;
165    }
166
167    /**
168     * Set priod indexes (only to be used by
169     * {@link ExamModel#addPeriod(Long, String, String, int, int)})
170     * 
171     * @param index
172     *            period index
173     * @param day
174     *            day index
175     * @param time
176     *            time index
177     */
178    public void setIndex(int index, int day, int time) {
179        iIndex = index;
180        iDay = day;
181        iTime = time;
182    }
183
184    /**
185     * Set previous period (only to be used by
186     * {@link ExamModel#addPeriod(Long, String, String, int, int)})
187     * 
188     * @param prev
189     *            previous period
190     */
191    public void setPrev(ExamPeriod prev) {
192        iPrev = prev;
193    }
194
195    /**
196     * Set next period (only to be used by
197     * {@link ExamModel#addPeriod(Long, String, String, int, int)})
198     * 
199     * @param next
200     *            next period
201     */
202    public void setNext(ExamPeriod next) {
203        iNext = next;
204    }
205
206    /**
207     * String representation
208     * 
209     * @return day string time string
210     */
211    @Override
212    public String toString() {
213        return getDayStr() + " " + getTimeStr();
214    }
215
216    /**
217     * String representation for debuging purposes
218     * 
219     * @return day string time string (idx: index, day: day index, time: time
220     *         index, weight: period penalty, prev: previous period, next: next
221     *         period)
222     */
223    public String toDebugString() {
224        return getDayStr() + " " + getTimeStr() + " (idx:" + getIndex() + ", day:" + getDay() + ", time:" + getTime()
225                + ", penalty:" + getPenalty()
226                + (prev() == null ? "" : ", prev:" + prev().getDayStr() + " " + prev().getTimeStr() + ")")
227                + (next() == null ? "" : ", next:" + next().getDayStr() + " " + next().getTimeStr() + ")");
228    }
229
230    @Override
231    public int hashCode() {
232        return iIndex;
233    }
234
235    @Override
236    public boolean equals(Object o) {
237        if (o == null || !(o instanceof ExamPeriod))
238            return false;
239        return getIndex() == ((ExamPeriod) o).getIndex();
240    }
241
242    @Override
243    public int compareTo(ExamPeriod p) {
244        return Double.compare(getIndex(), p.getIndex());
245    }
246    
247    public Integer getStartTime() { return iStart; }
248    public void setStartTime(Integer startTime) { iStart = startTime; }
249    
250    /**
251     * Check if this period overlaps with the given period
252     * @param p other period
253     * @return true if the two periods overlap in time (considering period lengths)
254     */
255    public boolean hasIntersection(ExamPeriod p) {
256        if (getIndex() == p.getIndex() || getStartTime() == null || p.getStartTime() == null) return false;
257        return getDay() == p.getDay() && (getStartTime() + getLength() > p.getStartTime()) && (p.getStartTime() + p.getLength() > getStartTime());
258    }
259    
260    /**
261     * Check if the first exam assigned to this period overlaps with the second exam assigned in the given period period
262     * @param x1 exam of this period
263     * @param x2 the other exam (of the period p)
264     * @param p other period
265     * @return true if the two periods overlap in time (considering examination lengths)
266     */
267    public boolean hasIntersection(Exam x1, Exam x2, ExamPeriod p) {
268        if (getIndex() == p.getIndex() || getStartTime() == null || p.getStartTime() == null) return false;
269        return getDay() == p.getDay() && (getStartTime() + x1.getLength() > p.getStartTime()) && (p.getStartTime() + x2.getLength() > getStartTime());
270    }
271}