package resa.evaluation.migrate;
import java.util.*;
/**
* Created by ding on 15/1/19.
*/
public class ConsistentHashing {
public static MigPlan calc(double[] dataSizes, List<int[]> currAlloc, int newSize) {
if (newSize == currAlloc.size()) {
throw new IllegalArgumentException("size equal");
}
return newSize > currAlloc.size() ? inc(dataSizes, currAlloc, newSize) : dec(dataSizes, currAlloc, newSize);
}
private static MigPlan dec(double[] dataSizes, List<int[]> currAlloc, int newSize) {
List<int[]> ret = new ArrayList<>(currAlloc);
List<Integer> tasks2Move = new ArrayList<>();
currAlloc.stream()
.map(alloc -> new AllocDataSize(alloc, Arrays.stream(alloc).mapToDouble(i -> dataSizes[i]).sum()))
.sorted().limit(currAlloc.size() - newSize)
.forEach(allocDataSize -> {
ret.remove(allocDataSize.alloc);
Arrays.stream(allocDataSize.alloc).forEach(tasks2Move::add);
});
int avgAddSize = tasks2Move.size() / newSize, k = tasks2Move.size() % newSize, pos = 0;
int[][] matching = new int[newSize][2];
for (int j = 0; j < newSize; j++) {
int newAllocSize = ret.get(j).length + avgAddSize;
if (j < k) {
newAllocSize++;
}
int[] newAlloc = new int[newAllocSize];
System.arraycopy(ret.get(j), 0, newAlloc, 0, ret.get(j).length);
for (int l = ret.get(j).length; l < newAllocSize; l++) {
newAlloc[l] = tasks2Move.get(pos++);
}
matching[j][0] = j;
matching[j][1] = currAlloc.indexOf(ret.get(j));
ret.set(j, newAlloc);
}
return new MigPlan(ret, tasks2Move.stream().mapToDouble(task -> dataSizes[task]).sum(), matching);
}
private static class AllocDataSize implements Comparable<AllocDataSize> {
final int[] alloc;
final double dataSize;
private AllocDataSize(int[] alloc, double dataSize) {
this.alloc = alloc;
this.dataSize = dataSize;
}
@Override
public int compareTo(AllocDataSize o) {
return Double.compare(dataSize, o.dataSize);
}
}
private static MigPlan inc(double[] dataSizes, List<int[]> currAlloc, int newSize) {
Map<int[], Integer> posMap = new HashMap<>();
for (int i = 0; i < currAlloc.size(); i++) {
posMap.put(currAlloc.get(i), i);
}
Collections.sort(currAlloc, (alloc1, alloc2) -> Integer.compare(alloc2.length, alloc1.length));
int avgSize = dataSizes.length / newSize;
int[] task2Move = new int[avgSize * (newSize - currAlloc.size())];
List<int[]> ret = new ArrayList<>(newSize);
int pos = 0;
int[][] matching = new int[currAlloc.size()][2];
for (int i = 0; i < currAlloc.size(); i++) {
int[] tmp = Arrays.stream(currAlloc.get(i)).mapToObj(Integer::valueOf)
.sorted((i1, i2) -> Double.compare(dataSizes[i1], dataSizes[i2]))
.mapToInt(Integer::intValue).toArray();
int cnt = task2Move.length / currAlloc.size();
if (i < (task2Move.length % currAlloc.size())) {
cnt++;
}
for (int j = 0; j < cnt; j++) {
task2Move[pos++] = tmp[j];
}
ret.add(Arrays.copyOfRange(tmp, cnt, tmp.length));
matching[i][0] = i;
matching[i][1] = posMap.get(currAlloc.get(i));
}
for (int i = 0; i < newSize - currAlloc.size(); i++) {
ret.add(Arrays.copyOfRange(task2Move, avgSize * i, avgSize * (i + 1)));
}
return new MigPlan(ret, Arrays.stream(task2Move).mapToDouble(task -> dataSizes[task]).sum(), matching);
}
}