/// <summary>Computes the sum of two specific <see cref="DenseMatrix"/> objects, i.e. A + \alpha * B.
        /// </summary>
        /// <param name="a">The first matrix, i.e. 'A'.</param>
        /// <param name="b">The second matrix, i.e. 'B'.</param>
        /// <param name="scalar">The scalar \alpha.</param>
        /// <returns>The sum A + \alpha * B in its <see cref="DenseMatrix"/> representation.</returns>
        private static DenseMatrix Add(DenseMatrix a, DenseMatrix b, double scalar)
        {
            MatrixSpecialFunction.TestAdditionInput(a, b);

            int n = a.RowCount * a.ColumnCount;
            var y = new double[n];

            int rowCount    = a.RowCount;
            int columnCount = a.ColumnCount;

            if (a.m_TransposeState == BLAS.MatrixTransposeState.NoTranspose)
            {
                switch (b.m_TransposeState)
                {
                case BLAS.MatrixTransposeState.NoTranspose:
                    BLAS.Level1.dcopy(n, a.Data, y);
                    BLAS.Level1.daxpy(n, scalar, b.Data, y);
                    break;

                case BLAS.MatrixTransposeState.Transpose:
                    BLAS.Level1.dcopy(n, a.Data, y);
                    for (int i = 0; i < rowCount; i++)
                    {
                        BLAS.Level1.daxpy(columnCount, scalar, b.Data, y, 1, rowCount, i * b.m_RowCount, i);
                    }
                    break;

                default: throw new InvalidOperationException();
                }
                return(new DenseMatrix(rowCount, columnCount, y));
            }
            else  // A^t
            {
                switch (b.m_TransposeState)
                {
                case BLAS.MatrixTransposeState.NoTranspose:
                    BLAS.Level1.dcopy(n, a.Data, y);
                    for (int i = 0; i < rowCount; i++)
                    {
                        BLAS.Level1.daxpy(columnCount, scalar, b.Data, y, 1, rowCount, i * b.m_RowCount, i);
                    }
                    break;

                case BLAS.MatrixTransposeState.Transpose:
                    BLAS.Level1.dcopy(n, a.Data, y);
                    BLAS.Level1.daxpy(n, scalar, b.Data, y);
                    break;

                default: throw new InvalidOperationException();
                }
                return(new DenseMatrix(rowCount, columnCount, y, BLAS.MatrixTransposeState.Transpose));
            }
        }
        /// <summary>Gets 'C = C + \alpha * A, where 'C' represents the matrix specified by the current instance. The current instance will be changed.
        /// </summary>
        /// <param name="a">The dense matrix 'A' to add.</param>
        /// <param name="alpha">The scalar factor \alpha.</param>
        /// <returns>The result of the operation, i.e. '\alpha * A + this', which is a reference to the current instance.</returns>
        /// <exception cref="ArgumentNullException">Thrown, if <paramref name="a"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">Thrown, if the number of rows/columns of <paramref name="a"/> does not coincide with the number of rows/columns of the matrix given by the current instance.</exception>
        /// <remarks>The current object will be changed and a reference to the current object will be returned.
        /// <para>
        ///    Use this method for some '+=' or '-=' operator which may performs better than some separate addition.
        /// </para></remarks>
        public DenseMatrix AddAssignment(DenseMatrix a, double alpha = 1.0)
        {
            MatrixSpecialFunction.TestAdditionInput(this, a);
            if (m_TransposeState == BLAS.MatrixTransposeState.NoTranspose)
            {
                switch (a.m_TransposeState)
                {
                case BLAS.MatrixTransposeState.NoTranspose:
                    BLAS.Level1.daxpy(m_RowCount * m_ColumnCount, alpha, a.m_Data, m_Data);
                    break;

                case BLAS.MatrixTransposeState.Transpose:     // c[i+j*m_RowCount] += \alpha * a[j+i*a.m_RowCount] for i=0,..,m_RowCount-1, j=0,..,m_ColumnCount-1
                    double[] aData = a.Data;
                    for (int i = 0; i < m_RowCount; i++)
                    {
                        BLAS.Level1.daxpy(m_ColumnCount, alpha, aData, m_Data, 1, m_RowCount, i * a.m_RowCount, i);
                    }
                    break;

                default: throw new InvalidOperationException();
                }
            }
            else if (m_TransposeState == BLAS.MatrixTransposeState.Transpose)
            {
                switch (a.m_TransposeState)
                {
                case BLAS.MatrixTransposeState.Transpose:
                    BLAS.Level1.daxpy(m_RowCount * m_ColumnCount, alpha, a.m_Data, m_Data);
                    break;

                case BLAS.MatrixTransposeState.NoTranspose:
                    for (int i = 0; i < m_RowCount; i++)
                    {
                        BLAS.Level1.daxpy(m_ColumnCount, alpha, a.m_Data, m_Data, a.m_RowCount, 1, i, i * m_RowCount);     //    m_Data[j + i * m_RowCount] += alpha * a.m_Data[i + j * a.m_RowCount] for j \in [0, m_ColumnCount),
                    }
                    break;

                default: throw new InvalidOperationException();
                }
            }
            else
            {
                throw new InvalidOperationException();
            }
            return(this);
        }
        /// <summary>Gets C = C + \alpha * A, where C represents the matrix specified by the current instance. The current instance will be changed.
        /// </summary>
        /// <param name="a">The general band matrix A to add.</param>
        /// <param name="alpha">The scalar factor \alpha.</param>
        /// <returns>The result of the operation, i.e. \alpha * A + this, which is just a reference of the current instance.</returns>
        /// <exception cref="ArgumentNullException">Thrown, if <paramref name="a"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">Thrown, if the number of rows/columns of A does not coincide with the number of rows/columns of the matrix given by the current instance.</exception>
        /// <remarks>The current object will be changed and a reference to the current object will be returned.
        /// <para>
        ///    Use this method for some '+=' or '-=' operator which may performs better than some separate addition.
        /// </para></remarks>
        public DenseMatrix AddAssignment(GeneralBandMatrix a, double alpha = 1.0)
        {
            MatrixSpecialFunction.TestAdditionInput(this, a);

            int superPlusSubPlusMainDiagonalCount = a.SuperDiagonalCount + a.SubDiagonalCount + 1;

            if (m_TransposeState == BLAS.MatrixTransposeState.NoTranspose)
            {
                switch (a.DataTransposeState)
                {
                case BLAS.MatrixTransposeState.NoTranspose:
                    for (int j = 0; j < m_ColumnCount; j++)
                    {
                        int iMin = System.Math.Max(0, j - a.SuperDiagonalCount);
                        int iMax = System.Math.Min(m_RowCount - 1, j + a.SubDiagonalCount);

                        for (int i = iMin; i <= iMax; i++)
                        {
                            m_Data[i + m_RowCount * j] += alpha * a.Data[j * superPlusSubPlusMainDiagonalCount + (i - j + a.SuperDiagonalCount)];
                        }
                    }
                    break;

                case BLAS.MatrixTransposeState.Transpose:
                    for (int j = 0; j < m_ColumnCount; j++)
                    {
                        int iMin = System.Math.Max(0, j - a.SuperDiagonalCount);
                        int iMax = System.Math.Min(m_RowCount - 1, j + a.SubDiagonalCount);

                        for (int i = iMin; i <= iMax; i++)
                        {
                            m_Data[i + m_RowCount * j] += alpha * a.Data[i * superPlusSubPlusMainDiagonalCount + (j - i + a.SuperDiagonalCount)];
                        }
                    }
                    break;

                default: throw new InvalidOperationException();
                }
            }
            else if (m_TransposeState == BLAS.MatrixTransposeState.Transpose)
            {
                switch (a.DataTransposeState)
                {
                case BLAS.MatrixTransposeState.NoTranspose:
                    for (int j = 0; j < ColumnCount; j++)      // Property 'ColumnCount' returns the value of m_RowCount
                    {
                        int iMin = System.Math.Max(0, j - a.SuperDiagonalCount);
                        int iMax = System.Math.Min(RowCount - 1, j + a.SubDiagonalCount);

                        for (int i = iMin; i <= iMax; i++)
                        {
                            m_Data[j + m_RowCount * i] += alpha * a.Data[j * superPlusSubPlusMainDiagonalCount + (i - j + a.SuperDiagonalCount)];
                        }
                    }
                    break;

                case BLAS.MatrixTransposeState.Transpose:
                    for (int j = 0; j < ColumnCount; j++)      // Property 'ColumnCount' returns the value of m_RowCount
                    {
                        int iMin = System.Math.Max(0, j - a.SuperDiagonalCount);
                        int iMax = System.Math.Min(RowCount - 1, j + a.SubDiagonalCount);

                        for (int i = iMin; i <= iMax; i++)
                        {
                            m_Data[j + m_RowCount * i] += alpha * a.Data[i * superPlusSubPlusMainDiagonalCount + (j - i + a.SuperDiagonalCount)];
                        }
                    }
                    break;

                default: throw new InvalidOperationException();
                }
            }
            else
            {
                throw new InvalidOperationException();
            }
            return(this);
        }
        /// <summary>The addition of two <see cref="GeneralBandMatrix"/> objects, i.e. A + \alpha * B.
        /// </summary>
        /// <param name="a">The first general band matrix A.</param>
        /// <param name="b">The second general band matrix B.</param>
        /// <param name="scalar">The scalar \alpha.</param>
        /// <returns>The result of the operator, i.e. A + \alpha * B.</returns>
        /// <exception cref="ArgumentNullException">Thrown, if <paramref name="a"/> or <paramref name="b"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">Thrown, if the number of columns of <paramref name="a"/> does not coincide with the number of rows of <paramref name="b"/>.</exception>
        private static GeneralBandMatrix Add(GeneralBandMatrix a, GeneralBandMatrix b, double scalar)
        {
            MatrixSpecialFunction.TestAdditionInput(a, b);
            int subDiagonalCount = Math.Max(a.SubDiagonalCount, b.SubDiagonalCount);
            int superDiagonalCount = Math.Max(a.SuperDiagonalCount, b.SuperDiagonalCount);

            int rowCount = a.RowCount;
            int columnCount = a.ColumnCount;

            int length = rowCount * (subDiagonalCount + superDiagonalCount + 1);
            var c = new double[length];

            switch (a.m_TransposeState)
            {
                case BLAS.MatrixTransposeState.NoTranspose:
                    if (b.m_TransposeState == BLAS.MatrixTransposeState.NoTranspose)
                    {
                        for (int j = 0; j < columnCount; j++)
                        {
                            int offSetMatrixA = j * (a.m_SubDiagonalCount + a.m_SuperDiagonalCount + 1);
                            int offSetMatrixB = j * (b.m_SubDiagonalCount + b.m_SuperDiagonalCount + 1);
                            int offsetMatrixC = j * (subDiagonalCount + superDiagonalCount + 1);

                            for (int i = Math.Max(0, j - superDiagonalCount); i <= Math.Min(rowCount - 1, j + subDiagonalCount); i++)
                            {
                                double value = 0.0;
                                if ((i - j <= a.m_SubDiagonalCount) && (j - i <= a.m_SuperDiagonalCount))
                                {
                                    value += a.m_Data[i - j + a.m_SuperDiagonalCount + offSetMatrixA];
                                }
                                if ((i - j <= b.m_SubDiagonalCount) && (j - i <= b.m_SuperDiagonalCount))
                                {
                                    value += scalar * b.m_Data[i - j + b.m_SuperDiagonalCount + offSetMatrixB];
                                }
                                c[i - j + superDiagonalCount + offsetMatrixC] = value;
                            }
                        }
                    }
                    else
                    {
                        int k = b.m_SubDiagonalCount + b.m_SuperDiagonalCount + 1;
                        for (int j = 0; j < columnCount; j++)
                        {
                            int offSetMatrixA = j * (a.m_SubDiagonalCount + a.m_SuperDiagonalCount + 1);
                            int offsetMatrixC = j * (subDiagonalCount + superDiagonalCount + 1);

                            for (int i = Math.Max(0, j - superDiagonalCount); i <= Math.Min(rowCount - 1, j + subDiagonalCount); i++)
                            {
                                double value = 0.0;
                                if ((i - j <= a.m_SubDiagonalCount) && (j - i <= a.m_SuperDiagonalCount))
                                {
                                    value += a.m_Data[i - j + a.m_SuperDiagonalCount + offSetMatrixA];
                                }
                                if ((i - j <= b.m_SubDiagonalCount) && (j - i <= b.m_SuperDiagonalCount))
                                {
                                    value += scalar * b.m_Data[j - i + b.m_SuperDiagonalCount + i * k];
                                }
                                c[i - j + superDiagonalCount + offsetMatrixC] = value;
                            }
                        }
                    }
                    break;

                case BLAS.MatrixTransposeState.Transpose:
                    if (b.m_TransposeState == BLAS.MatrixTransposeState.NoTranspose)
                    {
                        int k = a.m_SubDiagonalCount + a.m_SuperDiagonalCount + 1;

                        for (int j = 0; j < columnCount; j++)
                        {
                            int offSetMatrixB = j * (b.m_SubDiagonalCount + b.m_SuperDiagonalCount + 1);
                            int offsetMatrixC = j * (subDiagonalCount + superDiagonalCount + 1);

                            for (int i = Math.Max(0, j - superDiagonalCount); i <= Math.Min(rowCount - 1, j + subDiagonalCount); i++)
                            {
                                double value = 0.0;
                                if ((i - j <= a.m_SubDiagonalCount) && (j - i <= a.m_SuperDiagonalCount))
                                {
                                    value += a.m_Data[j - i + a.m_SuperDiagonalCount + i * k];
                                }
                                if ((i - j <= b.m_SubDiagonalCount) && (j - i <= b.m_SuperDiagonalCount))
                                {
                                    value += scalar * b.m_Data[i - j + b.m_SuperDiagonalCount + offSetMatrixB];
                                }
                                c[i - j + superDiagonalCount + offsetMatrixC] = value;
                            }
                        }
                    }
                    else
                    {
                        int ka = a.m_SubDiagonalCount + a.m_SuperDiagonalCount + 1;
                        int kb = b.m_SubDiagonalCount + b.m_SuperDiagonalCount + 1;
                        for (int j = 0; j < columnCount; j++)
                        {
                            int offsetMatrixC = j * (subDiagonalCount + superDiagonalCount + 1);

                            for (int i = Math.Max(0, j - superDiagonalCount); i <= Math.Min(rowCount - 1, j + subDiagonalCount); i++)
                            {
                                double value = 0.0;
                                if ((i - j <= a.m_SubDiagonalCount) && (j - i <= a.m_SuperDiagonalCount))
                                {
                                    value += a.m_Data[j - i + a.m_SuperDiagonalCount + i * ka];
                                }
                                if ((i - j <= b.m_SubDiagonalCount) && (j - i <= b.m_SuperDiagonalCount))
                                {
                                    value += scalar * b.m_Data[j - i + b.m_SuperDiagonalCount + i * kb];
                                }
                                c[i - j + superDiagonalCount + offsetMatrixC] = value;
                            }
                        }
                    }
                    break;

                default: throw new InvalidOperationException();
            }
            return new GeneralBandMatrix(rowCount, columnCount, subDiagonalCount, superDiagonalCount, c);
        }