    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #ifndef INCLUDED_BASEGFX_SOURCE_INC_HOMMATRIXTEMPLATE_HXX
    #define INCLUDED_BASEGFX_SOURCE_INC_HOMMATRIXTEMPLATE_HXX

    #include <sal/types.h>
    #include <basegfx/numeric/ftools.hxx>
    #include <math.h>
    #include <string.h>

    namespace basegfx
    {
        namespace internal
        {

            inline double implGetDefaultValue(sal_uInt16 nRow, sal_uInt16 nColumn)
            {
                if(nRow == nColumn)
                    return 1.0;
                return 0.0;
            }

            template < sal_uInt16 RowSize > class ImplMatLine
            {
                double                                          mfValue[RowSize];

            public:
                ImplMatLine()
                {
                }

                explicit ImplMatLine(sal_uInt16 nRow, ImplMatLine< RowSize >* pToBeCopied)
                {
                    if(pToBeCopied)
                    {
                        memcpy(&mfValue, pToBeCopied, sizeof(double) * RowSize);
                    }
                    else
                    {
                        for(sal_uInt16 a(0); a < RowSize; a++)
                        {
                            mfValue[a] = implGetDefaultValue(nRow, a);
                        }
                    }
                }

                double get(sal_uInt16 nColumn) const
                {
                    return mfValue[nColumn];
                }

                void set(sal_uInt16 nColumn, const double& rValue)
                {
                    mfValue[nColumn] = rValue;
                }
            };

            template < sal_uInt16 RowSize > class ImplHomMatrixTemplate
            {
                ImplMatLine< RowSize >                          maLine[RowSize - 1];
                std::unique_ptr<ImplMatLine< RowSize >> mutable mpLine;

            public:
                // Is last line used?
                bool isLastLineDefault() const
                {
                    if(!mpLine)
                        return true;

                    for(sal_uInt16 a(0); a < RowSize; a++)
                    {
                        const double fDefault(implGetDefaultValue((RowSize - 1), a));
                        const double fLineValue(mpLine->get(a));

                        if(!::basegfx::fTools::equal(fDefault, fLineValue))
                        {
                            return false;
                        }
                    }

                    // reset last line, it equals default
                    mpLine.reset();

                    return true;
                }

                ImplHomMatrixTemplate()
                {
                    // complete initialization with identity matrix, all lines
                    // were initialized with a trailing 1 followed by 0's.
                    for(sal_uInt16 a(0); a < RowSize-1; a++)
                    {
                        for(sal_uInt16 b(0); b < RowSize; b++)
                            maLine[a].set(b, implGetDefaultValue(a, b) );
                    }
                }

                ImplHomMatrixTemplate(const ImplHomMatrixTemplate& rToBeCopied)
                {
                    operator=(rToBeCopied);
                }

                ImplHomMatrixTemplate& operator=(const ImplHomMatrixTemplate& rToBeCopied)
                {
                    // complete initialization using copy
                    for(sal_uInt16 a(0); a < (RowSize - 1); a++)
                    {
                        memcpy(&maLine[a], &rToBeCopied.maLine[a], sizeof(ImplMatLine< RowSize >));
                    }
                    if(rToBeCopied.mpLine)
                    {
                        mpLine.reset( new ImplMatLine< RowSize >((RowSize - 1), rToBeCopied.mpLine.get()) );
                    }
                    return *this;
                }

                static sal_uInt16 getEdgeLength() { return RowSize; }

                double get(sal_uInt16 nRow, sal_uInt16 nColumn) const
                {
                    if(nRow < (RowSize - 1))
                    {
                        return maLine[nRow].get(nColumn);
                    }

                    if(mpLine)
                    {
                        return mpLine->get(nColumn);
                    }

                    return implGetDefaultValue((RowSize - 1), nColumn);
                }

                void set(sal_uInt16 nRow, sal_uInt16 nColumn, const double& rValue)
                {
                    if(nRow < (RowSize - 1))
                    {
                        maLine[nRow].set(nColumn, rValue);
                    }
                    else if(mpLine)
                    {
                        mpLine->set(nColumn, rValue);
                    }
                    else
                    {
                        const double fDefault(implGetDefaultValue((RowSize - 1), nColumn));

                        if(!::basegfx::fTools::equal(fDefault, rValue))
                        {
                            mpLine.reset(new ImplMatLine< RowSize >((RowSize - 1), nullptr));
                            mpLine->set(nColumn, rValue);
                        }
                    }
                }

                void testLastLine()
                {
                    if(mpLine)
                    {
                        bool bNecessary(false);

                        for(sal_uInt16 a(0);!bNecessary && a < RowSize; a++)
                        {
                            const double fDefault(implGetDefaultValue((RowSize - 1), a));
                            const double fLineValue(mpLine->get(a));

                            if(!::basegfx::fTools::equal(fDefault, fLineValue))
                            {
                                bNecessary = true;
                            }
                        }

                        if(!bNecessary)
                        {
                            mpLine.reset();
                        }
                    }
                }

                // Left-upper decomposition
                bool ludcmp(sal_uInt16 nIndex[], sal_Int16& nParity)
                {
                    double fBig, fSum, fDum;
                    double fStorage[RowSize];
                    sal_uInt16 a, b, c;

                    // #i30874# Initialize nAMax (compiler warns)
                    sal_uInt16 nAMax = 0;

                    nParity = 1;

                    // Calc the max of each line. If a line is empty,
                    // stop immediately since matrix is not invertible then.
                    for(a = 0; a < RowSize; a++)
                    {
                        fBig = 0.0;

                        for(b = 0; b < RowSize; b++)
                        {
                            double fTemp(fabs(get(a, b)));

                            if(::basegfx::fTools::more(fTemp, fBig))
                            {
                                fBig = fTemp;
                            }
                        }

                        if(::basegfx::fTools::equalZero(fBig))
                        {
                            return false;
                        }

                        fStorage[a] = 1.0 / fBig;
                    }

                    // start normalizing
                    for(b = 0; b < RowSize; b++)
                    {
                        for(a = 0; a < b; a++)
                        {
                            fSum = get(a, b);

                            for(c = 0; c < a; c++)
                            {
                                fSum -= get(a, c) * get(c, b);
                            }

                            set(a, b, fSum);
                        }

                        fBig = 0.0;

                        for(a = b; a < RowSize; a++)
                        {
                            fSum = get(a, b);

                            for(c = 0; c < b; c++)
                            {
                                fSum -= get(a, c) * get(c, b);
                            }

                            set(a, b, fSum);
                            fDum = fStorage[a] * fabs(fSum);

                            if(::basegfx::fTools::moreOrEqual(fDum, fBig))
                            {
                                fBig = fDum;
                                nAMax = a;
                            }
                        }

                        if(b != nAMax)
                        {
                            for(c = 0; c < RowSize; c++)
                            {
                                fDum = get(nAMax, c);
                                set(nAMax, c, get(b, c));
                                set(b, c, fDum);
                            }

                            nParity = -nParity;
                            fStorage[nAMax] = fStorage[b];
                        }

                        nIndex[b] = nAMax;

                        // here the failure of precision occurs
                        const double fValBB(fabs(get(b, b)));

                        if(::basegfx::fTools::equalZero(fValBB))
                        {
                            return false;
                        }

                        if(b != (RowSize - 1))
                        {
                            fDum = 1.0 / get(b, b);

                            for(a = b + 1; a < RowSize; a++)
                            {
                                set(a, b, get(a, b) * fDum);
                            }
                        }
                    }

                    return true;
                }

                void lubksb(const sal_uInt16 nIndex[], double fRow[]) const
                {
                    sal_uInt16 b, ip;
                    sal_Int16 a, a2 = -1;
                    double fSum;

                    for(a = 0; a < RowSize; a++)
                    {
                        ip = nIndex[a];
                        fSum = fRow[ip];
                        fRow[ip] = fRow[a];

                        if(a2 >= 0)
                        {
                            for(b = a2; b < a; b++)
                            {
                                fSum -= get(a, b) * fRow[b];
                            }
                        }
                        else if(!::basegfx::fTools::equalZero(fSum))
                        {
                            a2 = a;
                        }

                        fRow[a] = fSum;
                    }

                    for(a = (RowSize - 1); a >= 0; a--)
                    {
                        fSum = fRow[a];

                        for(b = a + 1; b < RowSize; b++)
                        {
                            fSum -= get(a, b) * fRow[b];
                        }

                        const double fValueAA(get(a, a));

                        if(!::basegfx::fTools::equalZero(fValueAA))
                        {
                            fRow[a] = fSum / get(a, a);
                        }
                    }
                }

                bool isIdentity() const
                {
                    // last line needs no testing if not existing
                    const sal_uInt16 nMaxLine(
                        sal::static_int_cast<sal_uInt16>(mpLine ? RowSize : (RowSize - 1)) );

                    for(sal_uInt16 a(0); a < nMaxLine; a++)
                    {
                        for(sal_uInt16 b(0); b < RowSize; b++)
                        {
                            const double fDefault(implGetDefaultValue(a, b));
                            const double fValueAB(get(a, b));

                            if(!::basegfx::fTools::equal(fDefault, fValueAB))
                            {
                                return false;
                            }
                        }
                    }

                    return true;
                }

                bool isInvertible() const
                {
                    ImplHomMatrixTemplate aWork(*this);
                    sal_uInt16 nIndex[RowSize];
                    sal_Int16 nParity;

                    return aWork.ludcmp(nIndex, nParity);
                }

                void doInvert(const ImplHomMatrixTemplate& rWork, const sal_uInt16 nIndex[])
                {
                    double fArray[RowSize];

                    for(sal_uInt16 a(0); a < RowSize; a++)
                    {
                        // prepare line
                sal_uInt16 b;
                        for( b = 0; b < RowSize; b++)
                        {
                            fArray[b] = implGetDefaultValue(a, b);
                        }

                        // expand line
                        rWork.lubksb(nIndex, fArray);

                        // copy line transposed to this matrix
                        for( b = 0; b < RowSize; b++)
                        {
                            set(b, a, fArray[b]);
                        }
                    }

                    // evtl. get rid of last matrix line
                    testLastLine();
                }

                double doDeterminant() const
                {
                    ImplHomMatrixTemplate aWork(*this);
                    sal_uInt16 nIndex[RowSize];
                    sal_Int16 nParity;
                    double fRetval(0.0);

                    if(aWork.ludcmp(nIndex, nParity))
                    {
                        fRetval = static_cast<double>(nParity);

                        // last line needs no multiply if not existing; default value would be 1.
                        const sal_uInt16 nMaxLine(
                            sal::static_int_cast<sal_uInt16>(aWork.mpLine ? RowSize : (RowSize - 1)) );

                        for(sal_uInt16 a(0); a < nMaxLine; a++)
                        {
                            fRetval *= aWork.get(a, a);
                        }
                    }

                    return fRetval;
                }

                void doAddMatrix(const ImplHomMatrixTemplate& rMat)
                {
                    for(sal_uInt16 a(0); a < RowSize; a++)
                    {
                        for(sal_uInt16 b(0); b < RowSize; b++)
                        {
                            set(a, b, get(a, b) + rMat.get(a, b));
                        }
                    }

                    testLastLine();
                }

                void doSubMatrix(const ImplHomMatrixTemplate& rMat)
                {
                    for(sal_uInt16 a(0); a < RowSize; a++)
                    {
                        for(sal_uInt16 b(0); b < RowSize; b++)
                        {
                            set(a, b, get(a, b) - rMat.get(a, b));
                        }
                    }

                    testLastLine();
                }

                void doMulMatrix(const double& rfValue)
                {
                    for(sal_uInt16 a(0); a < RowSize; a++)
                    {
                        for(sal_uInt16 b(0); b < RowSize; b++)
                        {
                            set(a, b, get(a, b) * rfValue);
                        }
                    }

                    testLastLine();
                }

                void doMulMatrix(const ImplHomMatrixTemplate& rMat)
                {
                    // create a copy as source for the original values
                    const ImplHomMatrixTemplate aCopy(*this);

                    // TODO: maybe optimize cases where last line is [0 0 1].

                    double fValue(0.0);

                    for(sal_uInt16 a(0); a < RowSize; ++a)
                    {
                        for(sal_uInt16 b(0); b < RowSize; ++b)
                        {
                            fValue = 0.0;

                            for(sal_uInt16 c(0); c < RowSize; ++c)
                                fValue += aCopy.get(c, b) * rMat.get(a, c);

                            set(a, b, fValue);
                        }
                    }

                    testLastLine();
                }

                bool isEqual(const ImplHomMatrixTemplate& rMat) const
                {
                    const sal_uInt16 nMaxLine(
                        sal::static_int_cast<sal_uInt16>((mpLine || rMat.mpLine) ? RowSize : (RowSize - 1)) );

                    for(sal_uInt16 a(0); a < nMaxLine; a++)
                    {
                        for(sal_uInt16 b(0); b < RowSize; b++)
                        {
                            const double fValueA(get(a, b));
                            const double fValueB(rMat.get(a, b));

                            if(!::basegfx::fTools::equal(fValueA, fValueB))
                            {
                                return false;
                            }
                        }
                    }

                    return true;
                }
            };

        } // namespace internal
    } // namespace basegfx

    #endif // INCLUDED_BASEGFX_SOURCE_INC_HOMMATRIXTEMPLATE_HXX

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V794 The assignment operator should be protected from the case of 'this == &rToBeCopied'.