/// <summary> /// Searches for the given object. /// </summary> /// <param name = "toFind">The object to search for.</param> /// <param name = "range">The range in which to search.</param> /// <param name = "indexOperations">Operations that can be done on an indexer.</param> /// <returns>The found object, or it's nearest matches.</returns> public static BinarySearchResult <TObject> Search( TObject toFind, IInterval <TIndex> range, IndexerDelegates <TObject, TIndex> indexOperations) { // Make sure start and end of range are valid. // TODO: This is only necessary the first call of the recursion. Implementing the search as a loop could be more performant. if (range.Start.CompareTo(indexOperations.GetNearestIndex(range.Start)) != 0 || range.End.CompareTo(indexOperations.GetNearestIndex(range.End)) != 0) { throw new ArgumentException("The start and end of the range should be valid indices.", nameof(range)); } // Get object near the center of the range. TIndex center = indexOperations.GetNearestIndex(range.Center); TObject centerObject = indexOperations.GetByIndex(center); // Check whether desired object was found. int orderToCenter = toFind.CompareTo(centerObject); // See whether finished. bool isObjectFound = orderToCenter == 0; bool hasNoMoreObjects = IndexComparer.Equals(center, range.Start) || IndexComparer.Equals(center, range.End); bool isFinished = isObjectFound || hasNoMoreObjects; if (!isFinished) { // Split interval in the middle. range.Split(center, SplitOption.Both, out var smaller, out var bigger); // Continue recursively in the range in which the object lies. IInterval <TIndex> inRange = orderToCenter > 0 ? bigger : smaller; return(Search(toFind, inRange, indexOperations)); } else { TObject smaller = indexOperations.GetByIndex(range.Start); TObject bigger = indexOperations.GetByIndex(range.End); // Find the desired object. var foundObject = default(TObject); if (isObjectFound) { foundObject = centerObject; } else { if (toFind.CompareTo(smaller) == 0) { isObjectFound = true; foundObject = smaller; } else if (toFind.CompareTo(bigger) == 0) { isObjectFound = true; foundObject = bigger; } } // Return result. bool isObjectInRange = toFind.CompareTo(smaller) >= 0 && toFind.CompareTo(bigger) <= 0; return(new BinarySearchResult <TObject> { IsObjectInRange = isObjectInRange, IsObjectFound = isObjectFound, Found = isObjectFound ? new BinarySearchResult <TObject> .FoundResult(foundObject) : null, NotFound = isObjectFound || !isObjectInRange ? null : new BinarySearchResult <TObject> .NotFoundResult(smaller, bigger) }); } }