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