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