/// <inheritdoc /> public bool Equals(SortedReadOnlyArray <TValue, TComparer> other) { return(m_array == other.m_array); }
public static SortedReadOnlyArray <TValue, TComparer> Instance(TComparer comparer) => SortedReadOnlyArray <TValue, TComparer> .CloneAndSort( EmptyArray <TValue>().ToReadOnlyArray(), comparer);
private int ExceptWithVisitor(SortedReadOnlyArray <TValue, TComparer>[] others, Action <int, TValue>?visit = null) { // Number of non-excepted items seen so far (returned at the end) int size = 0; // Indicates if any of 'others' have valid cursors still (this instance may have items greater than all others). bool othersRemaining = true; // Cursor for the current item to compare with in each of the others. var otherCursors = new int[others.Length]; // We always visit every item in this array since each is potentially (independently) not in the others. for (int i = 0; i < Length;) { bool found = false; TValue thisCurrent = this[i]; // If there are others to look at, it is possible that we will find this item. if (othersRemaining) { // We now try to find the item in at least one of the others (note we don't need to advance the cursors of the rest). othersRemaining = false; for (int otherIdx = 0; !found && otherIdx < others.Length; otherIdx++) { SortedReadOnlyArray <TValue, TComparer> other = others[otherIdx]; if (other.Length == otherCursors[otherIdx]) { continue; } othersRemaining = true; if (Comparer.Compare(other[otherCursors[otherIdx]], thisCurrent) == 0) { // Exact match (fast path for heavily overlapping sets). otherCursors[otherIdx]++; found = true; } else { // This cursor may be arbitrarily behind (note that we quit looking at additional others for an item when it is first found). // Here we catch up efficiently via binary search. We either find an exact match (non-negative index) or an upper-bound // (encoded negative). int skipToEncoded = other.BinarySearch(thisCurrent, otherCursors[otherIdx], other.Length - otherCursors[otherIdx]); if (skipToEncoded >= 0) { // Exact match. The binary search may have landed in the middle of a sequence of duplicates, // but that's fine; the next cursor advance will take care of that (invariant is advancing cursor points // it at an item at least as high, but not neccesarily higher). otherCursors[otherIdx] = skipToEncoded + 1; found = true; } else { otherCursors[otherIdx] = ~skipToEncoded; } } Contract.Assume(otherCursors[otherIdx] == other.Length || Comparer.Compare(other[otherCursors[otherIdx]], thisCurrent) >= 0); } } if (!found) { visit?.Invoke(size, thisCurrent); size++; } i++; // Skip duplicates in the input sequence (this value was either excepted or not already; cursors have advanced possibly past it). while (i < Length && Comparer.Compare(thisCurrent, this[i]) == 0) { i++; } } return(size); }