FgMatrixV<T>
fgConcatVert(
    const FgMatrixV<T> &    upper,
    const FgMatrixV<T> &    lower)
{
    if (upper.empty())
        return lower;
    if (lower.empty())
        return upper;
    FGASSERT(upper.numCols() == lower.numCols());
    FgMatrixV<T>      ret(upper.numRows()+lower.numRows(),upper.numCols());
    for (uint ii=0; ii<upper.numElems(); ++ii)
        ret[ii] = upper[ii];
    uint    off = upper.numElems();
    for (uint ii=0; ii<lower.numElems(); ++ii)
        ret[off+ii] = lower[ii];
    return ret;
}
FgMatrixV<T>
fgConcatHoriz(
    const FgMatrixV<T> &    left,
    const FgMatrixV<T> &    right)
{
    if (left.empty())
        return right;
    if (right.empty())
        return left;
    FGASSERT(left.numRows() == right.numRows());
    uint            numRows = left.numRows(),
                    numCols = left.numCols() + right.numCols();
    FgMatrixV<T>    retval(numRows,numCols);
    for (uint rr=0; rr<numRows; rr++)
    {
        uint    col=0;
        for (uint cc=0; cc<left.numCols(); ++cc)
            retval.elem(rr,col++) = left.elem(rr,cc);
        for (uint cc=0; cc<right.numCols(); ++cc)
            retval.elem(rr,col++) = right.elem(rr,cc);
    }
    return retval;
}