001 package net.sf.cpsolver.coursett.model; 002 003 import java.util.BitSet; 004 import java.util.Enumeration; 005 006 import net.sf.cpsolver.coursett.Constants; 007 import net.sf.cpsolver.ifs.util.ToolBox; 008 009 /** 010 * Time part of placement. 011 * 012 * @version CourseTT 1.2 (University Course Timetabling)<br> 013 * Copyright (C) 2006 - 2010 Tomas Muller<br> 014 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 015 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 016 * <br> 017 * This library is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser General Public License as 019 * published by the Free Software Foundation; either version 3 of the 020 * License, or (at your option) any later version. <br> 021 * <br> 022 * This library is distributed in the hope that it will be useful, but 023 * WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 025 * Lesser General Public License for more details. <br> 026 * <br> 027 * You should have received a copy of the GNU Lesser General Public 028 * License along with this library; if not see 029 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 030 */ 031 032 public class TimeLocation { 033 private int iStartSlot; 034 035 private int iPreference; 036 private double iNormalizedPreference; 037 038 private Long iTimePatternId = null; 039 private int iHashCode; 040 041 private int iDayCode; 042 private int iLength; 043 private int iNrMeetings; 044 private int iBreakTime; 045 046 private BitSet iWeekCode; 047 private Long iDatePatternId = null; 048 private String iDatePatternName = null; 049 private int iDatePreference; 050 051 /** 052 * Constructor 053 * 054 * @param dayCode 055 * days (combination of 1 for Monday, 2 for Tuesday, ...) 056 * @param startTime 057 * start slot 058 * @param length 059 * number of slots 060 * @param pref 061 * time preference 062 */ 063 public TimeLocation(int dayCode, int startTime, int length, int pref, double normPref, int datePatternPreference, 064 Long datePatternId, String datePatternName, BitSet weekCode, int breakTime) { 065 iPreference = pref; 066 iNormalizedPreference = normPref; 067 iStartSlot = startTime; 068 iDayCode = dayCode; 069 iLength = length; 070 iBreakTime = breakTime; 071 iNrMeetings = 0; 072 for (int i = 0; i < Constants.DAY_CODES.length; i++) { 073 if ((iDayCode & Constants.DAY_CODES[i]) == 0) 074 continue; 075 iNrMeetings++; 076 } 077 iHashCode = combine(combine(iDayCode, iStartSlot), iLength); 078 iDatePatternName = datePatternName; 079 iWeekCode = weekCode; 080 iDatePatternId = datePatternId; 081 if (iDatePatternName == null) 082 iDatePatternName = "not set"; 083 iDatePreference = datePatternPreference; 084 if (iWeekCode == null) { 085 iWeekCode = new BitSet(366); 086 for (int i = 0; i <= 365; i++) 087 iWeekCode.set(i); 088 } 089 } 090 091 public TimeLocation(int dayCode, int startTime, int length, int pref, double normPref, Long datePatternId, 092 String datePatternName, BitSet weekCode, int breakTime) { 093 this(dayCode, startTime, length, pref, normPref, 0, datePatternId, datePatternName, weekCode, breakTime); 094 } 095 096 /** Number of meetings */ 097 public int getNrMeetings() { 098 return iNrMeetings; 099 } 100 101 public int getBreakTime() { 102 return iBreakTime; 103 } 104 105 public void setBreakTime(int breakTime) { 106 iBreakTime = breakTime; 107 } 108 109 private static int combine(int a, int b) { 110 int ret = 0; 111 for (int i = 0; i < 15; i++) 112 ret = ret | ((a & (1 << i)) << i) | ((b & (1 << i)) << (i + 1)); 113 return ret; 114 } 115 116 /** Days (combination of 1 for Monday, 2 for Tuesday, ...) */ 117 public int getDayCode() { 118 return iDayCode; 119 } 120 121 /** Days for printing purposes */ 122 public String getDayHeader() { 123 StringBuffer sb = new StringBuffer(); 124 for (int i = 0; i < Constants.DAY_CODES.length; i++) 125 if ((iDayCode & Constants.DAY_CODES[i]) != 0) 126 sb.append(Constants.DAY_NAMES_SHORT[i]); 127 return sb.toString(); 128 } 129 130 /** Start time for printing purposes */ 131 public String getStartTimeHeader() { 132 int min = iStartSlot * Constants.SLOT_LENGTH_MIN + Constants.FIRST_SLOT_TIME_MIN; 133 int h = min / 60; 134 int m = min % 60; 135 return (h > 12 ? h - 12 : h) + ":" + (m < 10 ? "0" : "") + m + (h >= 12 ? "p" : "a"); 136 } 137 138 /** End time for printing purposes */ 139 public String getEndTimeHeader() { 140 int min = (iStartSlot + iLength) * Constants.SLOT_LENGTH_MIN + Constants.FIRST_SLOT_TIME_MIN - getBreakTime(); 141 int m = min % 60; 142 int h = min / 60; 143 return (h > 12 ? h - 12 : h) + ":" + (m < 10 ? "0" : "") + m + (h >= 12 ? "p" : "a"); 144 } 145 146 /** End time for printing purposes */ 147 public String getEndTimeHeaderNoAdj() { 148 int min = (iStartSlot + iLength) * Constants.SLOT_LENGTH_MIN + Constants.FIRST_SLOT_TIME_MIN; 149 int m = min % 60; 150 int h = min / 60; 151 return (h > 12 ? h - 12 : h) + ":" + (m < 10 ? "0" : "") + m + (h >= 12 ? "p" : "a"); 152 } 153 154 /** Start slot */ 155 public int getStartSlot() { 156 return iStartSlot; 157 } 158 159 /** Used slots in a day (combination of 1..first, 2..second,...) */ 160 161 /** true if days overlap */ 162 public boolean shareDays(TimeLocation anotherLocation) { 163 return ((iDayCode & anotherLocation.iDayCode) != 0); 164 } 165 166 /** number of overlapping days */ 167 public int nrSharedDays(TimeLocation anotherLocation) { 168 int ret = 0; 169 for (int i = 0; i < Constants.NR_DAYS; i++) { 170 if ((iDayCode & Constants.DAY_CODES[i]) == 0) 171 continue; 172 if ((anotherLocation.iDayCode & Constants.DAY_CODES[i]) == 0) 173 continue; 174 ret++; 175 } 176 return ret; 177 } 178 179 /** true if hours overlap */ 180 public boolean shareHours(TimeLocation anotherLocation) { 181 return (iStartSlot + iLength > anotherLocation.iStartSlot) 182 && (anotherLocation.iStartSlot + anotherLocation.iLength > iStartSlot); 183 } 184 185 /** number of overlapping time slots (ignoring days) */ 186 public int nrSharedHours(TimeLocation anotherLocation) { 187 int end = Math.min(iStartSlot + iLength, anotherLocation.iStartSlot + anotherLocation.iLength); 188 int start = Math.max(iStartSlot, anotherLocation.iStartSlot); 189 return (end < start ? 0 : end - start); 190 } 191 192 /** true if weeks overlap */ 193 public boolean shareWeeks(TimeLocation anotherLocation) { 194 return iWeekCode.intersects(anotherLocation.iWeekCode); 195 } 196 197 /** true if weeks overlap */ 198 public boolean shareWeeks(BitSet weekCode) { 199 return iWeekCode.intersects(weekCode); 200 } 201 202 public boolean hasDay(int day) { 203 return iWeekCode.get(day); 204 } 205 206 /** true if overlap */ 207 public boolean hasIntersection(TimeLocation anotherLocation) { 208 return shareDays(anotherLocation) && shareHours(anotherLocation) && shareWeeks(anotherLocation); 209 } 210 211 /** Used slots */ 212 public IntEnumeration getSlots() { 213 return new SlotsEnum(); 214 } 215 216 /** Used start slots (for each meeting) */ 217 public IntEnumeration getStartSlots() { 218 return new StartSlotsEnum(); 219 } 220 221 /** Days */ 222 public IntEnumeration getDays() { 223 return new DaysEnum(); 224 } 225 226 public int[] getDaysArray() { 227 int[] days = new int[getNrMeetings()]; 228 int i = 0; 229 for (Enumeration<Integer> e = getDays(); e.hasMoreElements();) 230 days[i++] = e.nextElement(); 231 return days; 232 } 233 234 /** Text representation */ 235 public String getName() { 236 return getDayHeader() + " " + getStartTimeHeader(); 237 } 238 239 public String getLongName() { 240 return getDayHeader() + " " + getStartTimeHeader() + " - " + getEndTimeHeader() + " " + getDatePatternName(); 241 } 242 243 public String getLongNameNoAdj() { 244 return getDayHeader() + " " + getStartTimeHeader() + " - " + getEndTimeHeaderNoAdj() + " " 245 + getDatePatternName(); 246 } 247 248 /** Preference */ 249 public int getPreference() { 250 return iPreference; 251 } 252 253 public void setPreference(int preference) { 254 iPreference = preference; 255 } 256 257 /** Length */ 258 public int getLength() { 259 return iLength; 260 } 261 262 /** Length */ 263 public int getNrSlotsPerMeeting() { 264 return iLength; 265 } 266 267 /** Normalized preference */ 268 public double getNormalizedPreference() { 269 return iNormalizedPreference; 270 } 271 272 public void setNormalizedPreference(double normalizedPreference) { 273 iNormalizedPreference = normalizedPreference; 274 } 275 276 /** Time pattern model (can be null) */ 277 public Long getTimePatternId() { 278 return iTimePatternId; 279 } 280 281 public Long getDatePatternId() { 282 return iDatePatternId; 283 } 284 285 public void setTimePatternId(Long timePatternId) { 286 iTimePatternId = timePatternId; 287 } 288 289 public BitSet getWeekCode() { 290 return iWeekCode; 291 } 292 293 public String getDatePatternName() { 294 return iDatePatternName; 295 } 296 297 public void setDatePattern(Long datePatternId, String datePatternName, BitSet weekCode) { 298 iDatePatternId = datePatternId; 299 iDatePatternName = datePatternName; 300 iWeekCode = weekCode; 301 } 302 303 public int getDatePatternPreference() { 304 return iDatePreference; 305 } 306 307 @Override 308 public String toString() { 309 return getName() + " (" + iNormalizedPreference + ")"; 310 } 311 312 @Override 313 public int hashCode() { 314 return iHashCode; 315 } 316 317 private class StartSlotsEnum implements IntEnumeration { 318 int day = -1; 319 boolean hasNext = false; 320 321 private StartSlotsEnum() { 322 hasNext = nextDay(); 323 } 324 325 boolean nextDay() { 326 do { 327 day++; 328 if (day >= Constants.DAY_CODES.length) 329 return false; 330 } while ((Constants.DAY_CODES[day] & iDayCode) == 0); 331 return true; 332 } 333 334 @Override 335 public boolean hasMoreElements() { 336 return hasNext; 337 } 338 339 @Override 340 public Integer nextElement() { 341 int slot = (day * Constants.SLOTS_PER_DAY) + iStartSlot; 342 hasNext = nextDay(); 343 return slot; 344 } 345 346 @Deprecated 347 @Override 348 public Integer nextInt() { 349 return nextElement(); 350 } 351 } 352 353 private class DaysEnum extends StartSlotsEnum { 354 private DaysEnum() { 355 super(); 356 } 357 358 @Override 359 public Integer nextElement() { 360 int ret = day; 361 hasNext = nextDay(); 362 return ret; 363 } 364 } 365 366 private class SlotsEnum extends StartSlotsEnum { 367 int pos = 0; 368 369 private SlotsEnum() { 370 super(); 371 } 372 373 private boolean nextSlot() { 374 if (pos + 1 < iLength) { 375 pos++; 376 return true; 377 } 378 if (nextDay()) { 379 pos = 0; 380 return true; 381 } 382 return false; 383 } 384 385 @Override 386 public Integer nextElement() { 387 int slot = (day * Constants.SLOTS_PER_DAY) + iStartSlot + pos; 388 hasNext = nextSlot(); 389 return slot; 390 } 391 } 392 393 @Override 394 public boolean equals(Object o) { 395 if (o == null || !(o instanceof TimeLocation)) 396 return false; 397 TimeLocation t = (TimeLocation) o; 398 if (getStartSlot() != t.getStartSlot()) 399 return false; 400 if (getLength() != t.getLength()) 401 return false; 402 if (getDayCode() != t.getDayCode()) 403 return false; 404 return ToolBox.equals(getTimePatternId(), t.getTimePatternId()) 405 && ToolBox.equals(getDatePatternId(), t.getDatePatternId()); 406 } 407 408 public int getNrWeeks() { 409 return getNrWeeks(0, iWeekCode.size() - 1); 410 } 411 412 public int getNrWeeks(int startDay, int endDay) { 413 /* 414 * BitSet x = new BitSet(1+(endDay-startDay)/Constants.NR_DAYS); for 415 * (int i=iWeekCode.nextSetBit(startDay); i<=endDay && i>=0; 416 * i=iWeekCode.nextSetBit(i+1)) x.set((i-startDay)/Constants.NR_DAYS); 417 * return x.cardinality(); 418 */ 419 int card = iWeekCode.get(startDay, endDay).cardinality(); 420 if (card == 0) 421 return 0; 422 if (card <= 7) 423 return 1; 424 return (5 + card) / 6; 425 } 426 427 public interface IntEnumeration extends Enumeration<Integer> { 428 @Deprecated 429 public Integer nextInt(); 430 } 431 }