001 package net.sf.cpsolver.coursett.model; 002 003 import java.text.SimpleDateFormat; 004 import java.util.BitSet; 005 import java.util.Enumeration; 006 import java.util.Locale; 007 008 import net.sf.cpsolver.coursett.Constants; 009 import net.sf.cpsolver.ifs.util.ToolBox; 010 011 /** 012 * Time part of placement. 013 * 014 * @version 015 * CourseTT 1.1 (University Course Timetabling)<br> 016 * Copyright (C) 2006 Tomáš Müller<br> 017 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 018 * Lazenska 391, 76314 Zlin, Czech Republic<br> 019 * <br> 020 * This library is free software; you can redistribute it and/or 021 * modify it under the terms of the GNU Lesser General Public 022 * License as published by the Free Software Foundation; either 023 * version 2.1 of the License, or (at your option) any later version. 024 * <br><br> 025 * This library is distributed in the hope that it will be useful, 026 * but WITHOUT ANY WARRANTY; without even the implied warranty of 027 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 028 * Lesser General Public License for more details. 029 * <br><br> 030 * You should have received a copy of the GNU Lesser General Public 031 * License along with this library; if not, write to the Free Software 032 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 033 */ 034 035 public class TimeLocation { 036 private static SimpleDateFormat sDateFormatShort = new SimpleDateFormat("MM/dd", Locale.US); 037 private int iStartSlot; 038 039 private int iPreference; 040 private double iNormalizedPreference; 041 042 private Long iTimePatternId = null; 043 private int iHashCode; 044 045 private int iDayCode; 046 private int iLength; 047 private int iNrMeetings; 048 private int iBreakTime; 049 050 private BitSet iWeekCode; 051 private Long iDatePatternId = null; 052 private String iDatePatternName = null; 053 054 /** Constructor 055 * @param dayCode days (combination of 1 for Monday, 2 for Tuesday, ...) 056 * @param startTime start slot 057 * @param length number of slots 058 * @param pref time preference 059 */ 060 public TimeLocation(int dayCode, int startTime, int length, int pref, double normPref, Long datePatternId, String datePatternName, BitSet weekCode, int breakTime) { 061 iPreference = pref; 062 iNormalizedPreference = normPref; 063 iStartSlot = startTime; 064 iDayCode = dayCode; 065 iLength = length; 066 iBreakTime = breakTime; 067 iNrMeetings = 0; 068 for (int i=0;i<Constants.DAY_CODES.length;i++) { 069 if ((iDayCode & Constants.DAY_CODES[i])==0) continue; 070 iNrMeetings++; 071 } 072 iHashCode = combine(combine(iDayCode, iStartSlot),iLength); 073 iDatePatternName = datePatternName; 074 iWeekCode = weekCode; 075 iDatePatternId = datePatternId; 076 if (iDatePatternName==null) iDatePatternName = "not set"; 077 if (iWeekCode==null) { 078 iWeekCode = new BitSet(366); 079 for (int i=0;i<=365;i++) 080 iWeekCode.set(i); 081 } 082 } 083 084 /** Number of meetings */ 085 public int getNrMeetings() { 086 return iNrMeetings; 087 } 088 089 public int getBreakTime() { 090 return iBreakTime; 091 } 092 093 private static int combine(int a, int b) { 094 int ret = 0; 095 for (int i=0;i<15;i++) ret = ret | ((a & (1<<i))<<i) | ((b & (1<<i))<<(i+1)); 096 return ret; 097 } 098 099 /** Days (combination of 1 for Monday, 2 for Tuesday, ...) */ 100 public int getDayCode() { return iDayCode; } 101 /** Days for printing purposes */ 102 public String getDayHeader() { 103 StringBuffer sb = new StringBuffer(); 104 for (int i=0;i<Constants.DAY_CODES.length;i++) 105 if ((iDayCode & Constants.DAY_CODES[i])!=0) 106 sb.append(Constants.DAY_NAMES_SHORT[i]); 107 return sb.toString(); 108 } 109 /** Start time for printing purposes */ 110 public String getStartTimeHeader() { 111 int min = iStartSlot * Constants.SLOT_LENGTH_MIN + Constants.FIRST_SLOT_TIME_MIN; 112 int h = min/60; 113 int m = min%60; 114 return (h>12?h-12:h)+":"+(m<10?"0":"")+m+(h>=12?"p":"a"); 115 } 116 /** End time for printing purposes */ 117 public String getEndTimeHeader() { 118 int min = (iStartSlot + iLength) * Constants.SLOT_LENGTH_MIN + Constants.FIRST_SLOT_TIME_MIN - getBreakTime(); 119 int m = min % 60; 120 int h = min / 60; 121 return (h>12?h-12:h)+":"+(m<10?"0":"")+m+(h>=12?"p":"a"); 122 } 123 /** End time for printing purposes */ 124 public String getEndTimeHeaderNoAdj() { 125 int min = (iStartSlot + iLength) * Constants.SLOT_LENGTH_MIN + Constants.FIRST_SLOT_TIME_MIN; 126 int m = min % 60; 127 int h = min / 60; 128 return (h>12?h-12:h)+":"+(m<10?"0":"")+m+(h>=12?"p":"a"); 129 } 130 /** Start slot */ 131 public int getStartSlot() { return iStartSlot; } 132 /** Used slots in a day (combination of 1..first, 2..second,...) */ 133 134 /** true if days overlap */ 135 public boolean shareDays(TimeLocation anotherLocation) { 136 return ((iDayCode & anotherLocation.iDayCode)!=0); 137 } 138 /** number of overlapping days */ 139 public int nrSharedDays(TimeLocation anotherLocation) { 140 int ret=0; 141 for (int i=0;i<Constants.NR_DAYS;i++) { 142 if ((iDayCode & Constants.DAY_CODES[i])==0) continue; 143 if ((anotherLocation.iDayCode & Constants.DAY_CODES[i])==0) continue; 144 ret++; 145 } 146 return ret; 147 } 148 /** true if hours overlap */ 149 public boolean shareHours(TimeLocation anotherLocation) { 150 return (iStartSlot+iLength > anotherLocation.iStartSlot) && (anotherLocation.iStartSlot+anotherLocation.iLength > iStartSlot); 151 } 152 /** number of overlapping days */ 153 public int nrSharedHours(TimeLocation anotherLocation) { 154 int end = Math.min(iStartSlot+iLength, anotherLocation.iStartSlot+anotherLocation.iLength); 155 int start = Math.max(iStartSlot, anotherLocation.iStartSlot); 156 return (end<start?0:end-start); 157 } 158 /** true if weeks overlap */ 159 public boolean shareWeeks(TimeLocation anotherLocation) { 160 return iWeekCode.intersects(anotherLocation.iWeekCode); 161 } 162 /** true if weeks overlap */ 163 public boolean shareWeeks(BitSet weekCode) { 164 return iWeekCode.intersects(weekCode); 165 } 166 public boolean hasDay(int day) { 167 return iWeekCode.get(day); 168 } 169 /** true if overlap */ 170 public boolean hasIntersection(TimeLocation anotherLocation) { 171 return shareDays(anotherLocation) && shareHours(anotherLocation) && shareWeeks(anotherLocation); 172 } 173 174 /** Used slots */ 175 public IntEnumeration getSlots() { return new SlotsEnum(); } 176 /** Used start slots (for each meeting) */ 177 public IntEnumeration getStartSlots() { return new StartSlotsEnum(); } 178 /** Days */ 179 public IntEnumeration getDays() { return new DaysEnum(); } 180 public int[] getDaysArray() { 181 int[] days = new int[getNrMeetings()]; 182 int i = 0; 183 for (IntEnumeration e=getDays();e.hasMoreElements();) 184 days[i++] = e.nextInt(); 185 return days; 186 } 187 188 /** Text representation */ 189 public String getName() { return getDayHeader()+" "+getStartTimeHeader(); } 190 public String getLongName() { return getDayHeader()+" "+getStartTimeHeader()+" - "+getEndTimeHeader()+" "+getDatePatternName(); } 191 public String getLongNameNoAdj() { return getDayHeader()+" "+getStartTimeHeader()+" - "+getEndTimeHeaderNoAdj()+" "+getDatePatternName(); } 192 /** Preference */ 193 public int getPreference() { return iPreference; } 194 public void setPreference(int preference) { iPreference = preference; } 195 /** Length */ 196 public int getLength() { return iLength; } 197 /** Length */ 198 public int getNrSlotsPerMeeting() { return iLength; } 199 /** Normalized preference */ 200 public double getNormalizedPreference() { return iNormalizedPreference;} 201 public void setNormalizedPreference(double normalizedPreference) { iNormalizedPreference = normalizedPreference; } 202 /** Time pattern model (can be null) */ 203 public Long getTimePatternId() { return iTimePatternId; } 204 public Long getDatePatternId() { return iDatePatternId; } 205 public void setTimePatternId(Long timePatternId) { iTimePatternId = timePatternId; } 206 public BitSet getWeekCode() { return iWeekCode; } 207 public String getDatePatternName() { return iDatePatternName; } 208 public void setDatePattern(Long datePatternId, String datePatternName, BitSet weekCode) { 209 iDatePatternId = datePatternId; 210 iDatePatternName = datePatternName; 211 iWeekCode = weekCode; 212 } 213 214 public String toString() { return getName()+" ("+iNormalizedPreference+")"; } 215 public int hashCode() { 216 return iHashCode; 217 } 218 219 /** Integer enumeration */ 220 public interface IntEnumeration extends Enumeration { 221 public int nextInt(); 222 } 223 224 private class StartSlotsEnum implements IntEnumeration { 225 int day = -1; 226 boolean hasNext = false; 227 private StartSlotsEnum() { 228 hasNext = nextDay(); 229 } 230 boolean nextDay() { 231 do { 232 day++; 233 if (day>=Constants.DAY_CODES.length) return false; 234 } while ((Constants.DAY_CODES[day]&iDayCode)==0); 235 return true; 236 } 237 public boolean hasMoreElements() { 238 return hasNext; 239 } 240 public Object nextElement() { 241 return new Integer(nextInt()); 242 } 243 public int nextInt() { 244 int slot = (day*Constants.SLOTS_PER_DAY)+iStartSlot; 245 hasNext = nextDay(); 246 return slot; 247 } 248 } 249 private class DaysEnum extends StartSlotsEnum { 250 private DaysEnum() { 251 super(); 252 } 253 public int nextInt() { 254 int ret = day; 255 hasNext = nextDay(); 256 return ret; 257 } 258 } 259 private class SlotsEnum extends StartSlotsEnum { 260 int pos = 0; 261 private SlotsEnum() { 262 super(); 263 } 264 private boolean nextSlot() { 265 if (pos+1<iLength) { 266 pos++; return true; 267 } 268 if (nextDay()) { 269 pos = 0; return true; 270 } 271 return false; 272 } 273 public int nextInt() { 274 int slot = (day*Constants.SLOTS_PER_DAY)+iStartSlot+pos; 275 hasNext = nextSlot(); 276 return slot; 277 } 278 } 279 280 public boolean equals(Object o) { 281 if (o==null || !(o instanceof TimeLocation)) return false; 282 TimeLocation t = (TimeLocation)o; 283 if (getStartSlot()!=t.getStartSlot()) return false; 284 if (getLength()!=t.getLength()) return false; 285 if (getDayCode()!=t.getDayCode()) return false; 286 return ToolBox.equals(getTimePatternId(),t.getTimePatternId()) && ToolBox.equals(getDatePatternId(),t.getDatePatternId()); 287 } 288 289 public int getNrWeeks() { 290 return getNrWeeks(0, iWeekCode.size()-1); 291 } 292 293 public int getNrWeeks(int startDay, int endDay) { 294 /* 295 BitSet x = new BitSet(1+(endDay-startDay)/Constants.NR_DAYS); 296 for (int i=iWeekCode.nextSetBit(startDay); i<=endDay && i>=0; i=iWeekCode.nextSetBit(i+1)) 297 x.set((i-startDay)/Constants.NR_DAYS); 298 return x.cardinality(); 299 */ 300 int card = iWeekCode.get(startDay, endDay).cardinality(); 301 if (card==0) return 0; 302 if (card<=7) return 1; 303 return (5+card)/6; 304 } 305 }