forked from TrueCloudLab/frostfs-sdk-java
123 lines
3.9 KiB
Java
123 lines
3.9 KiB
Java
package info.frostfs.sdk.placement;
|
|
|
|
import info.frostfs.sdk.dto.netmap.Hasher;
|
|
import info.frostfs.sdk.dto.netmap.NodeInfo;
|
|
|
|
import java.math.BigInteger;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
import java.util.function.Function;
|
|
|
|
import static info.frostfs.sdk.constants.AppConst.UNSIGNED_LONG_MASK;
|
|
import static info.frostfs.sdk.constants.CryptoConst.*;
|
|
|
|
public final class Tools {
|
|
private Tools() {
|
|
}
|
|
|
|
public static long distance(long x, long y) {
|
|
long acc = x ^ y;
|
|
acc ^= acc >>> MURMUR_MULTIPLIER;
|
|
acc *= LANDAU_PRIME_DIVISOR_65BIT;
|
|
acc ^= acc >>> MURMUR_MULTIPLIER;
|
|
acc *= LANDAU_PRIME_DIVISOR_64BIT;
|
|
acc ^= acc >>> MURMUR_MULTIPLIER;
|
|
return acc;
|
|
}
|
|
|
|
public static void appendWeightsTo(NodeInfo[] nodes, Function<NodeInfo, Double> wf, double[] weights) {
|
|
if (weights.length < nodes.length) {
|
|
weights = new double[nodes.length];
|
|
}
|
|
for (int i = 0; i < nodes.length; i++) {
|
|
weights[i] = wf.apply(nodes[i]);
|
|
}
|
|
}
|
|
|
|
public static <T extends Hasher> List<T> sortHasherSliceByWeightValue(List<T> nodes, double[] weights, long hash) {
|
|
if (nodes.isEmpty()) {
|
|
return nodes;
|
|
}
|
|
|
|
boolean allEquals = true;
|
|
if (weights.length > 1) {
|
|
for (int i = 1; i < weights.length; i++) {
|
|
if (weights[i] != weights[0]) {
|
|
allEquals = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Double[] dist = new Double[nodes.size()];
|
|
|
|
if (allEquals) {
|
|
for (int i = 0; i < dist.length; i++) {
|
|
long x = nodes.get(i).getHash();
|
|
dist[i] = toUnsignedBigInteger(distance(x, hash)).doubleValue();
|
|
}
|
|
return sortHasherByDistance(nodes, dist, true);
|
|
}
|
|
|
|
for (int i = 0; i < dist.length; i++) {
|
|
var reverse = UNSIGNED_LONG_MASK.subtract(toUnsignedBigInteger(distance(nodes.get(i).getHash(), hash)));
|
|
dist[i] = reverse.doubleValue() * weights[i];
|
|
}
|
|
|
|
return sortHasherByDistance(nodes, dist, false);
|
|
}
|
|
|
|
public static <T extends Hasher, N extends Comparable<N>> List<T> sortHasherByDistance(
|
|
List<T> nodes, N[] dist, boolean asc
|
|
) {
|
|
IndexedValue<T, N>[] indexes = new IndexedValue[nodes.size()];
|
|
for (int i = 0; i < dist.length; i++) {
|
|
indexes[i] = new IndexedValue<>(nodes.get(i), dist[i]);
|
|
}
|
|
|
|
if (asc) {
|
|
Arrays.sort(indexes, Comparator.comparing(iv -> iv.dist));
|
|
} else {
|
|
Arrays.sort(indexes, (iv1, iv2) -> iv2.dist.compareTo(iv1.dist));
|
|
}
|
|
|
|
List<T> result = new ArrayList<>();
|
|
for (IndexedValue<T, N> iv : indexes) {
|
|
result.add(iv.nodeInfo);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static Function<NodeInfo, Double> defaultWeightFunc(List<NodeInfo> nodes) {
|
|
MeanAgg mean = new MeanAgg();
|
|
MinAgg minV = new MinAgg();
|
|
|
|
for (NodeInfo node : nodes) {
|
|
mean.add(node.getCapacity());
|
|
minV.add(node.getPrice());
|
|
}
|
|
|
|
return newWeightFunc(new SigmoidNorm(mean.compute()), new ReverseMinNorm(minV.compute()));
|
|
}
|
|
|
|
private static BigInteger toUnsignedBigInteger(long i) {
|
|
return i >= 0 ? BigInteger.valueOf(i) : BigInteger.valueOf(i).and(UNSIGNED_LONG_MASK);
|
|
}
|
|
|
|
private static Function<NodeInfo, Double> newWeightFunc(Normalizer capNorm, Normalizer priceNorm) {
|
|
return nodeInfo -> capNorm.normalize(nodeInfo.getCapacity().doubleValue())
|
|
* priceNorm.normalize(nodeInfo.getPrice().doubleValue());
|
|
}
|
|
|
|
private static class IndexedValue<T, N> {
|
|
final T nodeInfo;
|
|
final N dist;
|
|
|
|
IndexedValue(T nodeInfo, N dist) {
|
|
this.nodeInfo = nodeInfo;
|
|
this.dist = dist;
|
|
}
|
|
}
|
|
}
|