/*
* Copyright 2010 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.graph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import javolution.util.FastSet;
/**
* @author Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net
*/
public abstract class AbstractGraphAlgorithm implements GraphAlgorithm {
protected int slotsPerWeek;
protected List<Integer> eventsDuration = new ArrayList<Integer>();
protected List<Set<EventTree>> slotToTrees = new ArrayList<Set<EventTree>>();
protected List<List<EventTree>> eventToTrees = new ArrayList<List<EventTree>>();
/**
* @param slotsPerWeek time-slots per week
*/
public AbstractGraphAlgorithm(int slotsPerWeek) {
this.slotsPerWeek = slotsPerWeek;
}
@Override
public abstract Set<EventTree> start();
protected List<EventTree> getTreesForEvent(int eNo) {
if (eNo < 0)
throw new IllegalArgumentException("specify an event number >= 0");
else if (eNo >= eventToTrees.size())
throw new IllegalArgumentException("add event before");
else if (eventToTrees.size() == 0)
throw new IllegalStateException("no events added to map!?");
return eventToTrees.get(eNo);
}
protected Collection<EventTree> getTreesForSlot(int slot) {
if (slot >= slotToTrees.size())
throw new IllegalStateException("Add resource "
+ slotToTrees.size() / slotsPerWeek + " before setting the weight");
return slotToTrees.get(slot);
}
/**
* @param eNo event number
* @param choiceNo every events has at least one set of slots to choose from
* @return tree pointing to all slots which should be assigned if the
* tree is in the matching
*/
EventTree getTree(int eNo, int choiceNo) {
List<EventTree> trees = getTreesForEvent(eNo);
int todo = choiceNo - trees.size();
if (todo >= 0) {
for (int i = 0; i < todo; i++) {
trees.add(null);
}
// create an empty tree at the correct index
EventTree tree = new EventTree(eNo, choiceNo);
trees.add(tree);
return tree;
} else
return trees.get(choiceNo);
}
private void initTreesForEvent(int eNo, int eDur) {
if (eNo < 0)
throw new IllegalArgumentException("event number should be >= 0");
if (eDur <= 0)
throw new IllegalArgumentException("event duration should be > 0");
int todo = eNo - eventToTrees.size();
if (todo >= 0) {
for (int i = 0; i < todo; i++) {
eventToTrees.add(null);
eventsDuration.add(null);
}
// create an empty arrayList at the correct index 'todo'
eventToTrees.add(new ArrayList());
eventsDuration.add(eDur);
}
}
protected ArrayList<EventTree> sortTrees() {
ArrayList<EventTree> list = new ArrayList();
for (List<EventTree> trees : eventToTrees) {
for (EventTree tree : trees) {
if (tree.isEnabled()) // && tree.getWeight() > 0)
list.add(tree);
}
}
if (list.size() == 0)
throw new IllegalStateException("No elements in list!?");
// not faster:
// LinearSort.sort(list);
Collections.sort(list, new Comparator<EventTree>() {
@Override
public int compare(EventTree o1, EventTree o2) {
return o2.getWeight() - o1.getWeight();
}
});
return list;
}
public int findWeight(Set<EventTree> matching) {
int w = 0;
for (EventTree tree : matching) {
w += tree.getWeight();
}
return w;
}
public void addEvent(int eNo, int eDur) {
initTreesForEvent(eNo, eDur);
}
/**
* Generates the necessary connections between all "choose"-trees of
* the specified event and the slots of the specified resource
*
* TODO ability to remove slots from distinct tree, to avoid starting
* at one day and ending in the other. other usecase is for example to
* force double-hours event pattern etc.
*
* @param rNo number of the room to add the event
* @param eNo number of the event
* @param eDur event duration
*/
public void addResourceTo(int rNo, int eNo) {
initResource(rNo);
Integer duration = eventsDuration.get(eNo);
if (duration == null)
throw new IllegalStateException("Add event to graph before specifing it in addResourceTo!");
int slot = rNo * slotsPerWeek;
int choice = 0;
int max = slot + slotsPerWeek - (duration - 1);
for (; slot < max; slot++) {
EventTree tree = getTree(eNo, choice++);
for (int d = 0; d < duration; d++) {
tree.add(slot + d);
Collection<EventTree> trees = getTreesForSlot(slot);
trees.add(tree);
}
}
}
public void incChoiceWeight(int eNo, int choice, int weight) {
EventTree tree = getTreesForEvent(eNo).get(choice);
tree.incWeight(weight);
}
public void incSlotWeight(int eNo, int slot, int weight) {
for (EventTree tree : getTreesForEvent(eNo)) {
if (tree.getSlots().contains(slot))
tree.incWeight(weight);
}
}
public void setEventAllEnabled(boolean b) {
for (List<EventTree> trees : eventToTrees) {
for (EventTree tree : trees) {
tree.setEnabled(b);
}
}
}
private int initResource(int rNo) {
if (rNo < 0)
throw new IllegalArgumentException("resource number should be >= 0");
int todo = (rNo + 1) * slotsPerWeek;
for (int slot = slotToTrees.size(); slot < todo; slot++) {
slotToTrees.add((Set) FastSet.newInstance());
}
return rNo;
}
public void setEventEnableStatus(int evNo, int start) {
if (start < 0) {
for (EventTree tree : getTreesForEvent(evNo)) {
tree.setEnabled(true);
}
return;
}
EventTree selected = null;
// avoid assigned trees in the neighbourhood
for (EventTree tree : getTreesForEvent(evNo)) {
tree.setEnabled(false);
if (tree.getMinSlot() % slotsPerWeek == start) {
selected = tree;
}
}
if (selected == null)
throw new UnsupportedOperationException("Cannot find event " + evNo + " start: " + start);
for (Integer sl : selected.getSlots()) {
for (EventTree conflictTree : getTreesForSlot(sl)) {
conflictTree.setEnabled(false);
}
}
}
List<Set<EventTree>> getSlots() {
return slotToTrees;
}
int getChoices(int eNo) {
return eventToTrees.get(eNo).size();
}
}