/// <summary>Performs hidden subset elimination on one dimension of possible numbers.</summary> /// <param name="possibleNumbers">The row/column/box to analyze.</param> /// <returns>The number of changes that were made to the possible numbers.</returns> private int EliminateHiddenSubsets(FastBitArray [] possibleNumbers, bool exitEarlyWhenSoleFound, out bool exitedEarly) { int changesMade = 0; exitedEarly = false; int numLocations; int [] foundLocations = _foundLocations; // optimization, rather than allocating on each call // We'll look starting with each cell in the row/column/box for (int i = 0; i < possibleNumbers.Length; i++) { // Only look at the cell if it has at least subsetSize values set, // otherwise it can't be part of a hidden subset int numPossible = possibleNumbers[i].CountSet; if (numPossible >= _subsetSize) { // For each combination foreach (int [] combination in CreateCombinations(_subsetSize, possibleNumbers[i].GetSetBits())) { // Find other cells that contain that same combination, // but only up to the subset size numLocations = 0; foundLocations[numLocations++] = i; for (int j = i + 1; j < possibleNumbers.Length && numLocations < foundLocations.Length; j++) { if (AllAreSet(combination, possibleNumbers[j])) { foundLocations[numLocations++] = j; } } if (numLocations == foundLocations.Length) { bool isValidHidden = true; // Make sure that none of the numbers appear in any other cell for (int j = 0; j < possibleNumbers.Length && isValidHidden; j++) { bool isFoundLocation = Array.BinarySearch(foundLocations, j) >= 0; if (!isFoundLocation && AnyAreSet(combination, possibleNumbers[j])) { isValidHidden = false; break; } } // If this is a valid hidden subset, eliminate all other numbers // from each cell in the subset if (isValidHidden) { foreach (int foundLoc in foundLocations) { FastBitArray possibleNumbersForLoc = possibleNumbers[foundLoc]; foreach (int n in possibleNumbersForLoc.GetSetBits()) { if (Array.BinarySearch(combination, n) < 0) { possibleNumbersForLoc[n] = false; changesMade++; } } if (exitEarlyWhenSoleFound && possibleNumbersForLoc.CountSet == 1) { exitedEarly = true; return(changesMade); } } break; } } } } } return(changesMade); }