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