| 
						
						
							
								
							
						
						
					 | 
					 | 
					@ -11,18 +11,15 @@ namespace ZeroLevel.HNSW
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    internal sealed class ReadOnlyLayer<TItem>
 | 
					 | 
					 | 
					 | 
					    internal sealed class ReadOnlyLayer<TItem>
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        : IBinarySerializable
 | 
					 | 
					 | 
					 | 
					        : IBinarySerializable
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    {
 | 
					 | 
					 | 
					 | 
					    {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        private readonly NSWReadOnlyOption<TItem> _options;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        private readonly ReadOnlyVectorSet<TItem> _vectors;
 | 
					 | 
					 | 
					 | 
					        private readonly ReadOnlyVectorSet<TItem> _vectors;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        private readonly ReadOnlyCompactBiDirectionalLinksSet _links;
 | 
					 | 
					 | 
					 | 
					        private readonly ReadOnlyCompactBiDirectionalLinksSet _links;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <summary>
 | 
					 | 
					 | 
					 | 
					        /// <summary>
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// HNSW layer
 | 
					 | 
					 | 
					 | 
					        /// HNSW layer
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// </summary>
 | 
					 | 
					 | 
					 | 
					        /// </summary>
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <param name="options">HNSW graph options</param>
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <param name="vectors">General vector set</param>
 | 
					 | 
					 | 
					 | 
					        /// <param name="vectors">General vector set</param>
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        internal ReadOnlyLayer(NSWReadOnlyOption<TItem> options, ReadOnlyVectorSet<TItem> vectors)
 | 
					 | 
					 | 
					 | 
					        internal ReadOnlyLayer(ReadOnlyVectorSet<TItem> vectors)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        {
 | 
					 | 
					 | 
					 | 
					        {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            _options = options;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            _vectors = vectors;
 | 
					 | 
					 | 
					 | 
					            _vectors = vectors;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            _links = new ReadOnlyCompactBiDirectionalLinksSet();
 | 
					 | 
					 | 
					 | 
					            _links = new ReadOnlyCompactBiDirectionalLinksSet();
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        }
 | 
					 | 
					 | 
					 | 
					        }
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -114,7 +111,7 @@ namespace ZeroLevel.HNSW
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <param name="q">query element</param>
 | 
					 | 
					 | 
					 | 
					        /// <param name="q">query element</param>
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <param name="ep">enter points ep</param>
 | 
					 | 
					 | 
					 | 
					        /// <param name="ep">enter points ep</param>
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <returns>Output: ef closest neighbors to q</returns>
 | 
					 | 
					 | 
					 | 
					        /// <returns>Output: ef closest neighbors to q</returns>
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        internal void KNearestAtLayer(int entryPointId, Func<int, float> targetCosts, IDictionary<int, float> W, int ef, HashSet<int> activeNodes)
 | 
					 | 
					 | 
					 | 
					        internal void KNearestAtLayer(int entryPointId, Func<int, float> targetCosts, IDictionary<int, float> W, int ef, SearchContext context)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        {
 | 
					 | 
					 | 
					 | 
					        {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            /*
 | 
					 | 
					 | 
					 | 
					            /*
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					             * v ← ep // set of visited elements
 | 
					 | 
					 | 
					 | 
					             * v ← ep // set of visited elements
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -143,7 +140,7 @@ namespace ZeroLevel.HNSW
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            var C = new Dictionary<int, float>();
 | 
					 | 
					 | 
					 | 
					            var C = new Dictionary<int, float>();
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            C.Add(entryPointId, targetCosts(entryPointId));
 | 
					 | 
					 | 
					 | 
					            C.Add(entryPointId, targetCosts(entryPointId));
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // W ← ep // dynamic list of found nearest neighbors
 | 
					 | 
					 | 
					 | 
					            // W ← ep // dynamic list of found nearest neighbors
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (activeNodes.Contains(entryPointId))
 | 
					 | 
					 | 
					 | 
					            if (context.IsActiveNode(entryPointId))
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            {
 | 
					 | 
					 | 
					 | 
					            {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                W.Add(entryPointId, C[entryPointId]);
 | 
					 | 
					 | 
					 | 
					                W.Add(entryPointId, C[entryPointId]);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            }
 | 
					 | 
					 | 
					 | 
					            }
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -173,7 +170,7 @@ namespace ZeroLevel.HNSW
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    {
 | 
					 | 
					 | 
					 | 
					                    {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        // enqueue perspective neighbours to expansion list
 | 
					 | 
					 | 
					 | 
					                        // enqueue perspective neighbours to expansion list
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        var neighbourDistance = targetCosts(neighbourId);
 | 
					 | 
					 | 
					 | 
					                        var neighbourDistance = targetCosts(neighbourId);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        if (activeNodes.Contains(neighbourId))
 | 
					 | 
					 | 
					 | 
					                        if (context.IsActiveNode(neighbourId))
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        {
 | 
					 | 
					 | 
					 | 
					                        {
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            if (W.Count < ef || (W.Count > 0 && neighbourDistance < farthestDistance()))
 | 
					 | 
					 | 
					 | 
					                            if (W.Count < ef || (W.Count > 0 && neighbourDistance < farthestDistance()))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            {
 | 
					 | 
					 | 
					 | 
					                            {
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -195,110 +192,6 @@ namespace ZeroLevel.HNSW
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            C.Clear();
 | 
					 | 
					 | 
					 | 
					            C.Clear();
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            v.Clear();
 | 
					 | 
					 | 
					 | 
					            v.Clear();
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        }
 | 
					 | 
					 | 
					 | 
					        }
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <summary>
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// Algorithm 3
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// </summary>
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        internal IDictionary<int, float> SELECT_NEIGHBORS_SIMPLE(Func<int, float> distance, IDictionary<int, float> candidates, int M)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            var bestN = M;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            var W = new Dictionary<int, float>(candidates);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (W.Count > bestN)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                var popFarther = new Action(() => { var pair = W.OrderByDescending(e => e.Value).First(); W.Remove(pair.Key); });
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                while (W.Count > bestN)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    popFarther();
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // return M nearest elements from C to q
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            return W;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <summary>
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// Algorithm 4
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// </summary>
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <param name="q">base element</param>
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <param name="C">candidate elements</param>
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <param name="extendCandidates">flag indicating whether or not to extend candidate list</param>
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <param name="keepPrunedConnections">flag indicating whether or not to add discarded elements</param>
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        /// <returns>Output: M elements selected by the heuristic</returns>
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        internal IDictionary<int, float> SELECT_NEIGHBORS_HEURISTIC(Func<int, float> distance, IDictionary<int, float> candidates, int M)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // R ← ∅
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            var R = new Dictionary<int, float>();
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // W ← C // working queue for the candidates
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            var W = new Dictionary<int, float>(candidates);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // if extendCandidates // extend candidates by their neighbors
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (_options.ExpandBestSelection)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                var extendBuffer = new HashSet<int>();
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                // for each e ∈ C
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                foreach (var e in W)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    var neighbors = GetNeighbors(e.Key);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    // for each e_adj ∈ neighbourhood(e) at layer lc
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    foreach (var e_adj in neighbors)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        // if eadj ∉ W
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        if (extendBuffer.Contains(e_adj) == false)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                            extendBuffer.Add(e_adj);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                        }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                // W ← W ⋃ eadj
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                foreach (var id in extendBuffer)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    W[id] = distance(id);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            //  Wd ← ∅ // queue for the discarded candidates
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            var Wd = new Dictionary<int, float>();
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            var popCandidate = new Func<(int, float)>(() => { var pair = W.OrderBy(e => e.Value).First(); W.Remove(pair.Key); return (pair.Key, pair.Value); });
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            var fartherFromResult = new Func<(int, float)>(() => { if (R.Count == 0) return (-1, 0f); var pair = R.OrderByDescending(e => e.Value).First(); return (pair.Key, pair.Value); });
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            var popNearestDiscarded = new Func<(int, float)>(() => { var pair = Wd.OrderBy(e => e.Value).First(); Wd.Remove(pair.Key); return (pair.Key, pair.Value); });
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // while │W│ > 0 and │R│< M
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            while (W.Count > 0 && R.Count < M)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                // e ← extract nearest element from W to q
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                var (e, ed) = popCandidate();
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                var (fe, fd) = fartherFromResult();
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                // if e is closer to q compared to any element from R
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                if (R.Count == 0 ||
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    ed < fd)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    // R ← R ⋃ e
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    R.Add(e, ed);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                else
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    // Wd ← Wd ⋃ e
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    Wd.Add(e, ed);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            // if keepPrunedConnections // add some of the discarded // connections from Wd
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if (_options.KeepPrunedConnections)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                // while │Wd│> 0 and │R│< M
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                while (Wd.Count > 0 && R.Count < M)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                {
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    // R ← R ⋃ extract nearest element from Wd to q
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    var nearest = popNearestDiscarded();
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                    R[nearest.Item1] = nearest.Item2;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            //  return R
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            return R;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        }
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        #endregion
 | 
					 | 
					 | 
					 | 
					        #endregion
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        private IEnumerable<int> GetNeighbors(int id) => _links.FindLinksForId(id);
 | 
					 | 
					 | 
					 | 
					        private IEnumerable<int> GetNeighbors(int id) => _links.FindLinksForId(id);
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					 | 
					
 
 |