package resa.migrate.plan; import java.util.AbstractMap; import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.stream.DoubleStream; import java.util.stream.Stream; /** * Created by ding on 14-6-11. */ public abstract class PackCalculator { public static class Range { public final int start; public final int end; Range(int start, int end) { if (start > end) { throw new IllegalArgumentException("start=" + start + ", end=" + end); } this.start = start; this.end = end; } boolean contains(int v) { return start <= v && v <= end; } } protected static class Pack { public int[] packing; public double gain; Pack(int[] packing, double gain) { this.packing = packing; this.gain = gain; } } protected static final Pack INFEASIBLE = new Pack(new int[0], Double.MIN_VALUE); protected double[] workloads; protected double[] normalizedWordloads; protected double[] dataSizes; protected Map.Entry<Range[], Double>[] srcPacks; protected int packSize; private float ratio = 1.3f; protected double loadUpperLimit; // cache result private Pack result = null; public int[] getPack() { Objects.requireNonNull(result, "Calc is not called"); return result == INFEASIBLE ? null : result.packing; } public Double gain() { Objects.requireNonNull(result, "Calc is not called"); return result == INFEASIBLE ? null : result.gain; } public PackCalculator setTargetPackSize(int packSize) { this.packSize = packSize; return this; } public PackCalculator setUpperLimitRatio(float ratio) { if (Float.compare(1f, ratio) >= 0) { throw new IllegalArgumentException("Bad ratio: " + ratio); } this.ratio = ratio; return this; } public PackCalculator setDataSizes(double[] dataSizes) { this.dataSizes = dataSizes; return this; } public PackCalculator setSrcPack(int[] pack) { return setSrcPacks(Collections.singletonMap(pack, 1.0)); } public static Range[] convertPack(int[] pack) { Range[] ret = new Range[pack.length]; int start = 0; for (int i = 0; i < pack.length; i++) { int end = start + pack[i]; ret[i] = new Range(start, end - 1); start = end; } return ret; } protected static int[] convertPack(Range[] pack) { return Stream.of(pack).mapToInt(p -> p.end - p.start + 1).toArray(); } public PackCalculator setSrcPacks(Map<int[], Double> packs) { this.srcPacks = packs.entrySet().stream() .map(e -> new AbstractMap.SimpleEntry<>(convertPack(e.getKey()), e.getValue())) .toArray(Map.Entry[]::new); return this; } public PackCalculator setWorkloads(double[] workloads) { this.workloads = workloads; return this; } private void checkAndInit() { if (workloads.length < packSize) { throw new IllegalArgumentException("packSize is larger than workload size"); } if (Stream.of(srcPacks).anyMatch(e -> e.getKey().length == packSize)) { throw new IllegalArgumentException("Current number of packs equals packSize"); } if (!Stream.of(srcPacks).allMatch(e -> workloads.length == e.getKey()[e.getKey().length - 1].end + 1)) { throw new IllegalArgumentException("srcPack mismatch with workload"); } if (workloads.length != dataSizes.length) { throw new IllegalArgumentException("workloads.length != dataSizes.length"); } loadUpperLimit = DoubleStream.of(workloads).sum() / packSize * ratio; normalizedWordloads = new double[workloads.length]; for (int i = 0; i < normalizedWordloads.length; i++) { if (workloads[i] > loadUpperLimit) { normalizedWordloads[i] = loadUpperLimit; } else { normalizedWordloads[i] = workloads[i]; } } } public PackCalculator calc() { checkAndInit(); result = calcPack(); normalizedWordloads = null; return this; } protected abstract Pack calcPack(); }