package lbms.plugins.mldht.kad.tasks;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.stream.Stream;
import lbms.plugins.mldht.kad.KBucketEntry;
import lbms.plugins.mldht.kad.Key;
import lbms.plugins.mldht.kad.RPCCall;
import lbms.plugins.mldht.kad.RPCState;
import lbms.plugins.mldht.kad.tasks.Task.RequestPermit;
/* algo:
* 1. check termination condition
* 2. allow if free slot
* 3. if stall slot check
* a) is candidate better than non-stalled in flight
* b) is candidate better than head (homing phase)
* c) is candidate better than tail (stabilizing phase)
*/
public class RequestCandidateEvaluator {
final Task t;
final Key target;
final ClosestSet closest;
final KBucketEntry candidate;
final Collection<RPCCall> inFlight;
final IterativeLookupCandidates todo;
public RequestCandidateEvaluator(TargetedTask t, ClosestSet c, IterativeLookupCandidates todo, KBucketEntry cand, Collection<RPCCall> inFlight) {
Objects.requireNonNull(cand);
this.t = t;
this.todo = todo;
this.target = t.getTargetKey();
this.closest = c;
this.candidate = cand;
this.inFlight = inFlight;
}
private Stream<Key> activeInFlight() {
return inFlight.stream().filter(c -> {
RPCState state = c.state();
return state == RPCState.UNSENT || state == RPCState.SENT;
}).map(RPCCall::getExpectedID);
}
private boolean inStabilization() {
int[] suggestedCounts = closest.entries().mapToInt((k) -> {return todo.nodeForEntry(k).sources.size();}).toArray();
return Arrays.stream(suggestedCounts).anyMatch(i -> i >= 5) || Arrays.stream(suggestedCounts).filter(i -> i >= 4).count() >= 2;
}
public boolean candidateAheadOfClosestSet() {
return !closest.reachedTargetCapacity() || target.threeWayDistance(closest.head(), candidate.getID()) > 0;
}
public boolean candidateAheadOfClosestSetTail() {
return !closest.reachedTargetCapacity() || target.threeWayDistance(closest.tail(), candidate.getID()) > 0;
}
public int activeInFlightBetterThanCandidate() {
return (int) activeInFlight().filter(k -> target.threeWayDistance(k, candidate.getID()) < 0).count();
}
public boolean terminationPrecondition() {
return !candidateAheadOfClosestSetTail() && (inStabilization() || closest.insertAttemptsSinceTailModification > closest.targetSize);
}
@Override
public String toString() {
return t.age().toMillis() +" "+ t.counts + " " + closest + " cand:" + candidate.getID().findApproxKeyDistance(target);
}
public boolean goodForRequest(RequestPermit p) {
if(p == RequestPermit.NONE_ALLOWED)
return false;
boolean result = false;
if(candidateAheadOfClosestSet())
result = true;
if(candidateAheadOfClosestSetTail() && inStabilization())
result = true;
if(!terminationPrecondition() && activeInFlight().count() == 0)
result = true;
//if(result)
// System.out.println("\n" + t.age() + " " + t.counts+ "\n"+((IteratingTask)t).closestDebug());
return result;
}
}