/// <summary> /// Finds the closest contact in the network. Does several round trips to the network /// </summary> /// <param name="target">The target.</param> /// <returns>an IEnumerable<contact> in order of distance from the target. Can be cast into a GetClosestNodes.ClosestResults</contact></returns> public IEnumerable <Contact> GetClosestContacts(Identifier512 target, Func <Contact, bool> terminate = null) { MinMaxHeap <Contact> heap = new MinMaxHeap <Contact>(new ContactComparer(target), contacts.ClosestNodes(target).Take(RoutingTable.Configuration.LookupConcurrency)); HashSet <Identifier512> contacted = new HashSet <Identifier512>(); contacted.Add(RoutingTable.LocalIdentifier); int iterations = 0; HashSet <Contact> uniqueDiscoveries; do { iterations++; uniqueDiscoveries = new HashSet <Contact>( //hashet means we only get each result once heap //from the set of results we know about .Where(c => !contacted.Contains(c.Identifier)) //which we have not already contacted .SelectMany(c => { try { return(RemoteGetClosest(RoutingTable.LocalContact, c, target, RoutingTable.Configuration.LookupConcurrency, RoutingTable.Configuration.LookupTimeout)); } catch (TimeoutException) { return(null); } }) //select the closest ones they know about .Where(n => n != null) .Where(r => !heap.Contains(r))); //remove the results we already know about //Make the system aware of these potentially new nodes foreach (var c in uniqueDiscoveries) { contacts.Update(c); } //make sure we never contact these nodes again contacted.UnionWith(heap.Select(a => a.Identifier)); //add the new results heap.AddMany(uniqueDiscoveries); while (heap.Count > RoutingTable.Configuration.LookupConcurrency) { heap.RemoveMax(); } if (terminate != null) { if (uniqueDiscoveries.Where(a => terminate(a)).FirstOrDefault() != null) { break; } } }while (uniqueDiscoveries.Count != 0 && heap.Minimum.Identifier != target); return(new ClosestResults(heap, iterations)); //while (heap.Count > 0) // yield return heap.RemoveMin(); }