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 * @author  Tomáš Müller
020 * @version ExamTT 1.3 (Examination Timetabling)<br>
021 *          Copyright (C) 2007 - 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 class ExamPeriod implements Comparable<ExamPeriod> {
040    private int iIndex = -1;
041    private Long iId = null;
042    private String iTimeStr;
043    private String iDayStr;
044    private int iLength;
045    private int iDay, iTime;
046    private int iPenalty;
047    private Integer iStart = null;
048    private ExamPeriod iPrev, iNext;
049
050    /**
051     * Constructor
052     * 
053     * @param id
054     *            period unique identifier
055     * @param day
056     *            day (e.g., 07/12/10)
057     * @param time
058     *            (e.g., 8:00am-10:00am)
059     * @param length
060     *            length of period in minutes
061     * @param penalty
062     *            penalization of using this period
063     */
064    public ExamPeriod(Long id, String day, String time, int length, int penalty) {
065        iId = id;
066        iDayStr = day;
067        iTimeStr = time;
068        iLength = length;
069        iPenalty = penalty;
070    }
071
072    /** Period unique identifier 
073     * @return period unique id
074     **/
075    public Long getId() {
076        return iId;
077    }
078
079    /** Period unique identifier 
080     * @param id period unique id
081     **/
082    public void setId(Long id) {
083        iId = id;
084    }
085
086    /**
087     * Day string, e.g., 07/12/10
088     * @return day of the period
089     */
090    public String getDayStr() {
091        return iDayStr;
092    }
093
094    /**
095     * Day index
096     * 
097     * @return index of the day within all days that are used for examination
098     */
099    public int getDay() {
100        return iDay;
101    }
102
103    /**
104     * Time string, e.g., 8:00am-10:00am
105     * @return time of the period
106     */
107    public String getTimeStr() {
108        return iTimeStr;
109    }
110
111    /**
112     * Time index
113     * 
114     * @return index of the time within all time that are used for examination
115     *         on the same day
116     */
117    public int getTime() {
118        return iTime;
119    }
120
121    /**
122     * Length of period in minutes
123     * 
124     * @return period length
125     */
126    public int getLength() {
127        return iLength;
128    }
129
130    /**
131     * Period index
132     * 
133     * @return index of the period within all examination periods
134     */
135    public int getIndex() {
136        return iIndex;
137    }
138
139    /**
140     * Period weight to be used to penalize unpopular periods
141     * 
142     * @return period weight
143     */
144    public int getPenalty() {
145        return iPenalty;
146    }
147
148    /**
149     * Previous period
150     * 
151     * @return period with index equal to index-1, null if this is the first
152     *         period
153     */
154    public ExamPeriod prev() {
155        return iPrev;
156    }
157
158    /**
159     * Next period
160     * 
161     * @return period with index equal to index+1, null if this is the last
162     *         period
163     */
164    public ExamPeriod next() {
165        return iNext;
166    }
167
168    /**
169     * Set priod indexes (only to be used by
170     * {@link ExamModel#addPeriod(Long, String, String, int, int)})
171     * 
172     * @param index
173     *            period index
174     * @param day
175     *            day index
176     * @param time
177     *            time index
178     */
179    public void setIndex(int index, int day, int time) {
180        iIndex = index;
181        iDay = day;
182        iTime = time;
183    }
184
185    /**
186     * Set previous period (only to be used by
187     * {@link ExamModel#addPeriod(Long, String, String, int, int)})
188     * 
189     * @param prev
190     *            previous period
191     */
192    public void setPrev(ExamPeriod prev) {
193        iPrev = prev;
194    }
195
196    /**
197     * Set next period (only to be used by
198     * {@link ExamModel#addPeriod(Long, String, String, int, int)})
199     * 
200     * @param next
201     *            next period
202     */
203    public void setNext(ExamPeriod next) {
204        iNext = next;
205    }
206
207    /**
208     * String representation
209     * 
210     * @return day string time string
211     */
212    @Override
213    public String toString() {
214        return getDayStr() + " " + getTimeStr();
215    }
216
217    /**
218     * String representation for debuging purposes
219     * 
220     * @return day string time string (idx: index, day: day index, time: time
221     *         index, weight: period penalty, prev: previous period, next: next
222     *         period)
223     */
224    public String toDebugString() {
225        return getDayStr() + " " + getTimeStr() + " (idx:" + getIndex() + ", day:" + getDay() + ", time:" + getTime()
226                + ", penalty:" + getPenalty()
227                + (prev() == null ? "" : ", prev:" + prev().getDayStr() + " " + prev().getTimeStr() + ")")
228                + (next() == null ? "" : ", next:" + next().getDayStr() + " " + next().getTimeStr() + ")");
229    }
230
231    @Override
232    public int hashCode() {
233        return iIndex;
234    }
235
236    @Override
237    public boolean equals(Object o) {
238        if (o == null || !(o instanceof ExamPeriod))
239            return false;
240        return getIndex() == ((ExamPeriod) o).getIndex();
241    }
242
243    @Override
244    public int compareTo(ExamPeriod p) {
245        return Double.compare(getIndex(), p.getIndex());
246    }
247    
248    public Integer getStartTime() { return iStart; }
249    public void setStartTime(Integer startTime) { iStart = startTime; }
250    
251    /**
252     * Check if this period overlaps with the given period
253     * @param p other period
254     * @return true if the two periods overlap in time (considering period lengths)
255     */
256    public boolean hasIntersection(ExamPeriod p) {
257        if (getIndex() == p.getIndex() || getStartTime() == null || p.getStartTime() == null) return false;
258        return getDay() == p.getDay() && (getStartTime() + getLength() > p.getStartTime()) && (p.getStartTime() + p.getLength() > getStartTime());
259    }
260    
261    /**
262     * Check if the first exam assigned to this period overlaps with the second exam assigned in the given period period
263     * @param x1 exam of this period
264     * @param x2 the other exam (of the period p)
265     * @param p other period
266     * @return true if the two periods overlap in time (considering examination lengths)
267     */
268    public boolean hasIntersection(Exam x1, Exam x2, ExamPeriod p) {
269        if (getIndex() == p.getIndex() || getStartTime() == null || p.getStartTime() == null) return false;
270        return getDay() == p.getDay() && (getStartTime() + x1.getLength() > p.getStartTime()) && (p.getStartTime() + x2.getLength() > getStartTime());
271    }
272}