001    package net.sf.cpsolver.coursett.constraint;
002    
003    import java.util.Collection;
004    import java.util.Comparator;
005    import java.util.Enumeration;
006    import java.util.Iterator;
007    import java.util.Set;
008    import java.util.TreeSet;
009    import java.util.Vector;
010    
011    import net.sf.cpsolver.coursett.model.Lecture;
012    import net.sf.cpsolver.coursett.model.Placement;
013    import net.sf.cpsolver.ifs.model.Constraint;
014    import net.sf.cpsolver.ifs.model.Value;
015    
016    /**
017     * Class limit constraint.
018     * 
019     * @version
020     * CourseTT 1.1 (University Course Timetabling)<br>
021     * Copyright (C) 2006 Tomáš Müller<br>
022     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
023     * Lazenska 391, 76314 Zlin, Czech Republic<br>
024     * <br>
025     * This library is free software; you can redistribute it and/or
026     * modify it under the terms of the GNU Lesser General Public
027     * License as published by the Free Software Foundation; either
028     * version 2.1 of the License, or (at your option) any later version.
029     * <br><br>
030     * This library is distributed in the hope that it will be useful,
031     * but WITHOUT ANY WARRANTY; without even the implied warranty of
032     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
033     * Lesser General Public License for more details.
034     * <br><br>
035     * You should have received a copy of the GNU Lesser General Public
036     * License along with this library; if not, write to the Free Software
037     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
038     */
039    
040    public class ClassLimitConstraint extends Constraint {
041            private int iClassLimit = 0;
042            private Lecture iParent = null;
043            private String iName = null;
044            boolean iEnabled = true;
045            
046            private int iClassLimitDelta = 0;
047            
048            public ClassLimitConstraint(int classLimit, String name) {
049                    iClassLimit = classLimit;
050                    iName = name;
051            }
052            public ClassLimitConstraint(Lecture parent, String name) {
053                    iParent = parent;
054                    iName = name;
055            }
056            
057            public int getClassLimitDelta() { return iClassLimitDelta; }
058            public void setClassLimitDelta(int classLimitDelta) { iClassLimitDelta = classLimitDelta; }
059            
060            public int classLimit() { 
061                    return (iParent==null?iClassLimit+iClassLimitDelta:iParent.minClassLimit()+iClassLimitDelta);
062            }
063            public Lecture getParentLecture() {
064                    return iParent;
065            }
066            public int currentClassLimit(Value value, Set conflicts) {
067                    int limit = 0;
068                    for (Enumeration e=variables().elements();e.hasMoreElements();) {
069                            Lecture lecture = (Lecture)e.nextElement();
070                            limit += lecture.classLimit((Placement)value, conflicts);
071                    }
072                    return limit;
073            }
074            
075            public void computeConflicts(Value value, Set conflicts) {
076                    if (!iEnabled) return;
077                    int currentLimit = currentClassLimit(value, conflicts);
078                    int classLimit = classLimit();
079                    if (currentLimit<classLimit) {
080                            //System.out.println(getName()+"> "+currentLimit+"<"+classLimit+" ("+value+")");
081                            TreeSet adepts = new TreeSet(new ClassLimitComparator());
082                            computeAdepts(adepts, variables(), value, conflicts);
083                            addParentAdepts(adepts, iParent, value, conflicts);
084                            //System.out.println(" -- found "+adepts.size()+" adepts");
085                            for (Iterator i=adepts.iterator();i.hasNext();) {
086                                    Placement adept = (Placement)i.next();
087                                    //System.out.println("   -- selected "+adept);
088                                    conflicts.add(adept);
089                                    currentLimit = currentClassLimit(value, conflicts);
090                                    //System.out.println("   -- new current limit "+currentLimit);
091                                    if (currentLimit>=classLimit) break;
092                            }
093                            //System.out.println(" -- done (currentLimit="+currentLimit+", classLimit="+classLimit+")");
094                    }
095                    
096                    if (currentLimit<classLimit) conflicts.add(value);
097    
098                    if (iParent!=null && iParent.getClassLimitConstraint()!=null)
099                            iParent.getClassLimitConstraint().computeConflicts(value, conflicts);
100            }
101            
102            public void computeAdepts(Collection adepts, Vector variables, Value value, Set conflicts) {
103                    for (Enumeration e=variables.elements();e.hasMoreElements();) {
104                            Lecture lecture = (Lecture)e.nextElement();
105                            Placement placement = (Placement)lecture.getAssignment();
106                            if (placement!=null && !placement.equals(value) && !conflicts.contains(placement)) {
107                                    adepts.add(placement);
108                            }
109                            if (lecture.hasAnyChildren()) {
110                                    for (Enumeration f=lecture.getChildrenSubpartIds();f.hasMoreElements();) {
111                                            Long subpartId = (Long)f.nextElement();
112                                            computeAdepts(adepts, lecture.getChildren(subpartId), value, conflicts);
113                                    }
114                            }
115                                    
116                    }
117            }
118            
119            public void addParentAdepts(Collection adepts, Lecture parent, Value value, Set conflicts) {
120                    if (parent==null) return;
121                    Placement placement = (Placement)parent.getAssignment();
122                    if (placement!=null && !placement.equals(value) && !conflicts.contains(placement)) {
123                            adepts.add(placement);
124                    }
125                    addParentAdepts(adepts, parent.getParent(), value, conflicts);
126            }
127            
128            public boolean inConflict(Value value) {
129                    if (!iEnabled) return false;
130                    int currentLimit = currentClassLimit(value, null);
131                    int classLimit = classLimit();
132                    if (currentLimit<classLimit) return true;
133                    
134                    if (iParent!=null && iParent.getClassLimitConstraint()!=null)
135                            return iParent.getClassLimitConstraint().inConflict(value);
136                    
137                    return false;
138            }
139            
140            public String getName() { return iName; }
141            
142            private static class ClassLimitComparator implements Comparator {
143                    public int compare(Object o1, Object o2) {
144                            Placement p1 = (Placement)o1;
145                            Lecture l1 = (Lecture)p1.variable();
146                            Placement p2 = (Placement)o2;
147                            Lecture l2 = (Lecture)p2.variable();
148                            int cl1 = Math.min(l1.maxClassLimit(),(int)Math.ceil(p1.minRoomSize()/l1.roomToLimitRatio()));
149                            int cl2 = Math.min(l2.maxClassLimit(),(int)Math.ceil(p2.minRoomSize()/l2.roomToLimitRatio()));
150                            int cmp = -Double.compare(l1.maxAchievableClassLimit()-cl1,l2.maxAchievableClassLimit()-cl2);
151                            if (cmp!=0) return cmp;
152                            return l1.getClassId().compareTo(l2.getClassId());
153                    }
154            }
155            
156        public void setEnabled(boolean enabled) { 
157            iEnabled = enabled;
158        }
159        public boolean isEnabled() { return iEnabled; }
160        
161        public String toString() {
162            return "Class-limit "+getName(); 
163        }
164            
165    }