frostfs-sdk-java/client/src/main/java/info/frostfs/sdk/placement/Tools.java
Ori Bruk 6546e98cf6 [#52] Add placement vector
Signed-off-by: Ori Bruk <o.bruk@yadro.com>
2025-04-23 10:41:12 +03:00

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;
}
}
}