FgMatrixC<T,dim,1>
fgClamp(FgMatrixC<T,dim,1> val,FgMatrixC<T,dim,2> bounds)
{
    for (uint ii=0; ii<dim; ++ii)
        val[ii] = fgClamp(val[ii],bounds.elem(ii,0),bounds.elem(ii,1));
    return val;
}
FgMatrixC<T,dims+1,dims+1>
fgAsHomogMat(const FgMatrixC<T,dims,dims> & linear)
{
    FgMatrixC<T,dims+1,dims+1>    ret;
    for (uint rr=0; rr<dims; ++rr)
        for (uint cc=0; cc<dims; ++cc)
            ret.elem(rr,cc) = linear.elem(rr,cc);
    ret.elem(dims,dims) = T(1);
    return ret;
}
FgMatrixC<T,2,2>
fgMatRotate(T radians)
{
    FgMatrixC<T,2,2> mat;
    FgTypeAttributeFloatingS<T>();
    T ct = T(cos(radians));
    T st = T(sin(radians));
    mat.elem(0,0)=ct;    mat.elem(0,1)=-st;
    mat.elem(1,0)=st;    mat.elem(1,1)=ct;
    return mat;
}
FgMatrixC<T,nrows1+nrows2,ncols>
fgConcatVert(
    const FgMatrixC<T,nrows1,ncols> & upper,
    const FgMatrixC<T,nrows2,ncols> & lower)
{
    FgMatrixC<T,nrows1+nrows2,ncols>    ret;
    for (uint rr=0; rr<nrows1; ++rr)
        for (uint cc=0; cc<ncols; ++cc)
            ret.elem(rr,cc) = upper.elem(rr,cc);
    for (uint rr=0; rr<nrows2; ++rr)
        for (uint cc=0; cc<ncols; ++cc)
            ret.elem(nrows1+rr,cc) = lower.elem(rr,cc);
    return ret;
}
FgMatrixC<T,dims+1,dims+1>
fgAsHomogMat(
    const FgMatrixC<T,dims,dims>  & linTrans,
    const FgMatrixC<T,dims,1>     & translation)
{
    FgMatrixC<T,dims+1,dims+1>    ret;
    for (uint rr=0; rr<dims; rr++)
        for (uint cc=0; cc<dims; cc++)
            ret.elem(rr,cc) = linTrans.elem(rr,cc);
    for (uint rr=0; rr<dims; rr++)
        ret.elem(rr,dims) = translation[rr];
    ret.elem(dims,dims) = 1;
    return ret;
}
FgMatrixC<T,nrows,ncols1+ncols2>
fgConcatHoriz(
    const FgMatrixC<T,nrows,ncols1> & lhs,
    const FgMatrixC<T,nrows,ncols2> & rhs)
{
    FgMatrixC<T,nrows,ncols1+ncols2>    ret;
    for (uint row=0; row<nrows; ++row)
    {
        uint    col=0;
        for (uint cc=0; cc<ncols1; ++cc)
            ret.elem(row,col++) = lhs.elem(row,cc);
        for (uint cc=0; cc<ncols2; ++cc)
            ret.elem(row,col++) = rhs.elem(row,cc);
    }
    return ret;
}
FgMatrixC<T,sz,sz>
fgDiagonal(FgMatrixC<T,sz,1> vec)
{
    FgMatrixC<T,sz,sz>      ret;
    for (uint ii=0; ii<sz; ++ii)
        ret.elem(ii,ii) = vec[ii];
    return ret;
}
FgMatrixC<T,3,3>
fgPermuteAxes(uint axisToBecomeX)
{
    FgMatrixC<T,3,3>    ret;
    for (uint ii=0; ii<3; ++ii)
        ret.elem(ii,(ii+axisToBecomeX)%3) = T(1);
    return ret;
}
 void
 setSubMat(const FgMatrixC<T,srows,scols> & sub,uint row,uint col)
 {
     FGASSERT((srows+row <= nrows) && (scols+col <= ncols));
     for (uint rr=0; rr<srows; rr++)
         for (uint cc=0; cc<scols; cc++)
             elem(rr+row,cc+col) = sub.elem(rr,cc);
 }
 void
 accSubMat(size_t row,size_t col,FgMatrixC<T,mrows,mcols> m)
 {
     FGASSERT((mrows+row <= nrows) && (mcols+col <= ncols));
     for (uint rr=0; rr<mrows; ++rr)
         for (uint cc=0; cc<mcols; ++cc)
             elem(row+rr,col+cc) += m.elem(rr,cc);
 }
FgMatrixC<T,2,2> fgMatInverse(
    const FgMatrixC<T,2,2>&      m)
{
    FgTypeAttributeFloatingS<T>();
    FgMatrixC<T,2,2>     retval;
    T   fac = (m.elem(0,0) * m.elem(1,1) - m.elem(0,1) * m.elem(1,0));
    FGASSERT(fac != T(0));
    fac = T(1) / fac;
    retval.elem(0,0) = m.elem(1,1) * fac;
    retval.elem(0,1) = - m.elem(0,1) * fac;
    retval.elem(1,0) = - m.elem(1,0) * fac;
    retval.elem(1,1) = m.elem(0,0) * fac;
    return retval;
}
 FgMatrixC<T,ncols,nrows>
 transpose() const
 {
     FgMatrixC<T,ncols,nrows> tMat;
     for (uint ii=0; ii<nrows; ii++)
         for (uint jj=0; jj<ncols; jj++)
             tMat.elem(jj,ii) = elem(ii,jj);
     return tMat;
 }
FgMatrixC<T,dims+1,dims+1>
fgAsHomogMat(const FgMatrixC<T,dims,1> & translation)
{
    FgMatrixC<T,dims+1,dims+1>    ret;
    ret.setIdentity();
    for (uint rr=0; rr<dims; rr++)
        ret.elem(rr,dims) = translation[rr];
    return ret;
}
double
lnNormalCholesky(
    const FgMatrixC<double,dim,1> &     pos,
    const FgMatrixC<double,dim,1> &     mean,
    const FgMatrixC<double,dim,dim> &   chol)
{
    double  det = 1.0;
    for (uint ii=0; ii<dim; ii++)
        det *= chol.elem(dim,dim);
    FgMatrixC<double,dim,1> mhlbs = chol * (pos-mean);
    return (0.5 * std::log(det) -               // Cholesky has all diagonals > 0
            0.5 * double(dim) * fgLn_2pi() -
            0.5 * mhlbs.lengthSqr());
}
FgMatrixC<T,3,3>
fgMatRotateAxis(                   // RHR rotation around an arbitrary axis
    T           radians, 
    const       FgMatrixC<T,3,1> &axis)             // vector describing that axis
{
    FgMatrixC<T,3,3> mat;
    FgTypeAttributeFloatingS<T>();
    T           len = axis.length();
    FGASSERT(len > T(0));
    FgMatrixC<T,3,1>    ax = axis * (T(1) / len);   // Ensure the rotation axis is normalized
    T           ct = (T)cos(radians);
    T           st = (T)sin(radians);
    T           vt = T(1)-ct;
    mat.elem(0,0) = ax[0]*ax[0]*vt + ct;
    mat.elem(0,1) = ax[0]*ax[1]*vt - ax[2]*st;
    mat.elem(0,2) = ax[0]*ax[2]*vt + ax[1]*st;
    mat.elem(1,0) = ax[0]*ax[1]*vt + ax[2]*st;
    mat.elem(1,1) = ax[1]*ax[1]*vt + ct;
    mat.elem(1,2) = ax[1]*ax[2]*vt - ax[0]*st;
    mat.elem(2,0) = ax[0]*ax[2]*vt - ax[1]*st;
    mat.elem(2,1) = ax[1]*ax[2]*vt + ax[0]*st;
    mat.elem(2,2) = ax[2]*ax[2]*vt + ct;
    return mat;
}
double
normalCholesky(
    const FgMatrixC<double,dim,1> &     pos,
    const FgMatrixC<double,dim,1> &     mean,
    const FgMatrixC<double,dim,dim> &   chol)   // Right-hand cholesky term of concentration
{
    double  det = 1.0;
    for (uint ii=0; ii<dim; ii++)
        det *= chol.elem(ii,ii);                // Cholesky is upper or lower triangular.
    FgMatrixC<double,dim,1> mhlbs = chol * (pos-mean);
    return (
        std::pow(2.0 * fgPi(),double(dim) * -0.5) *
        std::sqrt(det) *
        fgExp(-0.5 * mhlbs.lengthSqr()));
}
FgMatrixC<T,3,3>
fgMatRotateZ(T radians)        // RHR rotation around Z axis
{
    FgMatrixC<T,3,3> mat;
    FgTypeAttributeFloatingS<T>();
    T ct = (T)cos(radians);
    T st = (T)sin(radians);
    mat.elem(0,0)=ct;    mat.elem(0,1)=-st;   mat.elem(0,2)=0.0;
    mat.elem(1,0)=st;    mat.elem(1,1)=ct;    mat.elem(1,2)=0.0;
    mat.elem(2,0)=0.0;   mat.elem(2,1)=0.0;   mat.elem(2,2)=1.0;
    return mat;
}
FgMatrixC<T,nrows,ncols1+ncols2+ncols3>
fgConcatHoriz(
    const FgMatrixC<T,nrows,ncols1> & m1,
    const FgMatrixC<T,nrows,ncols2> & m2,
    const FgMatrixC<T,nrows,ncols3> & m3)
{
    FgMatrixC<T,nrows,ncols1+ncols2+ncols3> ret;
    for (uint row=0; row<nrows; ++row)
    {
        uint    col=0;
        for (uint cc=0; cc<ncols1; ++cc)
            ret.elem(row,col++) = m1.elem(row,cc);
        for (uint cc=0; cc<ncols2; ++cc)
            ret.elem(row,col++) = m2.elem(row,cc);
        for (uint cc=0; cc<ncols3; ++cc)
            ret.elem(row,col++) = m3.elem(row,cc);
    }
    return ret;
}
FgMatrixC<T,3,3> fgMatInverse(
    const FgMatrixC<T,3,3>&      m)
{
    FgTypeAttributeFloatingS<T>();
    FgMatrixC<T,3,3>     r;
    T   fac = (m.elem(0,0)*m.elem(1,1)*m.elem(2,2) - m.elem(0,0)*m.elem(1,2)*m.elem(2,1) +
               m.elem(1,0)*m.elem(0,2)*m.elem(2,1) - m.elem(1,0)*m.elem(0,1)*m.elem(2,2) +
               m.elem(2,0)*m.elem(0,1)*m.elem(1,2) - m.elem(2,0)*m.elem(1,1)*m.elem(0,2));
    FGASSERT(fac != T(0));
    r.elem(0,0) = m.elem(1,1) * m.elem(2,2) - m.elem(1,2) * m.elem(2,1);
    r.elem(0,1) = m.elem(0,2) * m.elem(2,1) - m.elem(0,1) * m.elem(2,2);
    r.elem(0,2) = m.elem(0,1) * m.elem(1,2) - m.elem(0,2) * m.elem(1,1);
    r.elem(1,0) = m.elem(1,2) * m.elem(2,0) - m.elem(1,0) * m.elem(2,2);
    r.elem(1,1) = m.elem(0,0) * m.elem(2,2) - m.elem(0,2) * m.elem(2,0);
    r.elem(1,2) = m.elem(0,2) * m.elem(1,0) - m.elem(0,0) * m.elem(1,2);
    r.elem(2,0) = m.elem(1,0) * m.elem(2,1) - m.elem(1,1) * m.elem(2,0);
    r.elem(2,1) = m.elem(0,1) * m.elem(2,0) - m.elem(0,0) * m.elem(2,1);
    r.elem(2,2) = m.elem(0,0) * m.elem(1,1) - m.elem(1,0) * m.elem(0,1);
    r *= T(1) / fac;
    return r;
}