    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 * @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 = org.apache.logging.log4j.LogManager.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}