Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:10:14

0001 // @(#)root/smatrix:$Id$
0002 // Author: M. Schiller    2009
0003 
0004 #ifndef ROOT_Math_CholeskyDecomp
0005 #define ROOT_Math_CholeskyDecomp
0006 
0007 /** @file
0008  * header file containing the templated implementation of matrix inversion
0009  * routines for use with ROOT's SMatrix classes (symmetric positive
0010  * definite case)
0011  *
0012  * @author Manuel Schiller
0013  * @date Aug 29 2008
0014  *    initial release inside LHCb
0015  * @date May 7 2009
0016  * factored code to provide a nice Cholesky decomposition class, along
0017  * with separate methods for solving a single linear system and to
0018  * obtain the inverse matrix from the decomposition
0019  * @date July 15th 2013
0020  *    provide a version of that class which works if the dimension of the
0021  *    problem is only known at run time
0022  * @date September 30th 2013
0023  *    provide routines to access the result of the decomposition L and its
0024  *    inverse
0025  */
0026 
0027 #include <cmath>
0028 #include <algorithm>
0029 
0030 namespace ROOT {
0031 
0032    namespace Math {
0033 
0034 /// helpers for CholeskyDecomp
0035 namespace CholeskyDecompHelpers {
0036    // forward decls
0037    template<class F, class M> struct _decomposerGenDim;
0038    template<class F, unsigned N, class M> struct _decomposer;
0039    template<class F, class M> struct _inverterGenDim;
0040    template<class F, unsigned N, class M> struct _inverter;
0041    template<class F, class V> struct _solverGenDim;
0042    template<class F, unsigned N, class V> struct _solver;
0043    template<typename G> class PackedArrayAdapter;
0044 }
0045 
0046 /// class to compute the Cholesky decomposition of a matrix
0047 /** class to compute the Cholesky decomposition of a symmetric
0048  * positive definite matrix
0049  *
0050  * provides routines to check if the decomposition succeeded (i.e. if
0051  * matrix is positive definite and non-singular), to solve a linear
0052  * system for the given matrix and to obtain its inverse
0053  *
0054  * the actual functionality is implemented in templated helper
0055  * classes which have specializations for dimensions N = 1 to 6
0056  * to achieve a gain in speed for common matrix sizes
0057  *
0058  * usage example:
0059  * @code
0060  * // let m be a symmetric positive definite SMatrix (use type float
0061  * // for internal computations, matrix size is 4x4)
0062  * CholeskyDecomp<float, 4> decomp(m);
0063  * // check if the decomposition succeeded
0064  * if (!decomp) {
0065  *   std::cerr << "decomposition failed!" << std::endl;
0066  * } else {
0067  *   // let rhs be a vector; we seek a vector x such that m * x = rhs
0068  *   decomp.Solve(rhs);
0069  *   // rhs now contains the solution we are looking for
0070  *
0071  *   // obtain the inverse of m, put it into m itself
0072  *   decomp.Invert(m);
0073  * }
0074  * @endcode
0075  */
0076 template<class F, unsigned N> class CholeskyDecomp
0077 {
0078 private:
0079    /// lower triangular matrix L
0080    /** lower triangular matrix L, packed storage, with diagonal
0081     * elements pre-inverted */
0082    F fL[N * (N + 1) / 2];
0083    /// flag indicating a successful decomposition
0084    bool fOk;
0085 public:
0086    /// perform a Cholesky decomposition
0087    /** perform a Cholesky decomposition of a symmetric positive
0088     * definite matrix m
0089     *
0090     * this is the constructor to uses with an SMatrix (and objects
0091     * that behave like an SMatrix in terms of using
0092     * operator()(int i, int j) for access to elements)
0093     */
0094    template<class M> CholeskyDecomp(const M& m) :
0095       fL( ), fOk(false)
0096    {
0097       using CholeskyDecompHelpers::_decomposer;
0098       fOk = _decomposer<F, N, M>()(fL, m);
0099    }
0100 
0101    /// perform a Cholesky decomposition
0102    /** perform a Cholesky decomposition of a symmetric positive
0103     * definite matrix m
0104     *
0105     * this is the constructor to use in special applications where
0106     * plain arrays are used
0107     *
0108     * NOTE: the matrix is given in packed representation, matrix
0109     * element m(i,j) (j <= i) is supposed to be in array element
0110     * (i * (i + 1)) / 2 + j
0111     */
0112    template<typename G> CholeskyDecomp(G* m) :
0113       fL(), fOk(false)
0114    {
0115       using CholeskyDecompHelpers::_decomposer;
0116       using CholeskyDecompHelpers::PackedArrayAdapter;
0117       fOk = _decomposer<F, N, PackedArrayAdapter<G> >()(
0118          fL, PackedArrayAdapter<G>(m));
0119    }
0120 
0121    /// returns true if decomposition was successful
0122    /** @returns true if decomposition was successful */
0123    bool ok() const { return fOk; }
0124    /// returns true if decomposition was successful
0125    /** @returns true if decomposition was successful */
0126    operator bool() const { return fOk; }
0127 
0128    /** @brief solves a linear system for the given right hand side
0129     *
0130     * Note that you can use both SVector classes and plain arrays for
0131     * rhs. (Make sure that the sizes match!). It will work with any vector
0132     * implementing the operator [i]
0133     *
0134     * @returns if the decomposition was successful
0135     */
0136    template<class V> bool Solve(V& rhs) const
0137    {
0138       using CholeskyDecompHelpers::_solver;
0139       if (fOk) _solver<F,N,V>()(rhs, fL);
0140       return fOk;
0141    }
0142 
0143    /** @brief place the inverse into m
0144     *
0145     * This is the method to use with an SMatrix.
0146     *
0147     * @returns if the decomposition was successful
0148     */
0149    template<class M> bool Invert(M& m) const
0150    {
0151       using CholeskyDecompHelpers::_inverter;
0152       if (fOk) _inverter<F,N,M>()(m, fL);
0153       return fOk;
0154    }
0155 
0156    /** @brief place the inverse into m
0157     *
0158     * This is the method to use with a plain array.
0159     *
0160     * @returns if the decomposition was successful
0161     *
0162     * NOTE: the matrix is given in packed representation, matrix
0163     * element m(i,j) (j <= i) is supposed to be in array element
0164     * (i * (i + 1)) / 2 + j
0165     */
0166    template<typename G> bool Invert(G* m) const
0167    {
0168       using CholeskyDecompHelpers::_inverter;
0169       using CholeskyDecompHelpers::PackedArrayAdapter;
0170       if (fOk) {
0171          PackedArrayAdapter<G> adapted(m);
0172          _inverter<F,N,PackedArrayAdapter<G> >()(adapted, fL);
0173       }
0174       return fOk;
0175    }
0176 
0177    /** @brief obtain the decomposed matrix L
0178     *
0179     * This is the method to use with a plain array.
0180     *
0181     * @returns if the decomposition was successful
0182     */
0183    template<class M> bool getL(M& m) const
0184    {
0185       if (!fOk) return false;
0186       for (unsigned i = 0; i < N; ++i) {
0187          // zero upper half of matrix
0188          for (unsigned j = i + 1; j < N; ++j)
0189          m(i, j) = F(0);
0190          // copy the rest
0191          for (unsigned j = 0; j <= i; ++j)
0192          m(i, j) = fL[i * (i + 1) / 2 + j];
0193          // adjust the diagonal - we save 1/L(i, i) in that position, so
0194          // convert to what caller expects
0195          m(i, i) = F(1) / m(i, i);
0196       }
0197       return true;
0198    }
0199 
0200    /** @brief obtain the decomposed matrix L
0201     *
0202     * @returns if the decomposition was successful
0203     *
0204     * NOTE: the matrix is given in packed representation, matrix
0205     * element m(i,j) (j <= i) is supposed to be in array element
0206     * (i * (i + 1)) / 2 + j
0207     */
0208    template<typename G> bool getL(G* m) const
0209    {
0210       if (!fOk) return false;
0211       // copy L
0212       for (unsigned i = 0; i < (N * (N + 1)) / 2; ++i)
0213          m[i] = fL[i];
0214       // adjust diagonal - we save 1/L(i, i) in that position, so convert to
0215       // what caller expects
0216       for (unsigned i = 0; i < N; ++i)
0217          m[(i * (i + 1)) / 2 + i] = F(1) / fL[(i * (i + 1)) / 2 + i];
0218       return true;
0219    }
0220 
0221    /** @brief obtain the inverse of the decomposed matrix L
0222     *
0223     * This is the method to use with a plain array.
0224     *
0225     * @returns if the decomposition was successful
0226     */
0227    template<class M> bool getLi(M& m) const
0228    {
0229       if (!fOk) return false;
0230       for (unsigned i = 0; i < N; ++i) {
0231          // zero lower half of matrix
0232          for (unsigned j = i + 1; j < N; ++j)
0233             m(j, i) = F(0);
0234          // copy the rest
0235          for (unsigned j = 0; j <= i; ++j)
0236          m(j, i) = fL[i * (i + 1) / 2 + j];
0237       }
0238       // invert the off-diagonal part of what we just copied
0239       for (unsigned i = 1; i < N; ++i) {
0240          for (unsigned j = 0; j < i; ++j) {
0241             typename M::value_type tmp = F(0);
0242             for (unsigned k = i; k-- > j;)
0243                tmp -= m(k, i) * m(j, k);
0244             m(j, i) = tmp * m(i, i);
0245          }
0246       }
0247       return true;
0248    }
0249 
0250    /** @brief obtain the inverse of the decomposed matrix L
0251     *
0252     * @returns if the decomposition was successful
0253     *
0254     * NOTE: the matrix is given in packed representation, matrix
0255     * element m(j,i) (j <= i) is supposed to be in array element
0256     * (i * (i + 1)) / 2 + j
0257     */
0258    template<typename G> bool getLi(G* m) const
0259    {
0260       if (!fOk) return false;
0261       // copy L
0262       for (unsigned i = 0; i < (N * (N + 1)) / 2; ++i)
0263          m[i] = fL[i];
0264       // invert the off-diagonal part of what we just copied
0265       G* base1 = &m[1];
0266       for (unsigned i = 1; i < N; base1 += ++i) {
0267          for (unsigned j = 0; j < i; ++j) {
0268             G tmp = F(0);
0269             const G *base2 = &m[(i * (i - 1)) / 2];
0270             for (unsigned k = i; k-- > j; base2 -= k)
0271             tmp -= base1[k] * base2[j];
0272             base1[j] = tmp * base1[i];
0273          }
0274       }
0275       return true;
0276    }
0277 };
0278 
0279 /// class to compute the Cholesky decomposition of a matrix
0280 /** class to compute the Cholesky decomposition of a symmetric
0281  * positive definite matrix when the dimensionality of the problem is not known
0282  * at compile time
0283  *
0284  * provides routines to check if the decomposition succeeded (i.e. if
0285  * matrix is positive definite and non-singular), to solve a linear
0286  * system for the given matrix and to obtain its inverse
0287  *
0288  * the actual functionality is implemented in templated helper
0289  * classes which have specializations for dimensions N = 1 to 6
0290  * to achieve a gain in speed for common matrix sizes
0291  *
0292  * usage example:
0293  * @code
0294  * // let m be a symmetric positive definite SMatrix (use type float
0295  * // for internal computations, matrix size is 4x4)
0296  * CholeskyDecompGenDim<float> decomp(4, m);
0297  * // check if the decomposition succeeded
0298  * if (!decomp) {
0299  *   std::cerr << "decomposition failed!" << std::endl;
0300  * } else {
0301  *   // let rhs be a vector; we seek a vector x such that m * x = rhs
0302  *   decomp.Solve(rhs);
0303  *   // rhs now contains the solution we are looking for
0304  *
0305  *   // obtain the inverse of m, put it into m itself
0306  *   decomp.Invert(m);
0307  * }
0308  * @endcode
0309  */
0310 template<class F> class CholeskyDecompGenDim
0311 {
0312 private:
0313    /** @brief dimensionality
0314     * dimensionality of the problem */
0315    unsigned fN;
0316    /// lower triangular matrix L
0317    /** lower triangular matrix L, packed storage, with diagonal
0318     * elements pre-inverted */
0319    F *fL;
0320    /// flag indicating a successful decomposition
0321    bool fOk;
0322 public:
0323    /// perform a Cholesky decomposition
0324    /** perform a Cholesky decomposition of a symmetric positive
0325     * definite matrix m
0326     *
0327     * this is the constructor to uses with an SMatrix (and objects
0328     * that behave like an SMatrix in terms of using
0329     * operator()(int i, int j) for access to elements)
0330     */
0331    template<class M> CholeskyDecompGenDim(unsigned N, const M& m) :
0332       fN(N), fL(new F[(fN * (fN + 1)) / 2]), fOk(false)
0333    {
0334       using CholeskyDecompHelpers::_decomposerGenDim;
0335       fOk = _decomposerGenDim<F, M>()(fL, m, fN);
0336    }
0337 
0338    /// perform a Cholesky decomposition
0339    /** perform a Cholesky decomposition of a symmetric positive
0340     * definite matrix m
0341     *
0342     * this is the constructor to use in special applications where
0343     * plain arrays are used
0344     *
0345     * NOTE: the matrix is given in packed representation, matrix
0346     * element m(i,j) (j <= i) is supposed to be in array element
0347     * (i * (i + 1)) / 2 + j
0348     */
0349    template<typename G> CholeskyDecompGenDim(unsigned N, G* m) :
0350       fN(N), fL(new F[(fN * (fN + 1)) / 2]), fOk(false)
0351    {
0352       using CholeskyDecompHelpers::_decomposerGenDim;
0353       using CholeskyDecompHelpers::PackedArrayAdapter;
0354       fOk = _decomposerGenDim<F, PackedArrayAdapter<G> >()(
0355          fL, PackedArrayAdapter<G>(m), fN);
0356    }
0357 
0358    /// destructor
0359    ~CholeskyDecompGenDim() { delete[] fL; }
0360 
0361    /// returns true if decomposition was successful
0362    /** @returns true if decomposition was successful */
0363    bool ok() const { return fOk; }
0364    /// returns true if decomposition was successful
0365    /** @returns true if decomposition was successful */
0366    operator bool() const { return fOk; }
0367 
0368    /** @brief solves a linear system for the given right hand side
0369     *
0370     * Note that you can use both SVector classes and plain arrays for
0371     * rhs. (Make sure that the sizes match!). It will work with any vector
0372     * implementing the operator [i]
0373     *
0374     * @returns if the decomposition was successful
0375     */
0376    template<class V> bool Solve(V& rhs) const
0377    {
0378       using CholeskyDecompHelpers::_solverGenDim;
0379       if (fOk) _solverGenDim<F,V>()(rhs, fL, fN);
0380       return fOk;
0381    }
0382 
0383    /** @brief place the inverse into m
0384     *
0385     * This is the method to use with an SMatrix.
0386     *
0387     * @returns if the decomposition was successful
0388     */
0389    template<class M> bool Invert(M& m) const
0390    {
0391       using CholeskyDecompHelpers::_inverterGenDim;
0392       if (fOk) _inverterGenDim<F,M>()(m, fL, fN);
0393       return fOk;
0394    }
0395 
0396    /** @brief place the inverse into m
0397     *
0398     * This is the method to use with a plain array.
0399     *
0400     * @returns if the decomposition was successful
0401     *
0402     * NOTE: the matrix is given in packed representation, matrix
0403     * element m(i,j) (j <= i) is supposed to be in array element
0404     * (i * (i + 1)) / 2 + j
0405     */
0406    template<typename G> bool Invert(G* m) const
0407    {
0408       using CholeskyDecompHelpers::_inverterGenDim;
0409       using CholeskyDecompHelpers::PackedArrayAdapter;
0410       if (fOk) {
0411          PackedArrayAdapter<G> adapted(m);
0412          _inverterGenDim<F,PackedArrayAdapter<G> >()(adapted, fL, fN);
0413       }
0414       return fOk;
0415    }
0416 
0417    /** @brief obtain the decomposed matrix L
0418     *
0419     * This is the method to use with a plain array.
0420     *
0421     * @returns if the decomposition was successful
0422     */
0423    template<class M> bool getL(M& m) const
0424    {
0425       if (!fOk) return false;
0426       for (unsigned i = 0; i < fN; ++i) {
0427          // zero upper half of matrix
0428          for (unsigned j = i + 1; j < fN; ++j)
0429             m(i, j) = F(0);
0430          // copy the rest
0431          for (unsigned j = 0; j <= i; ++j)
0432             m(i, j) = fL[i * (i + 1) / 2 + j];
0433          // adjust the diagonal - we save 1/L(i, i) in that position, so
0434          // convert to what caller expects
0435          m(i, i) = F(1) / m(i, i);
0436       }
0437       return true;
0438    }
0439 
0440    /** @brief obtain the decomposed matrix L
0441     *
0442     * @returns if the decomposition was successful
0443     *
0444     * NOTE: the matrix is given in packed representation, matrix
0445     * element m(i,j) (j <= i) is supposed to be in array element
0446     * (i * (i + 1)) / 2 + j
0447     */
0448    template<typename G> bool getL(G* m) const
0449    {
0450        if (!fOk) return false;
0451        // copy L
0452        for (unsigned i = 0; i < (fN * (fN + 1)) / 2; ++i)
0453           m[i] = fL[i];
0454        // adjust diagonal - we save 1/L(i, i) in that position, so convert to
0455        // what caller expects
0456        for (unsigned i = 0; i < fN; ++i)
0457           m[(i * (i + 1)) / 2 + i] = F(1) / fL[(i * (i + 1)) / 2 + i];
0458        return true;
0459    }
0460 
0461    /** @brief obtain the inverse of the decomposed matrix L
0462     *
0463     * This is the method to use with a plain array.
0464     *
0465     * @returns if the decomposition was successful
0466     */
0467    template<class M> bool getLi(M& m) const
0468    {
0469       if (!fOk) return false;
0470       for (unsigned i = 0; i < fN; ++i) {
0471          // zero lower half of matrix
0472          for (unsigned j = i + 1; j < fN; ++j)
0473             m(j, i) = F(0);
0474          // copy the rest
0475          for (unsigned j = 0; j <= i; ++j)
0476             m(j, i) = fL[i * (i + 1) / 2 + j];
0477       }
0478       // invert the off-diagonal part of what we just copied
0479       for (unsigned i = 1; i < fN; ++i) {
0480          for (unsigned j = 0; j < i; ++j) {
0481             typename M::value_type tmp = F(0);
0482             for (unsigned k = i; k-- > j;)
0483                tmp -= m(k, i) * m(j, k);
0484             m(j, i) = tmp * m(i, i);
0485          }
0486       }
0487       return true;
0488    }
0489 
0490    /** @brief obtain the inverse of the decomposed matrix L
0491     *
0492     * @returns if the decomposition was successful
0493     *
0494     * NOTE: the matrix is given in packed representation, matrix
0495     * element m(j,i) (j <= i) is supposed to be in array element
0496     * (i * (i + 1)) / 2 + j
0497     */
0498    template<typename G> bool getLi(G* m) const
0499    {
0500        if (!fOk) return false;
0501       // copy L
0502       for (unsigned i = 0; i < (fN * (fN + 1)) / 2; ++i)
0503          m[i] = fL[i];
0504       // invert the off-diagonal part of what we just copied
0505       G* base1 = &m[1];
0506       for (unsigned i = 1; i < fN; base1 += ++i) {
0507          for (unsigned j = 0; j < i; ++j) {
0508             G tmp = F(0);
0509             const G *base2 = &m[(i * (i - 1)) / 2];
0510             for (unsigned k = i; k-- > j; base2 -= k)
0511               tmp -= base1[k] * base2[j];
0512             base1[j] = tmp * base1[i];
0513          }
0514       }
0515       return true;
0516    }
0517 };
0518 
0519 namespace CholeskyDecompHelpers {
0520    /// adapter for packed arrays (to SMatrix indexing conventions)
0521    template<typename G> class PackedArrayAdapter
0522    {
0523    private:
0524       G* fArr; ///< pointer to first array element
0525    public:
0526       /// constructor
0527       PackedArrayAdapter(G* arr) : fArr(arr) {}
0528       /// read access to elements (make sure that j <= i)
0529       const G operator()(unsigned i, unsigned j) const
0530       { return fArr[((i * (i + 1)) / 2) + j]; }
0531       /// write access to elements (make sure that j <= i)
0532       G& operator()(unsigned i, unsigned j)
0533       { return fArr[((i * (i + 1)) / 2) + j]; }
0534    };
0535    /// struct to do a Cholesky decomposition (general dimensionality)
0536    template<class F, class M> struct _decomposerGenDim
0537    {
0538       /// method to do the decomposition
0539       /** @returns if the decomposition was successful */
0540       bool operator()(F* dst, const M& src, unsigned N) const
0541       {
0542          // perform Cholesky decomposition of matrix: M = L L^T
0543          // only thing that can go wrong: trying to take square
0544          // root of negative number or zero (matrix is
0545          // ill-conditioned or singular in these cases)
0546 
0547          // element L(i,j) is at array position (i * (i+1)) / 2 + j
0548 
0549          // quirk: we may need to invert L later anyway, so we can
0550          // invert elements on diagonal straight away (we only
0551          // ever need their reciprocals!)
0552 
0553          // cache starting address of rows of L for speed reasons
0554          F *base1 = &dst[0];
0555          for (unsigned i = 0; i < N; base1 += ++i) {
0556             F tmpdiag = F(0.0); // for element on diagonal
0557             // calculate off-diagonal elements
0558             F *base2 = &dst[0];
0559             for (unsigned j = 0; j < i; base2 += ++j) {
0560                F tmp = src(i, j);
0561                for (unsigned k = j; k--; )
0562                   tmp -= base1[k] * base2[k];
0563                base1[j] = tmp *= base2[j];
0564                // keep track of contribution to element on diagonal
0565                tmpdiag += tmp * tmp;
0566             }
0567             // keep truncation error small
0568             tmpdiag = src(i, i) - tmpdiag;
0569             // check if positive definite
0570             if (tmpdiag <= F(0.0)) return false;
0571             else base1[i] = std::sqrt(F(1.0) / tmpdiag);
0572          }
0573          return true;
0574       }
0575    };
0576 
0577    /// struct to do a Cholesky decomposition
0578    template<class F, unsigned N, class M> struct _decomposer
0579    {
0580       /// method to do the decomposition
0581       /** @returns if the decomposition was successful */
0582       bool operator()(F* dst, const M& src) const
0583       { return _decomposerGenDim<F, M>()(dst, src, N); }
0584    };
0585 
0586    /// struct to obtain the inverse from a Cholesky decomposition (general dimensionality)
0587    template<class F, class M> struct _inverterGenDim
0588    {
0589       /// method to do the inversion
0590       void operator()(M& dst, const F* src, unsigned N) const
0591       {
0592          // make working copy
0593          F * l = new F[N * (N + 1) / 2];
0594          std::copy(src, src + ((N * (N + 1)) / 2), l);
0595          // ok, next step: invert off-diagonal part of matrix
0596          F* base1 = &l[1];
0597          for (unsigned i = 1; i < N; base1 += ++i) {
0598             for (unsigned j = 0; j < i; ++j) {
0599                F tmp = F(0.0);
0600                const F *base2 = &l[(i * (i - 1)) / 2];
0601                for (unsigned k = i; k-- > j; base2 -= k)
0602                   tmp -= base1[k] * base2[j];
0603                base1[j] = tmp * base1[i];
0604             }
0605          }
0606 
0607          // Li = L^(-1) formed, now calculate M^(-1) = Li^T Li
0608          for (unsigned i = N; i--; ) {
0609             for (unsigned j = i + 1; j--; ) {
0610                F tmp = F(0.0);
0611                base1 = &l[(N * (N - 1)) / 2];
0612                for (unsigned k = N; k-- > i; base1 -= k)
0613                   tmp += base1[i] * base1[j];
0614                dst(i, j) = tmp;
0615             }
0616          }
0617          delete [] l;
0618       }
0619    };
0620 
0621    /// struct to obtain the inverse from a Cholesky decomposition
0622    template<class F, unsigned N, class M> struct _inverter
0623    {
0624       /// method to do the inversion
0625       void operator()(M& dst, const F* src) const
0626       { return _inverterGenDim<F, M>()(dst, src, N); }
0627    };
0628 
0629    /// struct to solve a linear system using its Cholesky decomposition (generalised dimensionality)
0630    template<class F, class V> struct _solverGenDim
0631    {
0632       /// method to solve the linear system
0633       void operator()(V& rhs, const F* l, unsigned N) const
0634       {
0635          // solve Ly = rhs
0636          for (unsigned k = 0; k < N; ++k) {
0637             const unsigned base = (k * (k + 1)) / 2;
0638             F sum = F(0.0);
0639             for (unsigned i = k; i--; )
0640                sum += rhs[i] * l[base + i];
0641             // elements on diagonal are pre-inverted!
0642             rhs[k] = (rhs[k] - sum) * l[base + k];
0643          }
0644          // solve L^Tx = y
0645          for (unsigned k = N; k--; ) {
0646             F sum = F(0.0);
0647             for (unsigned i = N; --i > k; )
0648                sum += rhs[i] * l[(i * (i + 1)) / 2 + k];
0649             // elements on diagonal are pre-inverted!
0650             rhs[k] = (rhs[k] - sum) * l[(k * (k + 1)) / 2 + k];
0651          }
0652       }
0653    };
0654 
0655    /// struct to solve a linear system using its Cholesky decomposition
0656    template<class F, unsigned N, class V> struct _solver
0657    {
0658       /// method to solve the linear system
0659       void operator()(V& rhs, const F* l) const
0660       { _solverGenDim<F, V>()(rhs, l, N); }
0661    };
0662 
0663    /// struct to do a Cholesky decomposition (specialized, N = 6)
0664    template<class F, class M> struct _decomposer<F, 6, M>
0665    {
0666       /// method to do the decomposition
0667       bool operator()(F* dst, const M& src) const
0668       {
0669          if (src(0,0) <= F(0.0)) return false;
0670          dst[0] = std::sqrt(F(1.0) / src(0,0));
0671          dst[1] = src(1,0) * dst[0];
0672          dst[2] = src(1,1) - dst[1] * dst[1];
0673          if (dst[2] <= F(0.0)) return false;
0674          else dst[2] = std::sqrt(F(1.0) / dst[2]);
0675          dst[3] = src(2,0) * dst[0];
0676          dst[4] = (src(2,1) - dst[1] * dst[3]) * dst[2];
0677          dst[5] = src(2,2) - (dst[3] * dst[3] + dst[4] * dst[4]);
0678          if (dst[5] <= F(0.0)) return false;
0679          else dst[5] = std::sqrt(F(1.0) / dst[5]);
0680          dst[6] = src(3,0) * dst[0];
0681          dst[7] = (src(3,1) - dst[1] * dst[6]) * dst[2];
0682          dst[8] = (src(3,2) - dst[3] * dst[6] - dst[4] * dst[7]) * dst[5];
0683          dst[9] = src(3,3) - (dst[6] * dst[6] + dst[7] * dst[7] + dst[8] * dst[8]);
0684          if (dst[9] <= F(0.0)) return false;
0685          else dst[9] = std::sqrt(F(1.0) / dst[9]);
0686          dst[10] = src(4,0) * dst[0];
0687          dst[11] = (src(4,1) - dst[1] * dst[10]) * dst[2];
0688          dst[12] = (src(4,2) - dst[3] * dst[10] - dst[4] * dst[11]) * dst[5];
0689          dst[13] = (src(4,3) - dst[6] * dst[10] - dst[7] * dst[11] - dst[8] * dst[12]) * dst[9];
0690          dst[14] = src(4,4) - (dst[10]*dst[10]+dst[11]*dst[11]+dst[12]*dst[12]+dst[13]*dst[13]);
0691          if (dst[14] <= F(0.0)) return false;
0692          else dst[14] = std::sqrt(F(1.0) / dst[14]);
0693          dst[15] = src(5,0) * dst[0];
0694          dst[16] = (src(5,1) - dst[1] * dst[15]) * dst[2];
0695          dst[17] = (src(5,2) - dst[3] * dst[15] - dst[4] * dst[16]) * dst[5];
0696          dst[18] = (src(5,3) - dst[6] * dst[15] - dst[7] * dst[16] - dst[8] * dst[17]) * dst[9];
0697          dst[19] = (src(5,4) - dst[10] * dst[15] - dst[11] * dst[16] - dst[12] * dst[17] - dst[13] * dst[18]) * dst[14];
0698          dst[20] = src(5,5) - (dst[15]*dst[15]+dst[16]*dst[16]+dst[17]*dst[17]+dst[18]*dst[18]+dst[19]*dst[19]);
0699          if (dst[20] <= F(0.0)) return false;
0700          else dst[20] = std::sqrt(F(1.0) / dst[20]);
0701          return true;
0702       }
0703    };
0704    /// struct to do a Cholesky decomposition (specialized, N = 5)
0705    template<class F, class M> struct _decomposer<F, 5, M>
0706    {
0707       /// method to do the decomposition
0708       bool operator()(F* dst, const M& src) const
0709       {
0710          if (src(0,0) <= F(0.0)) return false;
0711          dst[0] = std::sqrt(F(1.0) / src(0,0));
0712          dst[1] = src(1,0) * dst[0];
0713          dst[2] = src(1,1) - dst[1] * dst[1];
0714          if (dst[2] <= F(0.0)) return false;
0715          else dst[2] = std::sqrt(F(1.0) / dst[2]);
0716          dst[3] = src(2,0) * dst[0];
0717          dst[4] = (src(2,1) - dst[1] * dst[3]) * dst[2];
0718          dst[5] = src(2,2) - (dst[3] * dst[3] + dst[4] * dst[4]);
0719          if (dst[5] <= F(0.0)) return false;
0720          else dst[5] = std::sqrt(F(1.0) / dst[5]);
0721          dst[6] = src(3,0) * dst[0];
0722          dst[7] = (src(3,1) - dst[1] * dst[6]) * dst[2];
0723          dst[8] = (src(3,2) - dst[3] * dst[6] - dst[4] * dst[7]) * dst[5];
0724          dst[9] = src(3,3) - (dst[6] * dst[6] + dst[7] * dst[7] + dst[8] * dst[8]);
0725          if (dst[9] <= F(0.0)) return false;
0726          else dst[9] = std::sqrt(F(1.0) / dst[9]);
0727          dst[10] = src(4,0) * dst[0];
0728          dst[11] = (src(4,1) - dst[1] * dst[10]) * dst[2];
0729          dst[12] = (src(4,2) - dst[3] * dst[10] - dst[4] * dst[11]) * dst[5];
0730          dst[13] = (src(4,3) - dst[6] * dst[10] - dst[7] * dst[11] - dst[8] * dst[12]) * dst[9];
0731          dst[14] = src(4,4) - (dst[10]*dst[10]+dst[11]*dst[11]+dst[12]*dst[12]+dst[13]*dst[13]);
0732          if (dst[14] <= F(0.0)) return false;
0733          else dst[14] = std::sqrt(F(1.0) / dst[14]);
0734          return true;
0735       }
0736    };
0737    /// struct to do a Cholesky decomposition (specialized, N = 4)
0738    template<class F, class M> struct _decomposer<F, 4, M>
0739    {
0740       /// method to do the decomposition
0741       bool operator()(F* dst, const M& src) const
0742       {
0743          if (src(0,0) <= F(0.0)) return false;
0744          dst[0] = std::sqrt(F(1.0) / src(0,0));
0745          dst[1] = src(1,0) * dst[0];
0746          dst[2] = src(1,1) - dst[1] * dst[1];
0747          if (dst[2] <= F(0.0)) return false;
0748          else dst[2] = std::sqrt(F(1.0) / dst[2]);
0749          dst[3] = src(2,0) * dst[0];
0750          dst[4] = (src(2,1) - dst[1] * dst[3]) * dst[2];
0751          dst[5] = src(2,2) - (dst[3] * dst[3] + dst[4] * dst[4]);
0752          if (dst[5] <= F(0.0)) return false;
0753          else dst[5] = std::sqrt(F(1.0) / dst[5]);
0754          dst[6] = src(3,0) * dst[0];
0755          dst[7] = (src(3,1) - dst[1] * dst[6]) * dst[2];
0756          dst[8] = (src(3,2) - dst[3] * dst[6] - dst[4] * dst[7]) * dst[5];
0757          dst[9] = src(3,3) - (dst[6] * dst[6] + dst[7] * dst[7] + dst[8] * dst[8]);
0758          if (dst[9] <= F(0.0)) return false;
0759          else dst[9] = std::sqrt(F(1.0) / dst[9]);
0760          return true;
0761       }
0762    };
0763    /// struct to do a Cholesky decomposition (specialized, N = 3)
0764    template<class F, class M> struct _decomposer<F, 3, M>
0765    {
0766       /// method to do the decomposition
0767       bool operator()(F* dst, const M& src) const
0768       {
0769          if (src(0,0) <= F(0.0)) return false;
0770          dst[0] = std::sqrt(F(1.0) / src(0,0));
0771          dst[1] = src(1,0) * dst[0];
0772          dst[2] = src(1,1) - dst[1] * dst[1];
0773          if (dst[2] <= F(0.0)) return false;
0774          else dst[2] = std::sqrt(F(1.0) / dst[2]);
0775          dst[3] = src(2,0) * dst[0];
0776          dst[4] = (src(2,1) - dst[1] * dst[3]) * dst[2];
0777          dst[5] = src(2,2) - (dst[3] * dst[3] + dst[4] * dst[4]);
0778          if (dst[5] <= F(0.0)) return false;
0779          else dst[5] = std::sqrt(F(1.0) / dst[5]);
0780          return true;
0781       }
0782    };
0783    /// struct to do a Cholesky decomposition (specialized, N = 2)
0784    template<class F, class M> struct _decomposer<F, 2, M>
0785    {
0786       /// method to do the decomposition
0787       bool operator()(F* dst, const M& src) const
0788       {
0789          if (src(0,0) <= F(0.0)) return false;
0790          dst[0] = std::sqrt(F(1.0) / src(0,0));
0791          dst[1] = src(1,0) * dst[0];
0792          dst[2] = src(1,1) - dst[1] * dst[1];
0793          if (dst[2] <= F(0.0)) return false;
0794          else dst[2] = std::sqrt(F(1.0) / dst[2]);
0795          return true;
0796       }
0797    };
0798    /// struct to do a Cholesky decomposition (specialized, N = 1)
0799    template<class F, class M> struct _decomposer<F, 1, M>
0800    {
0801       /// method to do the decomposition
0802       bool operator()(F* dst, const M& src) const
0803       {
0804          if (src(0,0) <= F(0.0)) return false;
0805          dst[0] = std::sqrt(F(1.0) / src(0,0));
0806          return true;
0807       }
0808    };
0809    /// struct to do a Cholesky decomposition (specialized, N = 0)
0810    template<class F, class M> struct _decomposer<F, 0, M>
0811    {
0812    private:
0813       _decomposer() { };
0814       bool operator()(F* dst, const M& src) const;
0815    };
0816 
0817    /// struct to obtain the inverse from a Cholesky decomposition (N = 6)
0818    template<class F, class M> struct _inverter<F,6,M>
0819    {
0820       /// method to do the inversion
0821       inline void operator()(M& dst, const F* src) const
0822       {
0823          const F li21 = -src[1] * src[0] * src[2];
0824          const F li32 = -src[4] * src[2] * src[5];
0825          const F li31 = (src[1] * src[4] * src[2] - src[3]) * src[0] * src[5];
0826          const F li43 = -src[8] * src[9] * src[5];
0827          const F li42 = (src[4] * src[8] * src[5] - src[7]) * src[2] * src[9];
0828          const F li41 = (-src[1] * src[4] * src[8] * src[2] * src[5] +
0829                          src[1] * src[7] * src[2] + src[3] * src[8] * src[5] - src[6]) * src[0] * src[9];
0830          const F li54 = -src[13] * src[14] * src[9];
0831          const F li53 = (src[13] * src[8] * src[9] - src[12]) * src[5] * src[14];
0832          const F li52 = (-src[4] * src[8] * src[13] * src[5] * src[9] +
0833                          src[4] * src[12] * src[5] + src[7] * src[13] * src[9] - src[11]) * src[2] * src[14];
0834          const F li51 = (src[1]*src[4]*src[8]*src[13]*src[2]*src[5]*src[9] -
0835                          src[13]*src[8]*src[3]*src[9]*src[5] - src[12]*src[4]*src[1]*src[2]*src[5] - src[13]*src[7]*src[1]*src[9]*src[2] +
0836                          src[11]*src[1]*src[2] + src[12]*src[3]*src[5] + src[13]*src[6]*src[9] -src[10]) * src[0] * src[14];
0837          const F li65 = -src[19] * src[20] * src[14];
0838          const F li64 = (src[19] * src[13] * src[14] - src[18]) * src[9] * src[20];
0839          const F li63 = (-src[8] * src[13] * src[19] * src[9] * src[14] +
0840                          src[8] * src[18] * src[9] + src[12] * src[19] * src[14] - src[17]) * src[5] * src[20];
0841          const F li62 = (src[4]*src[8]*src[13]*src[19]*src[5]*src[9]*src[14] -
0842                          src[18]*src[8]*src[4]*src[9]*src[5] - src[19]*src[12]*src[4]*src[14]*src[5] -src[19]*src[13]*src[7]*src[14]*src[9] +
0843                          src[17]*src[4]*src[5] + src[18]*src[7]*src[9] + src[19]*src[11]*src[14] - src[16]) * src[2] * src[20];
0844          const F li61 = (-src[19]*src[13]*src[8]*src[4]*src[1]*src[2]*src[5]*src[9]*src[14] +
0845                          src[18]*src[8]*src[4]*src[1]*src[2]*src[5]*src[9] + src[19]*src[12]*src[4]*src[1]*src[2]*src[5]*src[14] +
0846                          src[19]*src[13]*src[7]*src[1]*src[2]*src[9]*src[14] + src[19]*src[13]*src[8]*src[3]*src[5]*src[9]*src[14] -
0847                          src[17]*src[4]*src[1]*src[2]*src[5] - src[18]*src[7]*src[1]*src[2]*src[9] - src[19]*src[11]*src[1]*src[2]*src[14] -
0848                          src[18]*src[8]*src[3]*src[5]*src[9] - src[19]*src[12]*src[3]*src[5]*src[14] - src[19]*src[13]*src[6]*src[9]*src[14] +
0849                          src[16]*src[1]*src[2] + src[17]*src[3]*src[5] + src[18]*src[6]*src[9] + src[19]*src[10]*src[14] - src[15]) *
0850             src[0] * src[20];
0851 
0852          dst(0,0) = li61*li61 + li51*li51 + li41*li41 + li31*li31 + li21*li21 + src[0]*src[0];
0853          dst(1,0) = li61*li62 + li51*li52 + li41*li42 + li31*li32 + li21*src[2];
0854          dst(1,1) = li62*li62 + li52*li52 + li42*li42 + li32*li32 + src[2]*src[2];
0855          dst(2,0) = li61*li63 + li51*li53 + li41*li43 + li31*src[5];
0856          dst(2,1) = li62*li63 + li52*li53 + li42*li43 + li32*src[5];
0857          dst(2,2) = li63*li63 + li53*li53 + li43*li43 + src[5]*src[5];
0858          dst(3,0) = li61*li64 + li51*li54 + li41*src[9];
0859          dst(3,1) = li62*li64 + li52*li54 + li42*src[9];
0860          dst(3,2) = li63*li64 + li53*li54 + li43*src[9];
0861          dst(3,3) = li64*li64 + li54*li54 + src[9]*src[9];
0862          dst(4,0) = li61*li65 + li51*src[14];
0863          dst(4,1) = li62*li65 + li52*src[14];
0864          dst(4,2) = li63*li65 + li53*src[14];
0865          dst(4,3) = li64*li65 + li54*src[14];
0866          dst(4,4) = li65*li65 + src[14]*src[14];
0867          dst(5,0) = li61*src[20];
0868          dst(5,1) = li62*src[20];
0869          dst(5,2) = li63*src[20];
0870          dst(5,3) = li64*src[20];
0871          dst(5,4) = li65*src[20];
0872          dst(5,5) = src[20]*src[20];
0873       }
0874    };
0875    /// struct to obtain the inverse from a Cholesky decomposition (N = 5)
0876    template<class F, class M> struct _inverter<F,5,M>
0877    {
0878       /// method to do the inversion
0879       inline void operator()(M& dst, const F* src) const
0880       {
0881          const F li21 = -src[1] * src[0] * src[2];
0882          const F li32 = -src[4] * src[2] * src[5];
0883          const F li31 = (src[1] * src[4] * src[2] - src[3]) * src[0] * src[5];
0884          const F li43 = -src[8] * src[9] * src[5];
0885          const F li42 = (src[4] * src[8] * src[5] - src[7]) * src[2] * src[9];
0886          const F li41 = (-src[1] * src[4] * src[8] * src[2] * src[5] +
0887                          src[1] * src[7] * src[2] + src[3] * src[8] * src[5] - src[6]) * src[0] * src[9];
0888          const F li54 = -src[13] * src[14] * src[9];
0889          const F li53 = (src[13] * src[8] * src[9] - src[12]) * src[5] * src[14];
0890          const F li52 = (-src[4] * src[8] * src[13] * src[5] * src[9] +
0891                          src[4] * src[12] * src[5] + src[7] * src[13] * src[9] - src[11]) * src[2] * src[14];
0892          const F li51 = (src[1]*src[4]*src[8]*src[13]*src[2]*src[5]*src[9] -
0893                          src[13]*src[8]*src[3]*src[9]*src[5] - src[12]*src[4]*src[1]*src[2]*src[5] - src[13]*src[7]*src[1]*src[9]*src[2] +
0894                          src[11]*src[1]*src[2] + src[12]*src[3]*src[5] + src[13]*src[6]*src[9] -src[10]) * src[0] * src[14];
0895 
0896          dst(0,0) = li51*li51 + li41*li41 + li31*li31 + li21*li21 + src[0]*src[0];
0897          dst(1,0) = li51*li52 + li41*li42 + li31*li32 + li21*src[2];
0898          dst(1,1) = li52*li52 + li42*li42 + li32*li32 + src[2]*src[2];
0899          dst(2,0) = li51*li53 + li41*li43 + li31*src[5];
0900          dst(2,1) = li52*li53 + li42*li43 + li32*src[5];
0901          dst(2,2) = li53*li53 + li43*li43 + src[5]*src[5];
0902          dst(3,0) = li51*li54 + li41*src[9];
0903          dst(3,1) = li52*li54 + li42*src[9];
0904          dst(3,2) = li53*li54 + li43*src[9];
0905          dst(3,3) = li54*li54 + src[9]*src[9];
0906          dst(4,0) = li51*src[14];
0907          dst(4,1) = li52*src[14];
0908          dst(4,2) = li53*src[14];
0909          dst(4,3) = li54*src[14];
0910          dst(4,4) = src[14]*src[14];
0911       }
0912    };
0913    /// struct to obtain the inverse from a Cholesky decomposition (N = 4)
0914    template<class F, class M> struct _inverter<F,4,M>
0915    {
0916       /// method to do the inversion
0917       inline void operator()(M& dst, const F* src) const
0918       {
0919          const F li21 = -src[1] * src[0] * src[2];
0920          const F li32 = -src[4] * src[2] * src[5];
0921          const F li31 = (src[1] * src[4] * src[2] - src[3]) * src[0] * src[5];
0922          const F li43 = -src[8] * src[9] * src[5];
0923          const F li42 = (src[4] * src[8] * src[5] - src[7]) * src[2] * src[9];
0924          const F li41 = (-src[1] * src[4] * src[8] * src[2] * src[5] +
0925                          src[1] * src[7] * src[2] + src[3] * src[8] * src[5] - src[6]) * src[0] * src[9];
0926 
0927          dst(0,0) = li41*li41 + li31*li31 + li21*li21 + src[0]*src[0];
0928          dst(1,0) = li41*li42 + li31*li32 + li21*src[2];
0929          dst(1,1) = li42*li42 + li32*li32 + src[2]*src[2];
0930          dst(2,0) = li41*li43 + li31*src[5];
0931          dst(2,1) = li42*li43 + li32*src[5];
0932          dst(2,2) = li43*li43 + src[5]*src[5];
0933          dst(3,0) = li41*src[9];
0934          dst(3,1) = li42*src[9];
0935          dst(3,2) = li43*src[9];
0936          dst(3,3) = src[9]*src[9];
0937       }
0938    };
0939    /// struct to obtain the inverse from a Cholesky decomposition (N = 3)
0940    template<class F, class M> struct _inverter<F,3,M>
0941    {
0942       /// method to do the inversion
0943       inline void operator()(M& dst, const F* src) const
0944       {
0945          const F li21 = -src[1] * src[0] * src[2];
0946          const F li32 = -src[4] * src[2] * src[5];
0947          const F li31 = (src[1] * src[4] * src[2] - src[3]) * src[0] * src[5];
0948 
0949          dst(0,0) = li31*li31 + li21*li21 + src[0]*src[0];
0950          dst(1,0) = li31*li32 + li21*src[2];
0951          dst(1,1) = li32*li32 + src[2]*src[2];
0952          dst(2,0) = li31*src[5];
0953          dst(2,1) = li32*src[5];
0954          dst(2,2) = src[5]*src[5];
0955       }
0956    };
0957    /// struct to obtain the inverse from a Cholesky decomposition (N = 2)
0958    template<class F, class M> struct _inverter<F,2,M>
0959    {
0960       /// method to do the inversion
0961       inline void operator()(M& dst, const F* src) const
0962       {
0963          const F li21 = -src[1] * src[0] * src[2];
0964 
0965          dst(0,0) = li21*li21 + src[0]*src[0];
0966          dst(1,0) = li21*src[2];
0967          dst(1,1) = src[2]*src[2];
0968       }
0969    };
0970    /// struct to obtain the inverse from a Cholesky decomposition (N = 1)
0971    template<class F, class M> struct _inverter<F,1,M>
0972    {
0973       /// method to do the inversion
0974       inline void operator()(M& dst, const F* src) const
0975       {
0976          dst(0,0) = src[0]*src[0];
0977       }
0978    };
0979    /// struct to obtain the inverse from a Cholesky decomposition (N = 0)
0980    template<class F, class M> struct _inverter<F,0,M>
0981    {
0982    private:
0983       _inverter();
0984       void operator()(M& dst, const F* src) const;
0985    };
0986 
0987    /// struct to solve a linear system using its Cholesky decomposition (N=6)
0988    template<class F, class V> struct _solver<F,6,V>
0989    {
0990       /// method to solve the linear system
0991       void operator()(V& rhs, const F* l) const
0992       {
0993          // solve Ly = rhs
0994          const F y0 = rhs[0] * l[0];
0995          const F y1 = (rhs[1]-l[1]*y0)*l[2];
0996          const F y2 = (rhs[2]-(l[3]*y0+l[4]*y1))*l[5];
0997          const F y3 = (rhs[3]-(l[6]*y0+l[7]*y1+l[8]*y2))*l[9];
0998          const F y4 = (rhs[4]-(l[10]*y0+l[11]*y1+l[12]*y2+l[13]*y3))*l[14];
0999          const F y5 = (rhs[5]-(l[15]*y0+l[16]*y1+l[17]*y2+l[18]*y3+l[19]*y4))*l[20];
1000          // solve L^Tx = y, and put x into rhs
1001          rhs[5] = y5 * l[20];
1002          rhs[4] = (y4-l[19]*rhs[5])*l[14];
1003          rhs[3] = (y3-(l[18]*rhs[5]+l[13]*rhs[4]))*l[9];
1004          rhs[2] = (y2-(l[17]*rhs[5]+l[12]*rhs[4]+l[8]*rhs[3]))*l[5];
1005          rhs[1] = (y1-(l[16]*rhs[5]+l[11]*rhs[4]+l[7]*rhs[3]+l[4]*rhs[2]))*l[2];
1006          rhs[0] = (y0-(l[15]*rhs[5]+l[10]*rhs[4]+l[6]*rhs[3]+l[3]*rhs[2]+l[1]*rhs[1]))*l[0];
1007       }
1008    };
1009    /// struct to solve a linear system using its Cholesky decomposition (N=5)
1010    template<class F, class V> struct _solver<F,5,V>
1011    {
1012       /// method to solve the linear system
1013       void operator()(V& rhs, const F* l) const
1014       {
1015          // solve Ly = rhs
1016          const F y0 = rhs[0] * l[0];
1017          const F y1 = (rhs[1]-l[1]*y0)*l[2];
1018          const F y2 = (rhs[2]-(l[3]*y0+l[4]*y1))*l[5];
1019          const F y3 = (rhs[3]-(l[6]*y0+l[7]*y1+l[8]*y2))*l[9];
1020          const F y4 = (rhs[4]-(l[10]*y0+l[11]*y1+l[12]*y2+l[13]*y3))*l[14];
1021          // solve L^Tx = y, and put x into rhs
1022          rhs[4] = (y4)*l[14];
1023          rhs[3] = (y3-(l[13]*rhs[4]))*l[9];
1024          rhs[2] = (y2-(l[12]*rhs[4]+l[8]*rhs[3]))*l[5];
1025          rhs[1] = (y1-(l[11]*rhs[4]+l[7]*rhs[3]+l[4]*rhs[2]))*l[2];
1026          rhs[0] = (y0-(l[10]*rhs[4]+l[6]*rhs[3]+l[3]*rhs[2]+l[1]*rhs[1]))*l[0];
1027       }
1028    };
1029    /// struct to solve a linear system using its Cholesky decomposition (N=4)
1030    template<class F, class V> struct _solver<F,4,V>
1031    {
1032       /// method to solve the linear system
1033       void operator()(V& rhs, const F* l) const
1034       {
1035          // solve Ly = rhs
1036          const F y0 = rhs[0] * l[0];
1037          const F y1 = (rhs[1]-l[1]*y0)*l[2];
1038          const F y2 = (rhs[2]-(l[3]*y0+l[4]*y1))*l[5];
1039          const F y3 = (rhs[3]-(l[6]*y0+l[7]*y1+l[8]*y2))*l[9];
1040          // solve L^Tx = y, and put x into rhs
1041          rhs[3] = (y3)*l[9];
1042          rhs[2] = (y2-(l[8]*rhs[3]))*l[5];
1043          rhs[1] = (y1-(l[7]*rhs[3]+l[4]*rhs[2]))*l[2];
1044          rhs[0] = (y0-(l[6]*rhs[3]+l[3]*rhs[2]+l[1]*rhs[1]))*l[0];
1045       }
1046    };
1047    /// struct to solve a linear system using its Cholesky decomposition (N=3)
1048    template<class F, class V> struct _solver<F,3,V>
1049    {
1050       /// method to solve the linear system
1051       void operator()(V& rhs, const F* l) const
1052       {
1053          // solve Ly = rhs
1054          const F y0 = rhs[0] * l[0];
1055          const F y1 = (rhs[1]-l[1]*y0)*l[2];
1056          const F y2 = (rhs[2]-(l[3]*y0+l[4]*y1))*l[5];
1057          // solve L^Tx = y, and put x into rhs
1058          rhs[2] = (y2)*l[5];
1059          rhs[1] = (y1-(l[4]*rhs[2]))*l[2];
1060          rhs[0] = (y0-(l[3]*rhs[2]+l[1]*rhs[1]))*l[0];
1061       }
1062    };
1063    /// struct to solve a linear system using its Cholesky decomposition (N=2)
1064    template<class F, class V> struct _solver<F,2,V>
1065    {
1066       /// method to solve the linear system
1067       void operator()(V& rhs, const F* l) const
1068       {
1069          // solve Ly = rhs
1070          const F y0 = rhs[0] * l[0];
1071          const F y1 = (rhs[1]-l[1]*y0)*l[2];
1072          // solve L^Tx = y, and put x into rhs
1073          rhs[1] = (y1)*l[2];
1074          rhs[0] = (y0-(l[1]*rhs[1]))*l[0];
1075       }
1076    };
1077    /// struct to solve a linear system using its Cholesky decomposition (N=1)
1078    template<class F, class V> struct _solver<F,1,V>
1079    {
1080       /// method to solve the linear system
1081       void operator()(V& rhs, const F* l) const
1082       {
1083          // solve LL^T x = rhs, put y into rhs
1084          rhs[0] *= l[0] * l[0];
1085       }
1086    };
1087    /// struct to solve a linear system using its Cholesky decomposition (N=0)
1088    template<class F, class V> struct _solver<F,0,V>
1089    {
1090    private:
1091       _solver();
1092       void operator()(V& rhs, const F* l) const;
1093    };
1094 }
1095 
1096 
1097    }  // namespace Math
1098 
1099 }  // namespace ROOT
1100 
1101 #endif // ROOT_Math_CHOLESKYDECOMP
1102