    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}