001package org.cpsolver.instructor.model;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.List;
006import java.util.Set;
007
008import org.cpsolver.coursett.Constants;
009import org.cpsolver.coursett.model.TimeLocation;
010import org.cpsolver.coursett.preference.PreferenceCombination;
011import org.cpsolver.coursett.preference.SumPreferenceCombination;
012import org.cpsolver.ifs.assignment.Assignment;
013import org.cpsolver.ifs.model.Variable;
014
015/**
016 * Teaching request. A set of sections of a course to be assigned to an instructor.
017 * Each teaching request has a teaching load. The maximal teaching load of an instructor
018 * cannot be breached.
019 * 
020 * @version IFS 1.3 (Instructor Sectioning)<br>
021 *          Copyright (C) 2016 Tomas Muller<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 TeachingRequest extends Variable<TeachingRequest, TeachingAssignment> {
040    private long iRequestId;
041    private int iIndex;
042    private Course iCourse;
043    private float iLoad;
044    private List<Section> iSections = new ArrayList<Section>();
045    private List<Preference<Attribute>> iAttributePreferences = new ArrayList<Preference<Attribute>>();
046    private List<Preference<Instructor>> iInstructorPreferences = new ArrayList<Preference<Instructor>>();
047
048    /**
049     * 
050     * @param requestId teaching request id
051     * @param index instructor index (if a class can be taught by multiple instructors, the index identifies the particular request)
052     * @param course course
053     * @param load teaching load
054     * @param sections list of sections
055     */
056    public TeachingRequest(long requestId, int index, Course course, float load, Collection<Section> sections) {
057        super();
058        iId = (requestId << 8) + index;
059        iRequestId = requestId;
060        iIndex = index;
061        iCourse = course;
062        iLoad = load;
063        iSections.addAll(sections);
064    }
065
066    /**
067     * Teaching request id that was provided in the constructor
068     * @return request id
069     */
070    public long getRequestId() {
071        return iRequestId;
072    }
073    
074    /**
075     * Instructor index that was provided in the constructor
076     * @return instructor index
077     */
078    public int getInstructorIndex() {
079        return iIndex;
080    }
081
082    @Override
083    public List<TeachingAssignment> values(Assignment<TeachingRequest, TeachingAssignment> assignment) {
084        List<TeachingAssignment> values = super.values(assignment);
085        if (values == null) {
086            values = new ArrayList<TeachingAssignment>();
087            for (Instructor instructor: ((InstructorSchedulingModel)getModel()).getInstructors()) {
088                if (instructor.canTeach(this)) {
089                    PreferenceCombination attributePref = getAttributePreference(instructor);
090                    if (attributePref.isProhibited()) continue;
091                    values.add(new TeachingAssignment(this, instructor, attributePref.getPreferenceInt()));
092                }
093            }
094            setValues(values);
095        }
096        return values;
097    }
098    
099    /**
100     * Return attribute preferences for this request
101     * @return attribute preferences
102     */
103    public List<Preference<Attribute>> getAttributePreferences() { return iAttributePreferences; }
104    
105    /**
106     * Add attribute preference
107     * @param pref attribute preference
108     */
109    public void addAttributePreference(Preference<Attribute> pref) { iAttributePreferences.add(pref); }
110    
111    /**
112     * Compute attribute preference for the given instructor and attribute type
113     * @param instructor an instructor
114     * @param type an attribute type
115     * @return combined preference using {@link Attribute.Type#isConjunctive()} and {@link Attribute.Type#isRequired()} properties
116     */
117    public int getAttributePreference(Instructor instructor, Attribute.Type type) {
118        Set<Attribute> attributes = instructor.getAttributes(type);
119        boolean hasReq = false, hasPref = false, needReq = false;
120        PreferenceCombination ret = new SumPreferenceCombination();
121        for (Preference<Attribute> pref: iAttributePreferences) {
122            if (!type.equals(pref.getTarget().getType())) continue;
123            if (pref.isRequired()) needReq = true;
124            if (attributes.contains(pref.getTarget())) {
125                if (pref.isProhibited()) return Constants.sPreferenceLevelProhibited;
126                else if (pref.isRequired()) hasReq = true;
127                else ret.addPreferenceInt(pref.getPreference());
128                hasPref = true;
129            } else {
130                if (pref.isRequired() && type.isConjunctive()) return Constants.sPreferenceLevelProhibited;
131            }
132        }
133        if (needReq && !hasReq) return Constants.sPreferenceLevelProhibited;
134        if (type.isRequired() && !hasPref) return Constants.sPreferenceLevelProhibited;
135        return ret.getPreferenceInt();
136    }
137    
138    /**
139     * Compute attribute preference for the given instructor
140     * @param instructor an instructor
141     * @return using {@link SumPreferenceCombination} for the preferences of each attribute type (using {@link TeachingRequest#getAttributePreference(Instructor, org.cpsolver.instructor.model.Attribute.Type)})
142     */
143    public PreferenceCombination getAttributePreference(Instructor instructor) {
144        PreferenceCombination preference = new SumPreferenceCombination();
145        for (Attribute.Type type: ((InstructorSchedulingModel)getModel()).getAttributeTypes())
146            preference.addPreferenceInt(getAttributePreference(instructor, type));
147        return preference;
148    }
149
150    /**
151     * Return instructor preferences for this request
152     * @return instructor preferences
153     */
154    public List<Preference<Instructor>> getInstructorPreferences() { return iInstructorPreferences; }
155    
156    /**
157     * Add instructor preference
158     * @param pref instructor preference
159     */
160    public void addInstructorPreference(Preference<Instructor> pref) { iInstructorPreferences.add(pref); }
161    
162    /**
163     * Return instructor preference for the given instructor
164     * @param instructor an instructor
165     * @return instructor preference for the given instructor
166     */
167    public Preference<Instructor> getInstructorPreference(Instructor instructor) {
168        boolean hasRequired = false;
169        for (Preference<Instructor> pref: iInstructorPreferences)
170            if (pref.isRequired()) { hasRequired = true; break; }
171        for (Preference<Instructor> pref: iInstructorPreferences)
172            if (pref.getTarget().equals(instructor)) {
173                if (hasRequired && !pref.isRequired()) continue;
174                return pref;
175            }
176        if (hasRequired)
177            return new Preference<Instructor>(instructor, Constants.sPreferenceLevelProhibited);
178        return new Preference<Instructor>(instructor, Constants.sPreferenceLevelNeutral);
179    }
180    
181    /**
182     * Course of the request that was provided in the constructor
183     * @return course of the request
184     */
185    public Course getCourse() {
186        return iCourse;
187    }
188
189    @Override
190    public String getName() {
191        return iCourse.getCourseName() + " " + getSections();
192    }
193    
194    /**
195     * Sections of the request that was provided in the constructor
196     * @return sections of the request
197     */
198    public List<Section> getSections() { return iSections; }
199
200    /**
201     * Return teaching load of the request
202     * @return teaching load
203     */
204    public float getLoad() { return iLoad; }
205    
206    /**
207     * Set teaching load of the request
208     * @param load teaching load
209     */
210    public void setLoad(float load) { iLoad = load; }
211
212    @Override
213    public String toString() {
214        return getName();
215    }
216    
217    /**
218     * Check if the given request fully share the common sections with this request  
219     * @param request the other teaching request
220     * @return true, if all common sections of this request are also present in the other request
221     */
222    public boolean sameCommon(TeachingRequest request) {
223        for (Section section: getSections())
224            if (section.isCommon() && !request.getSections().contains(section))
225                return false;
226        return true;
227    }
228    
229    /**
230     * Count the number of common sections that the given request share with this request
231     * @param request the other teaching request
232     * @return the number of shared common sections
233     */
234    public double nrSameLectures(TeachingRequest request) {
235        if (!sameCourse(request)) return 0.0;
236        double same = 0; int common = 0;
237        for (Section section: getSections())
238            if (section.isCommon()) {
239                common ++;
240                if (request.getSections().contains(section)) same++;
241            }
242        return (common == 0 ? 0.0 : same / common);
243    }
244
245    /**
246     * Check if this request and the given request are of the same course
247     * @param request the other teaching request
248     * @return true, if the course of the given request is the same as the course of this request
249     */
250    public boolean sameCourse(TeachingRequest request) {
251        return getCourse().equals(request.getCourse());
252    }
253
254    /**
255     * Check if this request overlaps with the given one
256     * @param request the other teaching request
257     * @return true, if there are two sections that are overlapping in time (that are not allowed to overlap)
258     */
259    public boolean overlaps(TeachingRequest request) {
260        for (Section section: getSections()) {
261            if (section.isAllowOverlap() || section.getTime() == null || request.getSections().contains(section)) continue;
262            for (Section other: request.getSections()) {
263                if (other.isAllowOverlap() || other.getTime() == null || getSections().contains(other)) continue;
264                if (section.getTime().hasIntersection(other.getTime())) return true;
265            }
266        }
267        return false;
268    }
269    
270    /**
271     * Count the number of (allowed) overlapping time slots between this request and the given one
272     * @param request the other teaching request
273     * @return the number of overlapping time slots
274     */
275    public int share(TeachingRequest request) {
276        int ret = 0;
277        for (Section section: getSections())
278            ret += section.share(request.getSections());
279        return ret;
280    }
281    
282    /**
283     * Count the number of overlapping time slots between this request and the given time
284     * @param time a time
285     * @return the number of overlapping time slots
286     */
287    public int share(TimeLocation time) {
288        int ret = 0;
289        for (Section section: getSections())
290            ret += section.share(time);
291        return ret;
292    }
293
294    /**
295     * Average value of the back-to-backs between this request and the given one
296     * @param request the other teaching request
297     * @param diffRoomWeight different room penalty
298     * @param diffTypeWeight different instructional type penalty
299     * @return average value of {@link Section#countBackToBacks(Collection, double, double)} between the two, common sections are ignored
300     */
301    public double countBackToBacks(TeachingRequest request, double diffRoomWeight, double diffTypeWeight) {
302        double b2b = 0.0;
303        int count = 0;
304        for (Section section: getSections()) {
305            if (!section.isCommon() || !sameCourse(request) || !request.getSections().contains(section)) {
306                b2b += section.countBackToBacks(request.getSections(), diffRoomWeight, diffTypeWeight);
307                count ++;
308            }
309        }
310        return (count == 0 ? 0.0 : b2b / count);
311    }
312    
313    @Override
314    public int hashCode() {
315        return new Long(iRequestId << 8 + iIndex).hashCode();
316    }
317    
318    @Override
319    public boolean equals(Object o) {
320        if (o == null || !(o instanceof TeachingRequest)) return false;
321        TeachingRequest tr = (TeachingRequest)o;
322        return getRequestId() == tr.getRequestId() && getInstructorIndex() == tr.getInstructorIndex();
323    }
324    
325    @Override
326    public void variableAssigned(Assignment<TeachingRequest, TeachingAssignment> assignment, long iteration, TeachingAssignment ta) {
327        super.variableAssigned(assignment, iteration, ta);
328        ta.getInstructor().getContext(assignment).assigned(assignment, ta);
329    }
330
331    @Override
332    public void variableUnassigned(Assignment<TeachingRequest, TeachingAssignment> assignment, long iteration, TeachingAssignment ta) {
333        super.variableUnassigned(assignment, iteration, ta);
334        ta.getInstructor().getContext(assignment).unassigned(assignment, ta);
335    }
336}