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 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 List sortHasherSliceByWeightValue(List 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 > List sortHasherByDistance( List nodes, N[] dist, boolean asc ) { IndexedValue[] 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 result = new ArrayList<>(); for (IndexedValue iv : indexes) { result.add(iv.nodeInfo); } return result; } public static Function defaultWeightFunc(List 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 newWeightFunc(Normalizer capNorm, Normalizer priceNorm) { return nodeInfo -> capNorm.normalize(nodeInfo.getCapacity().doubleValue()) * priceNorm.normalize(nodeInfo.getPrice().doubleValue()); } private static class IndexedValue { final T nodeInfo; final N dist; IndexedValue(T nodeInfo, N dist) { this.nodeInfo = nodeInfo; this.dist = dist; } } }