001package org.cpsolver.studentsct.extension;
002
003import java.util.HashMap;
004import java.util.HashSet;
005import java.util.Iterator;
006import java.util.Map;
007import java.util.Set;
008
009import org.apache.log4j.Logger;
010import org.cpsolver.coursett.Constants;
011import org.cpsolver.coursett.model.Placement;
012import org.cpsolver.coursett.model.RoomLocation;
013import org.cpsolver.coursett.model.TimeLocation;
014import org.cpsolver.ifs.assignment.Assignment;
015import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext;
016import org.cpsolver.ifs.assignment.context.CanInheritContext;
017import org.cpsolver.ifs.assignment.context.ExtensionWithContext;
018import org.cpsolver.ifs.model.ModelListener;
019import org.cpsolver.ifs.solver.Solver;
020import org.cpsolver.ifs.util.DataProperties;
021import org.cpsolver.ifs.util.DistanceMetric;
022import org.cpsolver.studentsct.StudentSectioningModel;
023import org.cpsolver.studentsct.StudentSectioningModel.StudentSectioningModelContext;
024import org.cpsolver.studentsct.model.CourseRequest;
025import org.cpsolver.studentsct.model.Enrollment;
026import org.cpsolver.studentsct.model.Request;
027import org.cpsolver.studentsct.model.Section;
028import org.cpsolver.studentsct.model.Student;
029
030
031/**
032 * This extension computes student distant conflicts. Two sections that are
033 * attended by the same student are considered in a distance conflict if they
034 * are back-to-back taught in locations that are two far away. This means that
035 * the (walking) distance in minutes between the two classes are longer than
036 * the break time of the earlier class. See {@link DistanceMetric} for more details.
037 * 
038 * @see TimeLocation
039 * @see Placement
040 * 
041 * <br>
042 * <br>
043 * 
044 * @version StudentSct 1.3 (Student Sectioning)<br>
045 *          Copyright (C) 2007 - 2014 Tomáš Müller<br>
046 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
047 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
048 * <br>
049 *          This library is free software; you can redistribute it and/or modify
050 *          it under the terms of the GNU Lesser General Public License as
051 *          published by the Free Software Foundation; either version 3 of the
052 *          License, or (at your option) any later version. <br>
053 * <br>
054 *          This library is distributed in the hope that it will be useful, but
055 *          WITHOUT ANY WARRANTY; without even the implied warranty of
056 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
057 *          Lesser General Public License for more details. <br>
058 * <br>
059 *          You should have received a copy of the GNU Lesser General Public
060 *          License along with this library; if not see
061 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
062 */
063
064public class DistanceConflict extends ExtensionWithContext<Request, Enrollment, DistanceConflict.DistanceConflictContext> implements ModelListener<Request, Enrollment>, CanInheritContext<Request, Enrollment, DistanceConflict.DistanceConflictContext> {
065    private static Logger sLog = Logger.getLogger(DistanceConflict.class);
066    /** Debug flag */
067    public static boolean sDebug = false;
068    private DistanceMetric iDistanceMetric = null;
069
070    /**
071     * Constructor. Beside of other thigs, this constructor also uses
072     * {@link StudentSectioningModel#setDistanceConflict(DistanceConflict)} to
073     * set the this instance to the model.
074     * 
075     * @param solver
076     *            constraint solver
077     * @param properties
078     *            configuration
079     */
080    public DistanceConflict(Solver<Request, Enrollment> solver, DataProperties properties) {
081        super(solver, properties);
082        if (solver != null) {
083            StudentSectioningModel model = (StudentSectioningModel) solver.currentSolution().getModel(); 
084            iDistanceMetric = model.getDistanceMetric();
085            model.setDistanceConflict(this);
086        }
087        if (iDistanceMetric == null)
088            iDistanceMetric = new DistanceMetric(properties);
089    }
090    
091    /**
092     * Alternative constructor (for online student sectioning)
093     * @param metrics distance metrics
094     * @param properties configuration
095     */
096    public DistanceConflict(DistanceMetric metrics, DataProperties properties) {
097        super(null, properties);
098        iDistanceMetric = metrics;
099    }
100
101    @Override
102    public String toString() {
103        return "DistanceConstraint";
104    }
105    
106    public DistanceMetric getDistanceMetric() {
107        return iDistanceMetric;
108    }
109        
110    private Map<Long, Map<Long, Integer>> iDistanceCache = new HashMap<Long, Map<Long,Integer>>();
111    protected synchronized int getDistanceInMinutes(RoomLocation r1, RoomLocation r2) {
112        if (r1.getId().compareTo(r2.getId()) > 0) return getDistanceInMinutes(r2, r1);
113        if (r1.getId().equals(r2.getId()) || r1.getIgnoreTooFar() || r2.getIgnoreTooFar())
114            return 0;
115        if (r1.getPosX() == null || r1.getPosY() == null || r2.getPosX() == null || r2.getPosY() == null)
116            return iDistanceMetric.getMaxTravelDistanceInMinutes();
117        Map<Long, Integer> other2distance = iDistanceCache.get(r1.getId());
118        if (other2distance == null) {
119            other2distance = new HashMap<Long, Integer>();
120            iDistanceCache.put(r1.getId(), other2distance);
121        }
122        Integer distance = other2distance.get(r2.getId());
123        if (distance == null) {
124            distance = iDistanceMetric.getDistanceInMinutes(r1.getId(), r1.getPosX(), r1.getPosY(), r2.getId(), r2.getPosX(), r2.getPosY());
125            other2distance.put(r2.getId(), distance);    
126        }
127        return distance;
128    }
129
130    protected int getDistanceInMinutes(Placement p1, Placement p2) {
131        if (p1.isMultiRoom()) {
132            if (p2.isMultiRoom()) {
133                int dist = 0;
134                for (RoomLocation r1 : p1.getRoomLocations()) {
135                    for (RoomLocation r2 : p2.getRoomLocations()) {
136                        dist = Math.max(dist, getDistanceInMinutes(r1, r2));
137                    }
138                }
139                return dist;
140            } else {
141                if (p2.getRoomLocation() == null)
142                    return 0;
143                int dist = 0;
144                for (RoomLocation r1 : p1.getRoomLocations()) {
145                    dist = Math.max(dist, getDistanceInMinutes(r1, p2.getRoomLocation()));
146                }
147                return dist;
148            }
149        } else if (p2.isMultiRoom()) {
150            if (p1.getRoomLocation() == null)
151                return 0;
152            int dist = 0;
153            for (RoomLocation r2 : p2.getRoomLocations()) {
154                dist = Math.max(dist, getDistanceInMinutes(p1.getRoomLocation(), r2));
155            }
156            return dist;
157        } else {
158            if (p1.getRoomLocation() == null || p2.getRoomLocation() == null)
159                return 0;
160            return getDistanceInMinutes(p1.getRoomLocation(), p2.getRoomLocation());
161        }
162    }
163    
164    /**
165     * Return true if the given two sections are in distance conflict. This
166     * means that the sections are back-to-back and that they are placed in
167     * locations that are two far.
168     * 
169     * @param student a student
170     * @param s1
171     *            a section
172     * @param s2
173     *            a section
174     * @return true, if the given sections are in a distance conflict
175     */
176    public boolean inConflict(Student student, Section s1, Section s2) {
177        if (s1.getPlacement() == null || s2.getPlacement() == null)
178            return false;
179        TimeLocation t1 = s1.getTime();
180        TimeLocation t2 = s2.getTime();
181        if (!t1.shareDays(t2) || !t1.shareWeeks(t2))
182            return false;
183        int a1 = t1.getStartSlot(), a2 = t2.getStartSlot();
184        if (student.isNeedShortDistances()) {
185            if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) {
186                if (a1 + t1.getNrSlotsPerMeeting() <= a2) {
187                    int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement());
188                    if (dist > Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength()))
189                        return true;
190                } else if (a2 + t2.getNrSlotsPerMeeting() <= a1) {
191                    int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement());
192                    if (dist > Constants.SLOT_LENGTH_MIN * (a1 - a2 - t2.getLength()))
193                        return true;
194                }
195            } else {
196                if (a1 + t1.getNrSlotsPerMeeting() == a2) {
197                    int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement());
198                    if (dist > 0) return true;
199                } else if (a2 + t2.getNrSlotsPerMeeting() == a1) {
200                    int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement());
201                    if (dist > 0) return true;
202                }
203            }
204            return false;
205        }
206        if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) {
207            if (a1 + t1.getNrSlotsPerMeeting() <= a2) {
208                int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement());
209                if (dist > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength()))
210                    return true;
211            } else if (a2 + t2.getNrSlotsPerMeeting() <= a1) {
212                int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement());
213                if (dist > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a1 - a2 - t2.getLength()))
214                    return true;
215            }
216        } else {
217            if (a1 + t1.getNrSlotsPerMeeting() == a2) {
218                int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement());
219                if (dist > t1.getBreakTime())
220                    return true;
221            } else if (a2 + t2.getNrSlotsPerMeeting() == a1) {
222                int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement());
223                if (dist > t2.getBreakTime())
224                    return true;
225            }
226        }
227        return false;
228    }
229
230    /**
231     * Return number of distance conflict of a (course) enrollment. It is the
232     * number of pairs of assignments of the enrollment that are in a distance
233     * conflict.
234     * 
235     * @param e1
236     *            an enrollment
237     * @return number of distance conflicts
238     */
239    public int nrConflicts(Enrollment e1) {
240        if (!e1.isCourseRequest())
241            return 0;
242        int cnt = 0;
243        for (Section s1 : e1.getSections()) {
244            for (Section s2 : e1.getSections()) {
245                if (s1.getId() < s2.getId() && inConflict(e1.getStudent(), s1, s2))
246                    cnt ++;
247            }
248        }
249        return cnt;
250    }
251
252    /**
253     * Return number of distance conflicts that are between two enrollments. It
254     * is the number of pairs of assignments of these enrollments that are in a
255     * distance conflict.
256     * 
257     * @param e1
258     *            an enrollment
259     * @param e2
260     *            an enrollment
261     * @return number of distance conflict between given enrollments
262     */
263    public int nrConflicts(Enrollment e1, Enrollment e2) {
264        if (!e1.isCourseRequest() || !e2.isCourseRequest() || !e1.getStudent().equals(e2.getStudent()))
265            return 0;
266        int cnt = 0;
267        for (Section s1 : e1.getSections()) {
268            for (Section s2 : e2.getSections()) {
269                if (inConflict(e1.getStudent(), s1, s2))
270                    cnt ++;
271            }
272        }
273        return cnt;
274    }
275
276    /**
277     * Return a set of distance conflicts ({@link Conflict} objects) of a
278     * (course) enrollment.
279     * 
280     * @param e1
281     *            an enrollment
282     * @return list of distance conflicts that are between assignment of the
283     *         given enrollment
284     */
285    public Set<Conflict> conflicts(Enrollment e1) {
286        Set<Conflict> ret = new HashSet<Conflict>();
287        if (!e1.isCourseRequest())
288            return ret;
289        for (Section s1 : e1.getSections()) {
290            for (Section s2 : e1.getSections()) {
291                if (s1.getId() < s2.getId() && inConflict(e1.getStudent(), s1, s2))
292                    ret.add(new Conflict(e1.getStudent(), e1, s1, e1, s2));
293            }
294        }
295        return ret;
296    }
297
298    /**
299     * Return a set of distance conflicts ({@link Conflict} objects) between
300     * given (course) enrollments.
301     * 
302     * @param e1
303     *            an enrollment
304     * @param e2
305     *            an enrollment
306     * @return list of distance conflicts that are between assignment of the
307     *         given enrollments
308     */
309    public Set<Conflict> conflicts(Enrollment e1, Enrollment e2) {
310        Set<Conflict> ret = new HashSet<Conflict>();
311        if (!e1.isCourseRequest() || !e2.isCourseRequest() || !e1.getStudent().equals(e2.getStudent()))
312            return ret;
313        for (Section s1 : e1.getSections()) {
314            for (Section s2 : e2.getSections()) {
315                if (inConflict(e1.getStudent(), s1, s2))
316                    ret.add(new Conflict(e1.getStudent(), e1, s1, e2, s2));
317            }
318        }
319        return ret;
320    }
321
322    /**
323     * The set of all conflicts ({@link Conflict} objects) of the given
324     * enrollment and other enrollments that are assignmed to the same student.
325     * @param assignment current assignment
326     * @param enrollment given enrollment
327     * @return set of all conflicts
328     */
329    public Set<Conflict> allConflicts(Assignment<Request, Enrollment> assignment, Enrollment enrollment) {
330        Set<Conflict> ret = conflicts(enrollment);
331        if (!enrollment.isCourseRequest())
332            return ret;
333        for (Request request : enrollment.getStudent().getRequests()) {
334            if (request.equals(enrollment.getRequest()) || assignment.getValue(request) == null)
335                continue;
336            ret.addAll(conflicts(enrollment, assignment.getValue(request)));
337        }
338        return ret;
339    }
340
341    /** Checks the counter counting all conflicts
342     * @param assignment current assignment
343     */
344    public void checkAllConflicts(Assignment<Request, Enrollment> assignment) {
345        getContext(assignment).checkAllConflicts(assignment);
346    }
347    
348    /** Actual number of all distance conflicts
349     * @param assignment current assignment
350     * @return cached number of all distance conflicts
351     **/
352    public int getTotalNrConflicts(Assignment<Request, Enrollment> assignment) {
353        return getContext(assignment).getTotalNrConflicts();
354    }
355    
356    /** Actual number of all distance conflicts of students that need short distances
357     * @param assignment current assignment
358     * @return cached number of all distance conflicts
359     **/
360    public int getTotalNrShortConflicts(Assignment<Request, Enrollment> assignment) {
361        return getContext(assignment).getTotalNrShortConflicts();
362    }
363
364    /**
365     * Compute the actual number of all distance conflicts. Should be equal to
366     * {@link DistanceConflict#getTotalNrConflicts(Assignment)}.
367     * @param assignment current assignment
368     * @return computed number of all distance conflicts
369     */
370    public int countTotalNrConflicts(Assignment<Request, Enrollment> assignment) {
371        int total = 0;
372        for (Request r1 : getModel().variables()) {
373            if (assignment.getValue(r1) == null || !(r1 instanceof CourseRequest))
374                continue;
375            Enrollment e1 = assignment.getValue(r1);
376            total += nrConflicts(e1);
377            for (Request r2 : r1.getStudent().getRequests()) {
378                Enrollment e2 = assignment.getValue(r2);
379                if (e2 == null || r1.getId() >= r2.getId() || !(r2 instanceof CourseRequest))
380                    continue;
381                total += nrConflicts(e1, e2);
382            }
383        }
384        return total;
385    }
386
387    /**
388     * Compute a set of all distance conflicts ({@link Conflict} objects).
389     * @param assignment current assignment
390     * @return computed set of all distance conflicts
391     */
392    public Set<Conflict> computeAllConflicts(Assignment<Request, Enrollment> assignment) {
393        Set<Conflict> ret = new HashSet<Conflict>();
394        for (Request r1 : getModel().variables()) {
395            Enrollment e1 = assignment.getValue(r1);
396            if (e1 == null || !(r1 instanceof CourseRequest))
397                continue;
398            ret.addAll(conflicts(e1));
399            for (Request r2 : r1.getStudent().getRequests()) {
400                Enrollment e2 = assignment.getValue(r2);
401                if (e2 == null || r1.getId() >= r2.getId() || !(r2 instanceof CourseRequest))
402                    continue;
403                ret.addAll(conflicts(e1, e2));
404            }
405        }
406        return ret;
407    }
408    
409    /**
410     * Return a set of all distance conflicts ({@link Conflict} objects).
411     * @param assignment current assignment
412     * @return cached set of all distance conflicts
413     */
414    public Set<Conflict> getAllConflicts(Assignment<Request, Enrollment> assignment) {
415        return getContext(assignment).getAllConflicts();
416    }
417
418    /**
419     * Called before a value is assigned to a variable.
420     */
421    @Override
422    public void beforeAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) {
423        getContext(assignment).beforeAssigned(assignment, iteration, value);
424    }
425
426    /**
427     * Called after a value is assigned to a variable.
428     */
429    @Override
430    public void afterAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) {
431        getContext(assignment).afterAssigned(assignment, iteration, value);
432    }
433
434    /**
435     * Called after a value is unassigned from a variable.
436     */
437    @Override
438    public void afterUnassigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) {
439        getContext(assignment).afterUnassigned(assignment, iteration, value);
440    }
441
442    /** A representation of a distance conflict */
443    public static class Conflict {
444        private Student iStudent;
445        private Section iS1, iS2;
446        private Enrollment iE1, iE2;
447        private int iHashCode;
448
449        /**
450         * Constructor
451         * 
452         * @param student
453         *            related student
454         * @param e1 first enrollment
455         * @param s1
456         *            first conflicting section
457         * @param e2 second enrollment
458         * @param s2
459         *            second conflicting section
460         */
461        public Conflict(Student student, Enrollment e1, Section s1, Enrollment e2, Section s2) {
462            iStudent = student;
463            if (s1.getId() < s2.getId()) {
464                iS1 = s1;
465                iS2 = s2;
466                iE1 = e1;
467                iE2 = e2;
468            } else {
469                iS1 = s2;
470                iS2 = s1;
471                iE1 = e2;
472                iE2 = e1;
473            }
474            iHashCode = (iStudent.getId() + ":" + iS1.getId() + ":" + iS2.getId()).hashCode();
475        }
476
477        /** Related student
478         * @return student
479         **/
480        public Student getStudent() {
481            return iStudent;
482        }
483
484        /** First section
485         * @return first section
486         **/
487        public Section getS1() {
488            return iS1;
489        }
490
491        /** Second section
492         * @return second section
493         **/
494        public Section getS2() {
495            return iS2;
496        }
497        
498        /** First request
499         * @return first request
500         **/
501        public Request getR1() {
502            return iE1.getRequest();
503        }
504        
505        /** Second request 
506         * @return second request
507         **/
508        public Request getR2() {
509            return iE2.getRequest();
510        }
511        
512        /** First enrollment 
513         * @return first enrollment
514         **/
515        public Enrollment getE1() {
516            return iE1;
517        }
518
519        /** Second enrollment 
520         * @return second enrollment
521         **/
522        public Enrollment getE2() {
523            return iE2;
524        }
525
526        @Override
527        public int hashCode() {
528            return iHashCode;
529        }
530
531        /** The distance between conflicting sections 
532         * @param dm distance metrics
533         * @return distance in meters between conflicting sections
534         **/
535        public double getDistance(DistanceMetric dm) {
536            return Placement.getDistanceInMeters(dm, getS1().getPlacement(), getS2().getPlacement());
537        }
538
539        @Override
540        public boolean equals(Object o) {
541            if (o == null || !(o instanceof Conflict)) return false;
542            Conflict c = (Conflict) o;
543            return getStudent().equals(c.getStudent()) && getS1().equals(c.getS1()) && getS2().equals(c.getS2());
544        }
545
546        @Override
547        public String toString() {
548            return getStudent() + ": " + getS1() + " -- " + getS2();
549        }
550    }
551    
552    public class DistanceConflictContext implements AssignmentConstraintContext<Request, Enrollment> {
553        private Set<Conflict> iAllConflicts = new HashSet<Conflict>();
554        private Request iOldVariable = null;
555        private Enrollment iUnassignedValue = null;
556
557        public DistanceConflictContext(Assignment<Request, Enrollment> assignment) {
558            iAllConflicts = computeAllConflicts(assignment);
559            StudentSectioningModelContext cx = ((StudentSectioningModel)getModel()).getContext(assignment);
560            for (Conflict c: iAllConflicts)
561                cx.add(assignment, c);
562        }
563        
564        public DistanceConflictContext(DistanceConflictContext parent) {
565            iAllConflicts.addAll(parent.iAllConflicts);
566            iOldVariable = parent.iOldVariable;
567            iUnassignedValue = parent.iUnassignedValue;
568        }
569        
570        /**
571         * Called before a value is assigned to a variable.
572         * @param assignment current assignment
573         * @param iteration current iteration
574         * @param value value to be assigned
575         */
576        public void beforeAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) {
577            if (value != null) {
578                Enrollment old = assignment.getValue(value.variable());
579                if (old != null) {
580                    unassigned(assignment, old);
581                    iUnassignedValue = old;
582                }
583                iOldVariable = value.variable();
584            }
585        }
586        
587        /**
588         * Called after a value is assigned to a variable.
589         * @param assignment current assignment
590         * @param iteration current iteration
591         * @param value value that was assigned
592         */
593        public void afterAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) {
594            iOldVariable = null;
595            iUnassignedValue = null;
596            if (value != null)
597                assigned(assignment, value);
598        }
599        
600        /**
601         * Called after a value is unassigned from a variable.
602         * @param assignment current assignment
603         * @param iteration current iteration
604         * @param value value to be unassigned
605         */
606        public void afterUnassigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) {
607            if (value != null && !value.equals(iUnassignedValue))
608                unassigned(assignment, value);
609        }
610
611        /**
612         * Called when a value is assigned to a variable. Internal number of
613         * distance conflicts is updated, see
614         * {@link DistanceConflict#getTotalNrConflicts(Assignment)}.
615         */
616        @Override
617        public void assigned(Assignment<Request, Enrollment> assignment, Enrollment value) {
618            StudentSectioningModelContext cx = ((StudentSectioningModel)getModel()).getContext(assignment);
619            for (Conflict c: allConflicts(assignment, value)) {
620                if (iAllConflicts.add(c))
621                    cx.add(assignment, c);
622            }
623            if (sDebug) {
624                sLog.debug("A:" + value.variable() + " := " + value);
625                int inc = nrConflicts(value);
626                if (inc != 0) {
627                    sLog.debug("-- DC+" + inc + " A: " + value.variable() + " := " + value);
628                    for (Iterator<Conflict> i = allConflicts(assignment, value).iterator(); i.hasNext();)
629                        sLog.debug("  -- " + i.next());
630                }
631            }
632        }
633
634        /**
635         * Called when a value is unassigned from a variable. Internal number of
636         * distance conflicts is updated, see
637         * {@link DistanceConflict#getTotalNrConflicts(Assignment)}.
638         */
639        @Override
640        public void unassigned(Assignment<Request, Enrollment> assignment, Enrollment value) {
641            if (value.variable().equals(iOldVariable))
642                return;
643            StudentSectioningModelContext cx = ((StudentSectioningModel)getModel()).getContext(assignment);
644            for (Conflict c: allConflicts(assignment, value)) {
645                if (iAllConflicts.remove(c))
646                    cx.remove(assignment, c);
647            }
648            if (sDebug) {
649                sLog.debug("U:" + value.variable() + " := " + value);
650                int dec = nrAllConflicts(assignment, value);
651                if (dec != 0) {
652                    sLog.debug("-- DC+" + dec + " U: " + value.variable() + " := " + value);
653                    Set<Conflict> confs = allConflicts(assignment, value);
654                    for (Iterator<Conflict> i = confs.iterator(); i.hasNext();)
655                        sLog.debug("  -- " + i.next());
656                }
657            }
658        }
659        
660        /** Checks the counter counting all conflicts
661         * @param assignment current assignment
662         **/
663        public void checkAllConflicts(Assignment<Request, Enrollment> assignment) {
664            Set<Conflict> allConfs = computeAllConflicts(assignment);
665            if (iAllConflicts.size() != allConfs.size()) {
666                sLog.error("Different number of conflicts " + iAllConflicts.size() + "!=" + allConfs.size());
667                for (Iterator<Conflict> i = allConfs.iterator(); i.hasNext();) {
668                    Conflict c = i.next();
669                    if (!iAllConflicts.contains(c))
670                        sLog.debug("  +add+ " + c);
671                }
672                for (Iterator<Conflict> i = iAllConflicts.iterator(); i.hasNext();) {
673                    Conflict c = i.next();
674                    if (!allConfs.contains(c))
675                        sLog.debug("  -rem- " + c);
676                }
677                iAllConflicts = allConfs;
678            }
679        }
680        
681        /** Actual number of all distance conflicts
682         * @return number of all distance conflicts
683         **/
684        public int getTotalNrConflicts() {
685            return iAllConflicts.size();
686        }
687        
688        /** Actual number of all distance conflicts of students that need short distances
689         * @return number of all distance conflicts
690         **/
691        public int getTotalNrShortConflicts() {
692            int ret = 0;
693            for (Conflict c: iAllConflicts)
694                if (c.getStudent().isNeedShortDistances()) ret ++;
695            return ret;
696        }
697        
698        
699        /**
700         * Return a set of all distance conflicts ({@link Conflict} objects).
701         * @return all distance conflicts
702         */
703        public Set<Conflict> getAllConflicts() {
704            return iAllConflicts;
705        }
706        
707        /**
708         * Total sum of all conflict of the given enrollment and other enrollments
709         * that are assigned to the same student.
710         * @param assignment current assignment
711         * @param enrollment given enrollment
712         * @return number of all conflict of the given enrollment
713         */
714        public int nrAllConflicts(Assignment<Request, Enrollment> assignment, Enrollment enrollment) {
715            if (!enrollment.isCourseRequest())
716                return 0;
717            int cnt = nrConflicts(enrollment);
718            Request old = iOldVariable;
719            for (Request request : enrollment.getStudent().getRequests()) {
720                if (request.equals(enrollment.getRequest()) || assignment.getValue(request) == null || request.equals(old))
721                    continue;
722                cnt += nrConflicts(enrollment, assignment.getValue(request));
723            }
724            return cnt;
725        }
726    }
727
728    @Override
729    public DistanceConflictContext createAssignmentContext(Assignment<Request, Enrollment> assignment) {
730        return new DistanceConflictContext(assignment);
731    }
732
733    @Override
734    public DistanceConflictContext inheritAssignmentContext(Assignment<Request, Enrollment> assignment, DistanceConflictContext parentContext) {
735        return new DistanceConflictContext(parentContext);
736    }
737}