/*
* Copyright 2009 Peter Karich, peat_hal ‘at’ users ‘dot’ sourceforge ‘dot’ net.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package de.timefinder.algo.constraint;
import de.timefinder.data.algo.DataPoolSettings;
import de.timefinder.data.Event;
import de.timefinder.data.Person;
import de.timefinder.data.algo.Constraint;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
/**
* Very specific constraint taken from the requirements in the internat. timetabling comp.
*
* @author Peter Karich, peat_hal ‘at’ users ‘dot’ sourceforge ‘dot’ net
*/
public class PersonITCRasterConstraint extends Constraint {
private static final long serialVersionUID = -3663434621373045628L;
private Person person;
private int dayDuration;
private int weekLength;
private PersonITCRasterConstraint() {
}
public PersonITCRasterConstraint(Person person, DataPoolSettings settings) {
this.person = person;
dayDuration = settings.getTimeslotsPerDay();
weekLength = settings.getTimeslotsPerWeek();
}
public int getDayDuration() {
return dayDuration;
}
public void setDayDuration(int dayDuration) {
this.dayDuration = dayDuration;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public int getWeekLength() {
return weekLength;
}
public void setWeekLength(int weekLength) {
this.weekLength = weekLength;
}
@Override
public int getViolations(Object raster) {
TreeMap<Integer, Event> map = (TreeMap<Integer, Event>) raster;
return getSoftConstraintViolations(map, null);
}
public int getSoftConstraintViolations() {
return getSoftConstraintViolations(createIndexRasterFromStartTimes(), null);
}
public int getSoftConstraintViolations(TreeMap<Integer, Event> treeMapRaster,
Map<Event, Integer> problemMap) {
return getSuccessiveViolation(treeMapRaster, problemMap)
+ getSingleViolation(treeMapRaster, problemMap)
+ getEndViolation(treeMapRaster, problemMap);
}
public int getSingleViolation() {
return getSingleViolation(createIndexRasterFromStartTimes(), null);
}
/**
* This method calculates one of the soft constraints specified in the ITC Track2.
* A single Event on a day counts as a violation.
*/
public int getSingleViolation(TreeMap<Integer, Event> treeMapRaster,
Map<Event, Integer> problemMap) {
int counter = 0;
int currentDay;
Integer newTime;
Entry entry = treeMapRaster.ceilingEntry(0);
while (entry != null) {
int time = (Integer) entry.getKey();
currentDay = time / dayDuration;
newTime = treeMapRaster.higherKey(time);
if (newTime == null) {
counter++;
increaseProblemCounter(problemMap, (Event) entry.getValue());
break;
} else if (newTime / dayDuration > currentDay) {
counter++;
increaseProblemCounter(problemMap, (Event) entry.getValue());
//we want that time = newTime for the next iteration
newTime--;
} else {
//there couldn't be a violation on currentDay (at least two ti's found)
newTime = dayDuration * (currentDay + 1) - 1;
}
entry = treeMapRaster.higherEntry(newTime);
}
return counter;
}
public int getEndViolation() {
return getEndViolation(createIndexRasterFromStartTimes(), null);
}
/**
* This method calculates one of the soft constraints specified in the ITC Track2.
* A Event at the end of a day counts as a violation.
*/
public int getEndViolation(TreeMap raster, Map<Event, Integer> problemMap) {
int counter = 0;
int end = weekLength;
Event ti;
for (int i = dayDuration - 1; i < end; i += dayDuration) {
ti = (Event) raster.get(i);
if (ti != null) {
counter++;
increaseProblemCounter(problemMap, ti);
}
}
return counter;
}
public int getSuccessiveViolation() {
return getSuccessiveViolation(createIndexRasterFromStartTimes(), null);
}
/**
* This method calculates one of the soft constraints specified in the ITC Track2.
* So n Event's (where n = 2 or more) which follows each other
* directly and are on the same day will be violations. They call them
* 'successive'.
*/
public int getSuccessiveViolation(TreeMap raster, Map<Event, Integer> problemMap) {
int counter = 0;
int lastTime = 0;
List<Event> allTIs = new ArrayList<Event>(5);
for (Object o : raster.entrySet()) {
Entry entry = (Entry) o;
int time = (Integer) entry.getKey();
if (time / dayDuration == lastTime / dayDuration
&& time - lastTime < 2) {
// Event with 'time' could be involved in a violation
lastTime = time;
allTIs.add((Event) entry.getValue());
continue;
}
// Next day or 'time' is not a consecutive to 'lastTime'
if (allTIs.size() > 2) {
counter += allTIs.size() - 2;
increaseProblemCounter(problemMap, allTIs);
}
// Now Event with 'time' could be involved in a violation, too!
lastTime = time;
allTIs.clear();
allTIs.add((Event) entry.getValue());
}
if (allTIs.size() > 2) {
counter += allTIs.size() - 2;
increaseProblemCounter(problemMap, allTIs);
}
return counter;
}
private void increaseProblemCounter(Map<Event, Integer> problemMap, List<Event> allEvs) {
if (problemMap != null) {
for (Event ti : allEvs) {
Integer i = problemMap.get(ti);
assert i != null : allEvs + " map:" + problemMap;
problemMap.put(ti, i + 1);
}
}
}
private void increaseProblemCounter(Map<Event, Integer> problemMap, Event event) {
if (problemMap != null) {
Integer i = problemMap.get(event);
assert i != null : event + " map:" + problemMap;
problemMap.put(event, i + 1);
}
}
private TreeMap<Integer, Event> createIndexRasterFromStartTimes() {
TreeMap<Integer, Event> indexRaster = new TreeMap<Integer, Event>();
for (Event ev : person.getEvents()) {
if (ev.getStart() >= 0)
indexRaster.put(ev.getStart(), ev);
}
return indexRaster;
}
}