001    package net.sf.cpsolver.coursett.constraint;
002    
003    import java.util.Set;
004    
005    import net.sf.cpsolver.coursett.model.Lecture;
006    import net.sf.cpsolver.coursett.model.Placement;
007    import net.sf.cpsolver.coursett.model.Student;
008    import net.sf.cpsolver.coursett.model.TimeLocation;
009    import net.sf.cpsolver.coursett.model.TimetableModel;
010    import net.sf.cpsolver.ifs.model.BinaryConstraint;
011    import net.sf.cpsolver.ifs.model.Value;
012    import net.sf.cpsolver.ifs.model.Variable;
013    import net.sf.cpsolver.ifs.util.ToolBox;
014    
015    /**
016     * Join student enrollment constraint.
017     * <br>
018     * This constraint is placed between all pairs of classes where there is at least one student attending both classes.
019     * It represents a number of student conflicts (number of joined enrollments), if the given two classes overlap in time.
020     * <br>
021     * Also, it dynamically maintains the counter of all student conflicts. It is a soft constraint.
022     *
023     *
024     * @version
025     * CourseTT 1.1 (University Course Timetabling)<br>
026     * Copyright (C) 2006 Tomáš Müller<br>
027     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
028     * Lazenska 391, 76314 Zlin, Czech Republic<br>
029     * <br>
030     * This library is free software; you can redistribute it and/or
031     * modify it under the terms of the GNU Lesser General Public
032     * License as published by the Free Software Foundation; either
033     * version 2.1 of the License, or (at your option) any later version.
034     * <br><br>
035     * This library is distributed in the hope that it will be useful,
036     * but WITHOUT ANY WARRANTY; without even the implied warranty of
037     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
038     * Lesser General Public License for more details.
039     * <br><br>
040     * You should have received a copy of the GNU Lesser General Public
041     * License along with this library; if not, write to the Free Software
042     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
043     */
044    
045    public class JenrlConstraint extends BinaryConstraint {
046        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(JenrlConstraint.class);
047        private double iJenrl = 0.0;
048        private int iNrStrudents = 0;
049        private boolean iAdded = false;
050        private boolean iAddedDistance = false;
051        
052        /** Constructor
053         */
054        public JenrlConstraint() {
055            super();
056        }
057    
058        public void computeConflicts(Value value, Set conflicts) {
059        }
060        
061        public boolean inConflict(Value value) {
062            return false;
063        }
064        
065        public boolean isConsistent(Value value1, Value value2) {
066            return true;
067        }
068        
069        public void unassigned(long iteration, Value value) {
070            super.unassigned(iteration, value);
071            if (iAdded) {
072                    ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().dec((long)Math.ceil(iJenrl));
073                if (areStudentConflictsHard())
074                    ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().dec((long)Math.ceil(iJenrl));
075                iAdded=false;
076                ((Lecture)first()).removeActiveJenrl(this);
077                ((Lecture)second()).removeActiveJenrl(this);
078            }
079            if (iAddedDistance) {
080                    ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().dec((long)Math.ceil(iJenrl));
081                    iAddedDistance = false;
082            }
083        }
084        
085        /** Returns true if the given placements are overlapping or they are back-to-back and too far for students.*/
086        public static boolean isInConflict(Placement p1, Placement p2) {
087            return isInConflict(p1, p2, true);
088        }
089        
090       public static boolean isInConflict(Placement p1, Placement p2, boolean useDistances) {
091           if (p1==null || p2==null) return false;
092           TimeLocation t1=p1.getTimeLocation(), t2=p2.getTimeLocation();
093           if (!t1.shareDays(t2)) return false;
094           if (!t1.shareWeeks(t2)) return false;
095           if (t1.shareHours(t2)) return true;
096           if (!useDistances) return false;
097           int s1 = t1.getStartSlot(), s2 = t2.getStartSlot();
098           if (s1+t1.getNrSlotsPerMeeting()!=s2 &&
099               s2+t2.getNrSlotsPerMeeting()!=s1) return false;
100           double distance = Placement.getDistance(p1,p2);
101           TimetableModel m = (TimetableModel)p1.variable().getModel();
102           if (m==null) {
103               if (distance <= 67.0) return false;
104               if (distance <= 100.0 && (
105                               (t1.getLength()==18 && s1+t1.getLength()==s2) ||
106                               (t2.getLength()==18 && s2+t2.getLength()==s1)))
107                       return false;
108               return true;
109           } else {
110               if (distance <= m.getStudentDistanceLimit()) return false;
111               if (distance <= m.getStudentDistanceLimit75min() && (
112                               (t1.getLength()==18 && s1+t1.getLength()==s2) ||
113                               (t2.getLength()==18 && s2+t2.getLength()==s1)))
114                       return false;
115               return true;
116           }
117       }
118    
119       public void assigned(long iteration, Value value) {
120           super.assigned(iteration, value);
121           if (second()==null || first().getAssignment()==null || second().getAssignment()==null) return;
122            //if (v1.getInitialAssignment()!=null && v2.getInitialAssignment()!=null && v1.getAssignment().equals(v1.getInitialAssignment()) && v2.getAssignment().equals(v2.getInitialAssignment())) return;
123            if (isInConflict((Placement)first().getAssignment(),(Placement)second().getAssignment())) {
124                iAdded=true;
125                ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().inc((long)Math.ceil(iJenrl));
126                if (areStudentConflictsHard())
127                    ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().inc((long)Math.ceil(iJenrl));
128                if (areStudentConflictsDistance()) {
129                    ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().inc((long)Math.ceil(iJenrl));
130                    iAddedDistance = true;
131                }
132                ((Lecture)first()).addActiveJenrl(this);
133                ((Lecture)second()).addActiveJenrl(this);
134            }
135        }
136    
137       /** Number of joined enrollments if the given value is assigned to the given variable */
138        public long jenrl(Variable variable, Value value) {
139            Lecture anotherLecture = (Lecture)(first().equals(variable)?second():first());
140            if (anotherLecture.getAssignment()==null) return 0;
141            Lecture lecture = (Lecture) variable;
142            return (isInConflict((Placement)anotherLecture.getAssignment(),(Placement)value)?(long)Math.ceil(iJenrl):0);
143        }
144    
145        /** True if the given two lectures overlap in time */
146        public boolean isInConflict() {
147            return iAdded;
148        }
149    
150        /** True if the given two lectures overlap in time */
151        public boolean isInConflictPrecise() {
152            return isInConflict((Placement)first().getAssignment(),(Placement)second().getAssignment());
153        }
154    
155        /** Increment the number of joined enrollments (during student final sectioning) */
156        public void incJenrl(Student student ) {
157            if (iAdded) {
158                    ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().dec((long)Math.ceil(iJenrl));
159                    if (areStudentConflictsHard())
160                            ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().dec((long)Math.ceil(iJenrl));
161                if (iAddedDistance)
162                    ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().dec((long)Math.ceil(iJenrl));
163            }
164            iJenrl+=student.getJenrlWeight((Lecture)first(),(Lecture)second());
165            iNrStrudents++;
166            if (iAdded) {
167                    ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().inc((long)Math.ceil(iJenrl));
168                    if (areStudentConflictsHard())
169                            ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().inc((long)Math.ceil(iJenrl));
170                if (iAddedDistance)
171                    ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().inc((long)Math.ceil(iJenrl));
172            }
173        }
174        
175        public double getJenrlWeight(Student student) {
176            return student.getJenrlWeight((Lecture)first(),(Lecture)second());
177        }
178        
179        /** Decrement the number of joined enrollments (during student final sectioning) */
180        public void decJenrl(Student student) {
181            if (iAdded) {
182                    ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().dec((long)Math.ceil(iJenrl));
183                    if (areStudentConflictsHard())
184                            ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().dec((long)Math.ceil(iJenrl));
185                if (iAddedDistance)
186                    ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().dec((long)Math.ceil(iJenrl));
187            }
188            iJenrl-=student.getJenrlWeight((Lecture)first(),(Lecture)second());
189            iNrStrudents--;
190            if (iAdded) {
191                    ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().inc((long)Math.ceil(iJenrl));
192                    if (areStudentConflictsHard())
193                            ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().inc((long)Math.ceil(iJenrl));
194                if (iAddedDistance)
195                    ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().inc((long)Math.ceil(iJenrl));
196            }
197        }
198    
199        /** Number of joined enrollments (during student final sectioning) */
200        public long getJenrl() { return (long)Math.ceil(iJenrl); }
201        public int getNrStudents() { return iNrStrudents; }
202        public boolean isHard() { return false; }
203        
204        public String toString() {
205            return "Joint Enrollment between "+first().getName()+" and "+second().getName();
206        }
207        
208        public boolean areStudentConflictsHard() {
209            return ((Lecture)first()).areStudentConflictsHard((Lecture)second());
210        }
211        
212        public boolean areStudentConflictsDistance() {
213            return !((Placement)first().getAssignment()).getTimeLocation().hasIntersection(((Placement)second().getAssignment()).getTimeLocation());
214        }
215           
216        public boolean areStudentConflictsDistance(Value value) {
217            Placement first = (Placement)(first().equals(value.variable())?value:first().getAssignment());
218            Placement second = (Placement)(second().equals(value.variable())?value:second().getAssignment());
219            if (first==null || second==null) return false;
220            return !first.getTimeLocation().hasIntersection(second.getTimeLocation());
221        }
222        
223        public boolean isOfTheSameProblem() {
224            Lecture first = (Lecture)first();
225            Lecture second = (Lecture)second();
226            return ToolBox.equals(first.getSolverGroupId(),second.getSolverGroupId());
227        }
228    
229    }