package resa.migrate.plan;
import java.util.Map;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* Created by ding on 14-6-27.
*/
public class FastCalculator extends PackCalculator {
private KuhnMunkres kmAlg;
private double gain;
@Override
protected Pack calcPack() {
Range[] retPack = convertPack(PackingAlg.calc(this.workloads, this.packSize));
int maxPackSize = Stream.of(srcPacks).map(Map.Entry::getKey).mapToInt(pack -> pack.length).max().getAsInt();
kmAlg = new KuhnMunkres(Math.max(retPack.length, maxPackSize));
gain = totalGain(retPack);
Range[] buf = new Range[retPack.length];
while (true) {
int packToAdj = -1, newEnd = -1;
for (int i = 0; i < retPack.length - 1; i++) {
int point = retPack[i].end;
// step forward
System.arraycopy(retPack, 0, buf, 0, buf.length);
int p = stepForward(buf, i);
if (p != point) {
packToAdj = i;
newEnd = p;
}
// step backward
System.arraycopy(retPack, 0, buf, 0, buf.length);
p = stepBackward(buf, i);
if (p != point) {
packToAdj = i;
newEnd = p;
}
}
// nothing to update
if (packToAdj == -1) {
break;
} else {
retPack[packToAdj] = new Range(retPack[packToAdj].start, newEnd);
retPack[packToAdj + 1] = new Range(newEnd + 1, retPack[packToAdj + 1].end);
}
}
kmAlg = null;
return new Pack(convertPack(retPack), gain);
}
private int stepForward(Range[] orgPack, int point) {
double workload = totalWorkload(orgPack[point].start, orgPack[point].end + 1);
int newPos = orgPack[point].end;
int j = orgPack[point].end + 1;
final int upBound = orgPack[point + 1].end;
while (j < upBound && (workload += normalizedWordloads[j]) <= loadUpperLimit) {
orgPack[point] = new Range(orgPack[point].start, j);
orgPack[point + 1] = new Range(j + 1, orgPack[point + 1].end);
double g = totalGain(orgPack);
if (g > gain) {
gain = g;
newPos = j;
}
j++;
}
return newPos;
}
private int stepBackward(Range[] orgPack, int point) {
double workload = totalWorkload(orgPack[point + 1].start, orgPack[point + 1].end + 1);
int newPos = orgPack[point].end;
int j = orgPack[point].end - 1;
final int lowBound = orgPack[point].start;
while (j > lowBound && (workload += normalizedWordloads[j]) <= loadUpperLimit) {
orgPack[point] = new Range(orgPack[point].start, j);
orgPack[point + 1] = new Range(j + 1, orgPack[point + 1].end);
double g = totalGain(orgPack);
if (g > gain) {
gain = g;
newPos = j;
}
j--;
}
return newPos;
}
private double totalGain(Range[] pack) {
return Stream.of(srcPacks).mapToDouble(e -> packGain(pack, e.getKey()) * e.getValue()).sum();
}
private double packGain(Range[] pack1, Range[] pack2) {
double[][] weights = new double[pack1.length][pack2.length];
for (int i = 0; i < pack1.length; i++) {
for (int j = 0; j < pack2.length; j++) {
weights[i][j] = overlap(pack1[i], pack2[j]);
}
}
double[] maxWeight = new double[1];
kmAlg.getMaxBipartie(weights, maxWeight);
// int[][] pairs = kmAlg.getMaxBipartie(weights, maxWeight);
// return Stream.of(pairs).mapToDouble(r -> overlap(newPacks[r[0]], currPacks[pStart + r[1]])).sum();
return maxWeight[0];
}
private double overlap(Range r1, Range r2) {
if (r1.end < r2.start || r1.start > r2.end) {
return 0;
} else if (r1.start <= r2.start && r1.end >= r2.start) {
return IntStream.rangeClosed(r2.start, Math.min(r2.end, r1.end)).mapToDouble(i -> dataSizes[i]).sum();
} else if (r1.start >= r2.start && r1.start <= r2.end) {
return IntStream.rangeClosed(r1.start, Math.min(r2.end, r1.end)).mapToDouble(i -> dataSizes[i]).sum();
}
return 0;
}
private double totalWorkload(int wStart, int wEnd) {
double sum = 0;
for (int i = wStart; i < wEnd; i++) {
sum += normalizedWordloads[i];
}
return sum;
}
}