/// <summary>
        /// Fills an existing <see cref="SudokuPuzzle"/> with numbers.
        /// </summary>
        /// <param name="sudoku">The puzzle to fill with numbers.</param>
        /// <exception cref="ArgumentNullException"><c><paramref name="sudoku"/></c> is <c>null</c>.</exception>
        public static void AddNumbers(SudokuPuzzle sudoku)
        {
            if (sudoku is null)
            {
                throw new ArgumentNullException(nameof(sudoku));
            }

            sudoku.DisablePossible = true;
            SudokuSolver.RecursiveSolve(sudoku);
            sudoku.DisablePossible = false;
            sudoku.ResetPossible();
            sudoku.ClearReadOnlyProperties();
            sudoku.SetSolutions();
        }
        /// <summary>
        /// Parses the text representing a single <see cref="SudokuPuzzle"/>.
        /// </summary>
        /// <param name="s">The text to parse.</param>
        /// <returns>A <see cref="SudokuPuzzle"/> representation of <c><paramref name="s"/></c>.</returns>
        /// <exception cref="ArgumentNullException"><c><paramref name="s"/></c> is <c>null</c>.</exception>
        /// <exception cref="FormatException"><c><paramref name="s"/></c> is not in the correct format.</exception>
        public static SudokuPuzzle Deserialize(String s)
        {
            if (s is null)
            {
                throw new ArgumentNullException(nameof(s));
            }

            String[] split = s.Split(',');

            if (split.Length != 5)
            {
                throw new FormatException();
            }

            try
            {
                int size    = Int32.Parse(split[0]);
                int squared = size * size;

                if (!SudokuPuzzle.VerifySize(size))
                {
                    throw new FormatException();
                }

                SudokuDifficulty difficulty = (SudokuDifficulty)Enum.Parse(typeof(SudokuDifficulty), split[1], true);
                int[]            numbers    = new int[squared], solutions = new int[squared];
                bool[]           readOnly   = new bool[squared];

                if (split[2].Length != squared || split[3].Length != squared || split[4].Length != squared)
                {
                    throw new FormatException();
                }

                for (int i = 0; i < squared; i++)
                {
                    numbers[i]   = split[2][i] - '0';
                    solutions[i] = split[3][i] - '0';
                    readOnly[i]  = split[3][i] == '1';
                }

                SudokuPuzzle puzzle = SudokuPuzzle.Parse(size, difficulty, numbers, solutions, readOnly);
                puzzle.ResetPossible();
                return(puzzle);
            }

            catch
            {
                throw new FormatException();
            }
        }
        /// <summary>
        /// Solve the specified <see cref="T:Sudoku.Sudoku"/> puzzle using recursion, otherwise known as "brute-force".
        /// </summary>
        /// <param name="sudoku">The <see cref="T:Sudoku.Sudoku"/> puzzle to solve.</param>
        /// <exception cref="ArgumentNullException"><c><paramref name="sudoku"/></c> is <c>null</c>.</exception>
        public static void RecursiveSolve(SudokuPuzzle sudoku)
        {
            if (sudoku is null)
            {
                throw new ArgumentNullException(nameof(sudoku));
            }

            int count = 0;

            sudoku.DisablePossible = true;
            SudokuSolver.RecursiveSolve(sudoku, 0, 0, ref count);
            sudoku.DisablePossible = false;
            sudoku.ResetPossible();
        }