/// <summary> /// Modifies the given array in-place such that true-valued and false-valued items (determined by <paramref name="predicate" />) are contiguous rather than intermixed /// </summary> public static void Partition <TValue>( this TValue[] values, Func <TValue, bool> predicate, out ArrayView <TValue> trueValues, out ArrayView <TValue> falseValues) { Contract.RequiresNotNull(values); Contract.RequiresNotNull(predicate); if (values.Length == 0) { trueValues = ArrayView <TValue> .Empty; falseValues = ArrayView <TValue> .Empty; return; } int leftIndex = 0; int rightIndex = values.Length - 1; // left and right indices will eventually meet (immediate for a single element array). while (rightIndex != leftIndex) { // Advance the left cursor to the next out-of-place false-value. // (but don't go past right; consider an all-true array meaning that the right cursor initially also points to a true value). while (leftIndex < rightIndex && predicate(values[leftIndex])) { leftIndex++; } // Similarly for the right cursor - find the next out-of-place true-value while (leftIndex < rightIndex && !predicate(values[rightIndex])) { rightIndex--; } // Swap the out-of-place pair (each is no longer out of place). if (leftIndex != rightIndex) { TValue temp = values[leftIndex]; values[leftIndex] = values[rightIndex]; values[rightIndex] = temp; // We've established both loop conditions above: // leftIndex < rightIndex && predicate(values[leftIndex]) && !predicate(values[rightIndex]) // (due to the swap). // So, we can advance one or both cursors to avoid re-evaluating their predicates on the next iteration. leftIndex++; if (leftIndex < rightIndex) { rightIndex--; } Contract.Assert(leftIndex <= rightIndex); } } Contract.Assert( leftIndex == rightIndex, "Loop terminates when left and right cursors meet; we then have a pivot which could go in either the true or false view."); int pivotIndex = leftIndex; // The true values are [0, pivot] or [0, pivot - 1]. // Shift so that the true values are instead [0, pivot - 1] if (predicate(values[pivotIndex])) { pivotIndex++; } trueValues = new ArrayView <TValue>(values, 0, pivotIndex); falseValues = new ArrayView <TValue>(values, pivotIndex, values.Length - pivotIndex); }