protected override IterativeStatistics SolveInternal(int maxIterations, Func <IVector> zeroVectorInitializer)
        {
            iteration = 0;
            Preconditioner.SolveLinearSystem(residual, precondResidual);

            // d0 = s0 = inv(M) * r0
            //direction.CopyFrom(precondResidual);
            //Preconditioner.SolveLinearSystem(residual, direction);
            UpdateDirectionVector(precondResidual, direction);

            // q0 = A * d0
            Matrix.Multiply(direction, matrixTimesDirection);
            DirectionTimesMatrixTimesDirection = direction.DotProduct(matrixTimesDirection);

            // Update the direction vectors cache
            ReorthoCache.StoreDirectionData(this);

            // δnew = δ0 = r0 * s0 = r0 * d0
            resDotPrecondRes = residual.DotProduct(direction);

            // The convergence strategy must be initialized immediately after the first r and r*inv(M)*r are computed.
            Convergence.Initialize(this);
            Stagnation.StoreInitialError(Convergence.EstimateResidualNormRatio(this));

            // This is also used as output
            double residualNormRatio = double.NaN;

            // α0 = (d0 * r0) / (d0 * q0) = (s0 * r0) / (d0 * (A * d0))
            stepSize = resDotPrecondRes / DirectionTimesMatrixTimesDirection;

            for (iteration = 1; iteration < maxIterations; ++iteration)
            {
                // x = x + α * d
                solution.AxpyIntoThis(direction, stepSize);

                // Normally the residual vector is updated as: r = r - α * q. However corrections might need to be applied.
                residualUpdater.UpdateResidual(this, residual);

                // s = inv(M) * r
                Preconditioner.SolveLinearSystem(residual, precondResidual);

                // δold = δnew
                resDotPrecondResOld = resDotPrecondRes;

                // δnew = r * s
                resDotPrecondRes = residual.DotProduct(precondResidual);

                /// At this point we can check if CG has converged and exit, thus avoiding the uneccesary operations that follow.
                residualNormRatio = Convergence.EstimateResidualNormRatio(this);
                //Debug.WriteLine($"Reorthogonalized PCG iteration = {iteration}: residual norm ratio = {residualNormRatio}");
                Stagnation.StoreNewError(residualNormRatio);
                bool hasStagnated = Stagnation.HasStagnated();
                if (residualNormRatio <= ResidualTolerance)
                {
                    return(new IterativeStatistics
                    {
                        AlgorithmName = name,
                        HasConverged = true,
                        HasStagnated = false,
                        NumIterationsRequired = iteration + 1,
                        ResidualNormRatioEstimation = residualNormRatio
                    });
                }
                if (hasStagnated)
                {
                    return(new IterativeStatistics
                    {
                        AlgorithmName = name,
                        HasConverged = false,
                        HasStagnated = true,
                        NumIterationsRequired = iteration + 1,
                        ResidualNormRatioEstimation = residualNormRatio
                    });
                }

                // Update the direction vector using previous cached direction vectors.
                UpdateDirectionVector(precondResidual, direction);

                // q = A * d
                Matrix.Multiply(direction, matrixTimesDirection);
                DirectionTimesMatrixTimesDirection = direction.DotProduct(matrixTimesDirection);

                // Update the direction vectors cache
                ReorthoCache.StoreDirectionData(this);

                // α = (d * r) / (d * q) = (d * r) / (d * (A * d))
                stepSize = direction.DotProduct(residual) / DirectionTimesMatrixTimesDirection;
            }

            // We reached the max iterations before PCG converged
            return(new IterativeStatistics
            {
                AlgorithmName = name,
                HasConverged = false,
                HasStagnated = false,
                NumIterationsRequired = maxIterations,
                ResidualNormRatioEstimation = residualNormRatio
            });
        }