package resa.migrate.plan;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.IntStream;
/**
* Created by ding on 14-6-28.
*/
public class SinglePackCalculator extends PackCalculator {
private Map<String, Pack> cache;
private KuhnMunkres kmAlg;
private Range[] currPack;
protected Pack calcPack() {
currPack = srcPacks[0].getKey();
kmAlg = new KuhnMunkres(Math.max(packSize, currPack.length));
Pack p;
if (normalizedWordloads.length == packSize) {
int[] ret = new int[normalizedWordloads.length];
Arrays.fill(ret, 1);
Range[] packs = IntStream.range(0, packSize).mapToObj(i -> new Range(i, i)).toArray(Range[]::new);
p = new Pack(ret, gain(packs, 0, currPack.length));
} else {
//init buffer
cache = new HashMap<>();
p = calc0(0, normalizedWordloads.length, packSize, 0, currPack.length);
// release memory
cache = null;
}
kmAlg = null;
return p;
}
private Pack calc0(int wStart, int wEnd, int numPartition, int pStart, int pEnd) {
if (wEnd - wStart < numPartition || numPartition == 0) {
throw new IllegalStateException("start=" + wStart + ", end=" + wEnd + ", numPartition=" + numPartition);
}
String key = wStart + "-" + wEnd + "-" + numPartition + "-" + pStart + "-" + pEnd;
return cache.computeIfAbsent(key, k -> calc1(wStart, wEnd, numPartition, pStart, pEnd));
}
private Pack calc1(int wStart, int wEnd, int numPartition, int pStart, int pEnd) {
if (numPartition == 1) {
double sum = totalWorkload(wStart, wEnd);
return sum > loadUpperLimit ? INFEASIBLE :
new Pack(new int[]{wEnd - wStart}, gain(new Range[]{new Range(wStart, wEnd - 1)}, pStart, pEnd));
} else if (wEnd - wStart == numPartition) {
int[] ret = new int[numPartition];
Arrays.fill(ret, 1);
Range[] newRanges = IntStream.range(wStart, wEnd).mapToObj(i -> new Range(i, i)).toArray(Range[]::new);
return new Pack(ret, gain(newRanges, pStart, pEnd));
}
int split = wStart + 1;
Pack rightPack = INFEASIBLE;
double gain = Double.MIN_VALUE;
for (int i = split; i < wEnd; i++) {
if (totalWorkload(wStart, i) > loadUpperLimit || wEnd - i < numPartition - 1) {
break;
}
int startPack = findPack(i);
int stopPack = currPack[startPack].start < i ? startPack + 1 : startPack;
startPack = Math.max(startPack, pStart);
stopPack = Math.min(stopPack, pEnd);
for (int k = startPack; k <= stopPack; k++) {
Pack left = calc0(wStart, i, 1, pStart, k);
Pack right = calc0(i, wEnd, numPartition - 1, k, pEnd);
if (left != INFEASIBLE && right != INFEASIBLE) {
double newGain = left.gain + right.gain;
if (Double.compare(newGain, gain) > 0) {
gain = newGain;
split = i;
rightPack = right;
}
}
}
}
if (rightPack == INFEASIBLE) {
return INFEASIBLE;
}
int[] ret = new int[numPartition];
ret[0] = split - wStart;
System.arraycopy(rightPack.packing, 0, ret, 1, numPartition - 1);
return new Pack(ret, gain);
}
private int findPack(int v) {
for (int i = 0; i < currPack.length; i++) {
if (currPack[i].end >= v) {
return i;
}
}
throw new IllegalStateException("value is " + v);
}
private double totalWorkload(int wStart, int wEnd) {
double sum = 0;
for (int i = wStart; i < wEnd; i++) {
sum += normalizedWordloads[i];
}
return sum;
}
private double gain(Range[] newPacks, int pStart, int pEnd) {
if (newPacks.length == 0 || pStart == pEnd) {
return 0;
}
double[][] weights = new double[newPacks.length][pEnd - pStart];
for (int i = 0; i < weights.length; i++) {
for (int j = 0; j < weights[i].length; j++) {
weights[i][j] = overlap(newPacks[i], currPack[pStart + 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]], currPack[pStart + r[1]])).sum();
return maxWeight[0];
}
private double overlap(Range r1, Range r2) {
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;
}
}