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