// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; namespace osu.Game.Utils { public class BinarySearchUtils { /// /// Finds the index of the item in the sorted list which has its property equal to the search term. /// If no exact match is found, the complement of the index of the first item greater than the search term will be returned. /// /// The type of the items in the list to search. /// The type of the property to perform the search on. /// The list of items to search. /// The query to find. /// Function that maps an item in the list to its index property. /// Determines which index to return if there are multiple exact matches. /// The index of the found item. Will return the complement of the index of the first item greater than the search query if no exact match is found. public static int BinarySearch(IReadOnlyList list, T2 searchTerm, Func termFunc, EqualitySelection equalitySelection = EqualitySelection.FirstFound) { int n = list.Count; if (n == 0) return -1; var comparer = Comparer.Default; if (comparer.Compare(searchTerm, termFunc(list[0])) == -1) return -1; if (comparer.Compare(searchTerm, termFunc(list[^1])) == 1) return ~n; int min = 0; int max = n - 1; bool equalityFound = false; while (min <= max) { int mid = min + (max - min) / 2; T2 midTerm = termFunc(list[mid]); switch (comparer.Compare(midTerm, searchTerm)) { case 0: equalityFound = true; switch (equalitySelection) { case EqualitySelection.Leftmost: max = mid - 1; break; case EqualitySelection.Rightmost: min = mid + 1; break; default: case EqualitySelection.FirstFound: return mid; } break; case 1: max = mid - 1; break; case -1: min = mid + 1; break; } } if (!equalityFound) return ~min; switch (equalitySelection) { case EqualitySelection.Leftmost: return min; case EqualitySelection.Rightmost: return min - 1; } return ~min; } } public enum EqualitySelection { FirstFound, Leftmost, Rightmost } }