changeset 28029:c28b8ba841fb

move sparse functions from dldfcn to corefcn directory (bug #57459) Octave is always linked with SuiteSparse libraries to support functions in liboctave so there is no advantage to dynamically load the interpreter DEFUN functions that depend on SuiteSparse. * __eigs__.cc, amd.cc, ccolamd.cc, chol.cc, colamd.cc, dmperm.cc, qr.cc, symbfact.cc, symrcm.cc: Move from dldfcn to corefcn directory. Define functions with DEFUN or DEFMETHOD instead of DEFUN_DLD or DEFMETHOD_DLD. * libinterp/dldfcn/module-files, libinterp/corefcn/module.mk: Update. * amd.cc (Famd): Don't mlock function. * symbfact.cc (Fsymbfact): Don't mlock function. * variables.cc, type.m, which.m: Update tests.
author John W. Eaton <jwe@octave.org>
date Wed, 29 Jan 2020 06:30:40 -0500
parents 915b3630eed0
children 9d9e01986105
files libinterp/corefcn/__eigs__.cc libinterp/corefcn/amd.cc libinterp/corefcn/ccolamd.cc libinterp/corefcn/chol.cc libinterp/corefcn/colamd.cc libinterp/corefcn/dmperm.cc libinterp/corefcn/module.mk libinterp/corefcn/qr.cc libinterp/corefcn/symbfact.cc libinterp/corefcn/symrcm.cc libinterp/corefcn/variables.cc libinterp/dldfcn/__eigs__.cc libinterp/dldfcn/amd.cc libinterp/dldfcn/ccolamd.cc libinterp/dldfcn/chol.cc libinterp/dldfcn/colamd.cc libinterp/dldfcn/dmperm.cc libinterp/dldfcn/module-files libinterp/dldfcn/qr.cc libinterp/dldfcn/symbfact.cc libinterp/dldfcn/symrcm.cc scripts/help/type.m scripts/help/which.m
diffstat 23 files changed, 6692 insertions(+), 6699 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/__eigs__.cc	Wed Jan 29 06:30:40 2020 -0500
@@ -0,0 +1,675 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2005-2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <limits>
+#include <string>
+
+#include "Matrix.h"
+#include "eigs-base.h"
+#include "unwind-prot.h"
+
+#include "defun.h"
+#include "error.h"
+#include "errwarn.h"
+#include "interpreter-private.h"
+#include "oct-map.h"
+#include "ov.h"
+#include "ovl.h"
+#include "pager.h"
+#include "parse.h"
+#include "variables.h"
+
+#if defined (HAVE_ARPACK)
+
+// Global pointer for user defined function.
+static octave_value eigs_fcn;
+
+// Have we warned about imaginary values returned from user function?
+static bool warned_imaginary = false;
+
+// Is this a recursive call?
+static int call_depth = 0;
+
+ColumnVector
+eigs_func (const ColumnVector& x, int& eigs_error)
+{
+  ColumnVector retval;
+  octave_value_list args;
+  args(0) = x;
+
+  if (eigs_fcn.is_defined ())
+    {
+      octave_value_list tmp;
+
+      try
+        {
+          tmp = octave::feval (eigs_fcn, args, 1);
+        }
+      catch (octave::execution_exception& e)
+        {
+          err_user_supplied_eval (e, "eigs");
+        }
+
+      if (tmp.length () && tmp(0).is_defined ())
+        {
+          if (! warned_imaginary && tmp(0).iscomplex ())
+            {
+              warning ("eigs: ignoring imaginary part returned from user-supplied function");
+              warned_imaginary = true;
+            }
+
+          retval = tmp(0).xvector_value ("eigs: evaluation of user-supplied function failed");
+        }
+      else
+        {
+          eigs_error = 1;
+          err_user_supplied_eval ("eigs");
+        }
+    }
+
+  return retval;
+}
+
+ComplexColumnVector
+eigs_complex_func (const ComplexColumnVector& x, int& eigs_error)
+{
+  ComplexColumnVector retval;
+  octave_value_list args;
+  args(0) = x;
+
+  if (eigs_fcn.is_defined ())
+    {
+      octave_value_list tmp;
+
+      try
+        {
+          tmp = octave::feval (eigs_fcn, args, 1);
+        }
+      catch (octave::execution_exception& e)
+        {
+          err_user_supplied_eval (e, "eigs");
+        }
+
+      if (tmp.length () && tmp(0).is_defined ())
+        {
+          retval = tmp(0).xcomplex_vector_value ("eigs: evaluation of user-supplied function failed");
+        }
+      else
+        {
+          eigs_error = 1;
+          err_user_supplied_eval ("eigs");
+        }
+    }
+
+  return retval;
+}
+
+#endif
+
+DEFMETHOD (__eigs__, interp, args, nargout,
+           doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{d} =} __eigs__ (@var{A})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k}, @var{sigma})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k}, @var{sigma}, @var{opts})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k}, @var{sigma})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k}, @var{sigma}, @var{opts})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k}, @var{sigma})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k}, @var{sigma})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k}, @var{sigma}, @var{opts})
+@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k}, @var{sigma}, @var{opts})
+@deftypefnx {} {[@var{V}, @var{d}] =} __eigs__ (@var{A}, @dots{})
+@deftypefnx {} {[@var{V}, @var{d}] =} __eigs__ (@var{af}, @var{n}, @dots{})
+@deftypefnx {} {[@var{V}, @var{d}, @var{flag}] =} __eigs__ (@var{A}, @dots{})
+@deftypefnx {} {[@var{V}, @var{d}, @var{flag}] =} __eigs__ (@var{af}, @var{n}, @dots{})
+Undocumented internal function.
+@end deftypefn */)
+{
+#if defined (HAVE_ARPACK)
+
+  int nargin = args.length ();
+
+  if (nargin == 0)
+    print_usage ();
+
+  octave_value_list retval;
+
+  std::string fcn_name;
+  octave_idx_type n = 0;
+  octave_idx_type k = 6;
+  Complex sigma = 0.0;
+  double sigmar, sigmai;
+  bool have_sigma = false;
+  std::string typ = "LM";
+  Matrix amm, bmm, bmt;
+  ComplexMatrix acm, bcm, bct;
+  SparseMatrix asmm, bsmm, bsmt;
+  SparseComplexMatrix ascm, bscm, bsct;
+  int b_arg = 0;
+  bool have_b = false;
+  bool have_a_fun = false;
+  bool a_is_complex = false;
+  bool b_is_complex = false;
+  bool symmetric = false;
+  bool sym_tested = false;
+  bool cholB = false;
+  bool a_is_sparse = false;
+  bool b_is_sparse = false;
+  ColumnVector permB;
+  int arg_offset = 0;
+  double tol = std::numeric_limits<double>::epsilon ();
+  int maxit = 300;
+  int disp = 0;
+  octave_idx_type p = -1;
+  ColumnVector resid;
+  ComplexColumnVector cresid;
+  octave_idx_type info = 1;
+
+  warned_imaginary = false;
+
+  octave::unwind_protect frame;
+
+  frame.protect_var (call_depth);
+  call_depth++;
+
+  if (call_depth > 1)
+    error ("eigs: invalid recursive call");
+
+  if (args(0).is_function_handle () || args(0).is_inline_function ()
+      || args(0).is_string ())
+    {
+      eigs_fcn = octave::get_function_handle (interp, args(0), "x");
+
+      if (eigs_fcn.is_undefined ())
+        error ("eigs: unknown function");
+
+      if (nargin < 2)
+        error ("eigs: incorrect number of arguments");
+
+      n = args(1).nint_value ();
+      arg_offset = 1;
+      have_a_fun = true;
+    }
+  else
+    {
+      if (args(0).iscomplex ())
+        {
+          if (args(0).issparse ())
+            {
+              ascm = (args(0).sparse_complex_matrix_value ());
+              a_is_sparse = true;
+            }
+          else
+            acm = (args(0).complex_matrix_value ());
+          a_is_complex = true;
+        }
+      else
+        {
+          if (args(0).issparse ())
+            {
+              asmm = (args(0).sparse_matrix_value ());
+              a_is_sparse = true;
+            }
+          else
+            {
+              amm = (args(0).matrix_value ());
+            }
+        }
+    }
+
+  // Note hold off reading B until later to avoid issues of double
+  // copies of the matrix if B is full/real while A is complex.
+  if (nargin > 1 + arg_offset
+      && ! (args(1 + arg_offset).is_real_scalar ()))
+    {
+      if (args(1+arg_offset).iscomplex ())
+        {
+          b_arg = 1+arg_offset;
+          if (args(b_arg).issparse ())
+            {
+              bscm = (args(b_arg).sparse_complex_matrix_value ());
+              b_is_sparse = true;
+            }
+          else
+            bcm = (args(b_arg).complex_matrix_value ());
+          have_b = true;
+          b_is_complex = true;
+          arg_offset++;
+        }
+      else
+        {
+          b_arg = 1+arg_offset;
+          if (args(b_arg).issparse ())
+            {
+              bsmm = (args(b_arg).sparse_matrix_value ());
+              b_is_sparse = true;
+            }
+          else
+            bmm = (args(b_arg).matrix_value ());
+          have_b = true;
+          arg_offset++;
+        }
+    }
+
+  if (nargin > (1+arg_offset))
+    k = args(1+arg_offset).nint_value ();
+
+  if (nargin > (2+arg_offset))
+    {
+      if (args(2+arg_offset).is_string ())
+        {
+          typ = args(2+arg_offset).string_value ();
+
+          // Use STL function to convert to upper case
+          transform (typ.begin (), typ.end (), typ.begin (), toupper);
+
+          sigma = 0.0;
+        }
+      else
+        {
+          sigma = args(2+arg_offset).xcomplex_value ("eigs: SIGMA must be a scalar or a string");
+
+          have_sigma = true;
+        }
+    }
+
+  sigmar = sigma.real ();
+  sigmai = sigma.imag ();
+
+  if (nargin > (3+arg_offset))
+    {
+      if (! args(3+arg_offset).isstruct ())
+        error ("eigs: OPTS argument must be a structure");
+
+      octave_scalar_map map = args(3+arg_offset).xscalar_map_value ("eigs: OPTS argument must be a scalar structure");
+
+      octave_value tmp;
+
+      // issym is ignored for complex matrix inputs
+      tmp = map.getfield ("issym");
+      if (tmp.is_defined ())
+        {
+          if (tmp.numel () != 1)
+            error ("eigs: OPTS.issym must be a scalar value");
+
+          symmetric = tmp.xbool_value ("eigs: OPTS.issym must be a logical value");
+          sym_tested = true;
+        }
+
+      // isreal is ignored if A is not a function
+      if (have_a_fun)
+        {
+          tmp = map.getfield ("isreal");
+          if (tmp.is_defined ())
+            {
+              if (tmp.numel () != 1)
+                error ("eigs: OPTS.isreal must be a scalar value");
+
+              a_is_complex = ! tmp.xbool_value ("eigs: OPTS.isreal must be a logical value");
+            }
+        }
+
+      tmp = map.getfield ("tol");
+      if (tmp.is_defined ())
+        tol = tmp.double_value ();
+
+      tmp = map.getfield ("maxit");
+      if (tmp.is_defined ())
+        maxit = tmp.nint_value ();
+
+      tmp = map.getfield ("p");
+      if (tmp.is_defined ())
+        p = tmp.nint_value ();
+
+      tmp = map.getfield ("v0");
+      if (tmp.is_defined ())
+        {
+          if (a_is_complex || b_is_complex)
+            cresid = ComplexColumnVector (tmp.complex_vector_value ());
+          else
+            resid = ColumnVector (tmp.vector_value ());
+        }
+
+      tmp = map.getfield ("disp");
+      if (tmp.is_defined ())
+        disp = tmp.nint_value ();
+
+      tmp = map.getfield ("cholB");
+      if (tmp.is_defined ())
+        {
+          if (tmp.numel () != 1)
+            error ("eigs: OPTS.cholB must be a scalar value");
+
+          cholB = tmp.xbool_value ("eigs: OPTS.cholB must be a logical value");
+        }
+
+      tmp = map.getfield ("permB");
+      if (tmp.is_defined ())
+        permB = ColumnVector (tmp.vector_value ()) - 1.0;
+    }
+
+  if (nargin > (4+arg_offset))
+    error ("eigs: incorrect number of arguments");
+
+  // Test undeclared (no issym) matrix inputs for symmetry
+  if (! sym_tested && ! have_a_fun)
+    {
+      if (a_is_complex)
+        {
+          if (a_is_sparse)
+            symmetric = ascm.ishermitian ();
+          else
+            symmetric = acm.ishermitian ();
+        }
+      else
+        {
+          if (a_is_sparse)
+            symmetric = asmm.issymmetric ();
+          else
+            symmetric = amm.issymmetric ();
+        }
+    }
+
+  if (have_b)
+    {
+      if (a_is_complex || b_is_complex)
+        {
+          if (b_is_sparse)
+            bscm = args(b_arg).sparse_complex_matrix_value ();
+          else
+            bcm = args(b_arg).complex_matrix_value ();
+        }
+      else
+        {
+          if (b_is_sparse)
+            bsmm = args(b_arg).sparse_matrix_value ();
+          else
+            bmm = args(b_arg).matrix_value ();
+        }
+    }
+
+  // Mode 1 for SM mode seems unstable for some reason.
+  // Use Mode 3 instead, with sigma = 0.
+  if (! have_sigma && typ == "SM")
+    have_sigma = true;
+
+  octave_idx_type nconv;
+  if (a_is_complex || b_is_complex)
+    {
+      ComplexMatrix eig_vec;
+      ComplexColumnVector eig_val;
+
+      if (have_a_fun)
+        {
+          if (b_is_sparse)
+            nconv = EigsComplexNonSymmetricFunc
+              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
+               eig_val, bscm, permB, cresid, octave_stdout, tol,
+               (nargout > 1), cholB, disp, maxit);
+          else
+            nconv = EigsComplexNonSymmetricFunc
+              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
+               eig_val, bcm, permB, cresid, octave_stdout, tol,
+               (nargout > 1), cholB, disp, maxit);
+        }
+      else if (have_sigma)
+        {
+          if (a_is_sparse)
+            nconv = EigsComplexNonSymmetricMatrixShift
+                    (ascm, sigma, k, p, info, eig_vec, eig_val, bscm, permB,
+                     cresid, octave_stdout, tol, (nargout > 1), cholB, disp,
+                     maxit);
+          else
+            nconv = EigsComplexNonSymmetricMatrixShift
+                    (acm, sigma, k, p, info, eig_vec, eig_val, bcm, permB,
+                     cresid, octave_stdout, tol, (nargout > 1), cholB, disp,
+                     maxit);
+        }
+      else
+        {
+          if (a_is_sparse)
+            nconv = EigsComplexNonSymmetricMatrix
+                    (ascm, typ, k, p, info, eig_vec, eig_val, bscm, permB,
+                     cresid, octave_stdout, tol, (nargout > 1), cholB, disp,
+                     maxit);
+          else
+            nconv = EigsComplexNonSymmetricMatrix
+                    (acm, typ, k, p, info, eig_vec, eig_val, bcm, permB,
+                     cresid, octave_stdout, tol, (nargout > 1), cholB, disp,
+                     maxit);
+        }
+
+      if (nargout < 2)
+        {
+          if (symmetric)
+            retval(0) = real (eig_val);
+          else
+            retval(0) = eig_val;
+        }
+      else
+        {
+          if (symmetric)
+            retval = ovl (eig_vec, DiagMatrix (real (eig_val)), double (info));
+          else
+            retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info));
+        }
+    }
+  else if (sigmai != 0.0)
+    {
+      // Promote real problem to a complex one.
+      ComplexMatrix eig_vec;
+      ComplexColumnVector eig_val;
+
+      if (have_a_fun)
+        {
+          if (b_is_sparse)
+            nconv = EigsComplexNonSymmetricFunc
+              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
+               eig_val, bscm, permB, cresid, octave_stdout, tol,
+               (nargout > 1), cholB, disp, maxit);
+          else
+            nconv = EigsComplexNonSymmetricFunc
+              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
+               eig_val, bcm, permB, cresid, octave_stdout, tol,
+               (nargout > 1), cholB, disp, maxit);
+        }
+      else
+        {
+          if (a_is_sparse)
+            nconv = EigsComplexNonSymmetricMatrixShift
+                    (SparseComplexMatrix (asmm), sigma, k, p, info, eig_vec,
+                     eig_val, SparseComplexMatrix (bsmm), permB, cresid,
+                     octave_stdout, tol, (nargout > 1), cholB, disp, maxit);
+          else
+            nconv = EigsComplexNonSymmetricMatrixShift
+                    (ComplexMatrix (amm), sigma, k, p, info, eig_vec,
+                     eig_val, ComplexMatrix (bmm), permB, cresid,
+                     octave_stdout, tol, (nargout > 1), cholB, disp, maxit);
+        }
+
+      if (nargout < 2)
+        {
+          if (symmetric)
+            retval(0) = real (eig_val);
+          else
+            retval(0) = eig_val;
+        }
+      else
+        {
+          if (symmetric)
+            retval = ovl (eig_vec, DiagMatrix (real (eig_val)), double (info));
+          else
+            retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info));
+        }
+    }
+  else
+    {
+      if (symmetric)
+        {
+          Matrix eig_vec;
+          ColumnVector eig_val;
+
+          if (have_a_fun)
+            {
+              if (b_is_sparse)
+                nconv = EigsRealSymmetricFunc
+                       (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
+                        eig_val, bsmm, permB, resid, octave_stdout, tol,
+                        (nargout > 1), cholB, disp, maxit);
+              else
+                nconv = EigsRealSymmetricFunc
+                       (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
+                        eig_val, bmm, permB, resid, octave_stdout, tol,
+                        (nargout > 1), cholB, disp, maxit);
+            }
+          else if (have_sigma)
+            {
+              if (a_is_sparse)
+                nconv = EigsRealSymmetricMatrixShift
+                        (asmm, sigmar, k, p, info, eig_vec, eig_val, bsmm,
+                         permB, resid, octave_stdout, tol, (nargout > 1),
+                         cholB, disp, maxit);
+              else
+                nconv = EigsRealSymmetricMatrixShift
+                        (amm, sigmar, k, p, info, eig_vec, eig_val, bmm,
+                         permB, resid, octave_stdout, tol, (nargout > 1),
+                         cholB, disp, maxit);
+            }
+          else
+            {
+              if (a_is_sparse)
+                nconv = EigsRealSymmetricMatrix
+                        (asmm, typ, k, p, info, eig_vec, eig_val, bsmm,
+                         permB, resid, octave_stdout, tol, (nargout > 1),
+                         cholB, disp, maxit);
+              else
+                nconv = EigsRealSymmetricMatrix
+                        (amm, typ, k, p, info, eig_vec, eig_val, bmm, permB,
+                         resid, octave_stdout, tol, (nargout > 1), cholB,
+                         disp, maxit);
+            }
+
+          if (nargout < 2)
+            retval(0) = eig_val;
+          else
+            retval = ovl (eig_vec, DiagMatrix (eig_val), double (info));
+        }
+      else
+        {
+          ComplexMatrix eig_vec;
+          ComplexColumnVector eig_val;
+
+          if (have_a_fun)
+            {
+              if (b_is_sparse)
+                nconv = EigsRealNonSymmetricFunc
+                        (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
+                         eig_val, bsmm, permB, resid, octave_stdout, tol,
+                         (nargout > 1), cholB, disp, maxit);
+              else
+                nconv = EigsRealNonSymmetricFunc
+                        (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
+                         eig_val, bmm, permB, resid, octave_stdout, tol,
+                         (nargout > 1), cholB, disp, maxit);
+            }
+          else if (have_sigma)
+            {
+              if (a_is_sparse)
+                nconv = EigsRealNonSymmetricMatrixShift
+                        (asmm, sigmar, k, p, info, eig_vec, eig_val, bsmm,
+                         permB, resid, octave_stdout, tol, (nargout > 1),
+                         cholB, disp, maxit);
+              else
+                nconv = EigsRealNonSymmetricMatrixShift
+                        (amm, sigmar, k, p, info, eig_vec, eig_val, bmm,
+                         permB, resid, octave_stdout, tol, (nargout > 1),
+                         cholB, disp, maxit);
+            }
+          else
+            {
+              if (a_is_sparse)
+                nconv = EigsRealNonSymmetricMatrix
+                        (asmm, typ, k, p, info, eig_vec, eig_val, bsmm,
+                         permB, resid, octave_stdout, tol, (nargout > 1),
+                         cholB, disp, maxit);
+              else
+                nconv = EigsRealNonSymmetricMatrix
+                        (amm, typ, k, p, info, eig_vec, eig_val, bmm, permB,
+                         resid, octave_stdout, tol, (nargout > 1), cholB,
+                         disp, maxit);
+            }
+
+          if (nargout < 2)
+            retval(0) = eig_val;
+          else
+            retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info));
+        }
+    }
+
+  if (nconv <= 0)
+    warning_with_id ("Octave:eigs:UnconvergedEigenvalues",
+                     "eigs: None of the %" OCTAVE_IDX_TYPE_FORMAT
+                     " requested eigenvalues converged", k);
+  else if (nconv < k)
+    warning_with_id ("Octave:eigs:UnconvergedEigenvalues",
+                     "eigs: Only %" OCTAVE_IDX_TYPE_FORMAT
+                     " of the %" OCTAVE_IDX_TYPE_FORMAT
+                     " requested eigenvalues converged",
+                     nconv, k);
+
+  if (! fcn_name.empty ())
+    {
+      octave::symbol_table& symtab = interp.get_symbol_table ();
+
+      symtab.clear_function (fcn_name);
+    }
+
+  return retval;
+
+#else
+
+  octave_unused_parameter (interp);
+  octave_unused_parameter (args);
+  octave_unused_parameter (nargout);
+
+  err_disabled_feature ("eigs", "ARPACK");
+
+#endif
+}
+
+/*
+## No test needed for internal helper function.
+%!assert (1)
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/amd.cc	Wed Jan 29 06:30:40 2020 -0500
@@ -0,0 +1,202 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008-2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+// This is the octave interface to amd, which bore the copyright given
+// in the help of the functions.
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <cstdlib>
+
+#include "CSparse.h"
+#include "Sparse.h"
+#include "dMatrix.h"
+#include "oct-locbuf.h"
+#include "oct-sparse.h"
+
+#include "defun.h"
+#include "error.h"
+#include "errwarn.h"
+#include "oct-map.h"
+#include "ov.h"
+#include "ovl.h"
+#include "parse.h"
+
+DEFUN (amd, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{p} =} amd (@var{S})
+@deftypefnx {} {@var{p} =} amd (@var{S}, @var{opts})
+
+Return the approximate minimum degree permutation of a matrix.
+
+This is a permutation such that the Cholesky@tie{}factorization of
+@code{@var{S} (@var{p}, @var{p})} tends to be sparser than the
+Cholesky@tie{}factorization of @var{S} itself.  @code{amd} is typically
+faster than @code{symamd} but serves a similar purpose.
+
+The optional parameter @var{opts} is a structure that controls the behavior
+of @code{amd}.  The fields of the structure are
+
+@table @asis
+@item @var{opts}.dense
+Determines what @code{amd} considers to be a dense row or column of the
+input matrix.  Rows or columns with more than @code{max (16, (dense *
+sqrt (@var{n})))} entries, where @var{n} is the order of the matrix @var{S},
+are ignored by @code{amd} during the calculation of the permutation.
+The value of dense must be a positive scalar and the default value is 10.0
+
+@item @var{opts}.aggressive
+If this value is a nonzero scalar, then @code{amd} performs aggressive
+absorption.  The default is not to perform aggressive absorption.
+@end table
+
+The author of the code itself is Timothy A. Davis
+(see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html}).
+@seealso{symamd, colamd}
+@end deftypefn */)
+{
+#if defined (HAVE_AMD)
+
+  int nargin = args.length ();
+
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+
+  octave_idx_type n_row, n_col;
+  const octave::suitesparse_integer *ridx, *cidx;
+  SparseMatrix sm;
+  SparseComplexMatrix scm;
+
+  if (args(0).issparse ())
+    {
+      if (args(0).iscomplex ())
+        {
+          scm = args(0).sparse_complex_matrix_value ();
+          n_row = scm.rows ();
+          n_col = scm.cols ();
+          ridx = octave::to_suitesparse_intptr (scm.xridx ());
+          cidx = octave::to_suitesparse_intptr (scm.xcidx ());
+        }
+      else
+        {
+          sm = args(0).sparse_matrix_value ();
+          n_row = sm.rows ();
+          n_col = sm.cols ();
+          ridx = octave::to_suitesparse_intptr (sm.xridx ());
+          cidx = octave::to_suitesparse_intptr (sm.xcidx ());
+        }
+    }
+  else
+    {
+      if (args(0).iscomplex ())
+        sm = SparseMatrix (real (args(0).complex_matrix_value ()));
+      else
+        sm = SparseMatrix (args(0).matrix_value ());
+
+      n_row = sm.rows ();
+      n_col = sm.cols ();
+      ridx = octave::to_suitesparse_intptr (sm.xridx ());
+      cidx = octave::to_suitesparse_intptr (sm.xcidx ());
+    }
+
+  if (n_row != n_col)
+    err_square_matrix_required ("amd", "S");
+
+  OCTAVE_LOCAL_BUFFER (double, Control, AMD_CONTROL);
+  AMD_NAME (_defaults) (Control);
+  if (nargin > 1)
+    {
+      octave_scalar_map arg1 = args(1).xscalar_map_value ("amd: OPTS argument must be a scalar structure");
+
+      octave_value tmp;
+
+      tmp = arg1.getfield ("dense");
+      if (tmp.is_defined ())
+        Control[AMD_DENSE] = tmp.double_value ();
+
+      tmp = arg1.getfield ("aggressive");
+      if (tmp.is_defined ())
+        Control[AMD_AGGRESSIVE] = tmp.double_value ();
+    }
+
+  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, P, n_col);
+  Matrix xinfo (AMD_INFO, 1);
+  double *Info = xinfo.fortran_vec ();
+
+  // FIXME: how can we manage the memory allocation of amd
+  //        in a cleaner manner?
+  SUITESPARSE_ASSIGN_FPTR (malloc_func, amd_malloc, malloc);
+  SUITESPARSE_ASSIGN_FPTR (free_func, amd_free, free);
+  SUITESPARSE_ASSIGN_FPTR (calloc_func, amd_calloc, calloc);
+  SUITESPARSE_ASSIGN_FPTR (realloc_func, amd_realloc, realloc);
+  SUITESPARSE_ASSIGN_FPTR (printf_func, amd_printf, printf);
+
+  octave_idx_type result = AMD_NAME (_order) (n_col, cidx, ridx, P, Control,
+                                              Info);
+
+  if (result == AMD_OUT_OF_MEMORY)
+    error ("amd: out of memory");
+  else if (result == AMD_INVALID)
+    error ("amd: matrix S is corrupted");
+
+  Matrix Pout (1, n_col);
+  for (octave_idx_type i = 0; i < n_col; i++)
+    Pout.xelem (i) = P[i] + 1;
+
+  if (nargout > 1)
+    return ovl (Pout, xinfo);
+  else
+    return ovl (Pout);
+
+#else
+
+  octave_unused_parameter (args);
+  octave_unused_parameter (nargout);
+
+  err_disabled_feature ("amd", "AMD");
+
+#endif
+}
+
+/*
+%!shared A, A2, opts
+%! A = ones (20, 30);
+%! A2 = ones (30, 30);
+
+%!testif HAVE_AMD
+%! assert(amd (A2), [1:30]);
+%! opts.dense = 25;
+%! assert(amd (A2, opts), [1:30]);
+%! opts.aggressive = 1;
+%! assert(amd (A2, opts), [1:30]);
+
+%!testif HAVE_AMD
+%! assert (amd ([]), zeros (1,0))
+
+%!error <S must be a square matrix|was unavailable or disabled> amd (A)
+%!error amd (A2, 2)
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/ccolamd.cc	Wed Jan 29 06:30:40 2020 -0500
@@ -0,0 +1,583 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2005-2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+// This is the octave interface to ccolamd, which bore the copyright given
+// in the help of the functions.
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <cstdlib>
+
+#include "CSparse.h"
+#include "Sparse.h"
+#include "dNDArray.h"
+#include "oct-locbuf.h"
+#include "oct-sparse.h"
+
+#include "defun.h"
+#include "error.h"
+#include "errwarn.h"
+#include "ov.h"
+#include "pager.h"
+
+DEFUN (ccolamd, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{p} =} ccolamd (@var{S})
+@deftypefnx {} {@var{p} =} ccolamd (@var{S}, @var{knobs})
+@deftypefnx {} {@var{p} =} ccolamd (@var{S}, @var{knobs}, @var{cmember})
+@deftypefnx {} {[@var{p}, @var{stats}] =} ccolamd (@dots{})
+
+Constrained column approximate minimum degree permutation.
+
+@code{@var{p} = ccolamd (@var{S})} returns the column approximate minimum
+degree permutation vector for the sparse matrix @var{S}.  For a
+non-symmetric matrix @var{S}, @code{@var{S}(:, @var{p})} tends to have
+sparser LU@tie{}factors than @var{S}.
+@code{chol (@var{S}(:, @var{p})' * @var{S}(:, @var{p}))} also tends to be
+sparser than @code{chol (@var{S}' * @var{S})}.
+@code{@var{p} = ccolamd (@var{S}, 1)} optimizes the ordering for
+@code{lu (@var{S}(:, @var{p}))}.  The ordering is followed by a column
+elimination tree post-ordering.
+
+@var{knobs} is an optional 1-element to 5-element input vector, with a
+default value of @code{[0 10 10 1 0]} if not present or empty.  Entries not
+present are set to their defaults.
+
+@table @code
+@item @var{knobs}(1)
+if nonzero, the ordering is optimized for @code{lu (S(:, p))}.  It will be a
+poor ordering for @code{chol (@var{S}(:, @var{p})' * @var{S}(:, @var{p}))}.
+This is the most important knob for ccolamd.
+
+@item @var{knobs}(2)
+if @var{S} is m-by-n, rows with more than
+@code{max (16, @var{knobs}(2) * sqrt (n))} entries are ignored.
+
+@item @var{knobs}(3)
+columns with more than
+@code{max (16, @var{knobs}(3) * sqrt (min (@var{m}, @var{n})))} entries are
+ignored and ordered last in the output permutation
+(subject to the cmember constraints).
+
+@item @var{knobs}(4)
+if nonzero, aggressive absorption is performed.
+
+@item @var{knobs}(5)
+if nonzero, statistics and knobs are printed.
+
+@end table
+
+@var{cmember} is an optional vector of length @math{n}.  It defines the
+constraints on the column ordering.  If @code{@var{cmember}(j) = @var{c}},
+then column @var{j} is in constraint set @var{c} (@var{c} must be in the
+range 1 to n).  In the output permutation @var{p}, all columns in set 1
+appear first, followed by all columns in set 2, and so on.
+@code{@var{cmember} = ones (1,n)} if not present or empty.
+@code{ccolamd (@var{S}, [], 1 : n)} returns @code{1 : n}
+
+@code{@var{p} = ccolamd (@var{S})} is about the same as
+@code{@var{p} = colamd (@var{S})}.  @var{knobs} and its default values
+differ.  @code{colamd} always does aggressive absorption, and it finds an
+ordering suitable for both @code{lu (@var{S}(:, @var{p}))} and @code{chol
+(@var{S}(:, @var{p})' * @var{S}(:, @var{p}))}; it cannot optimize its
+ordering for @code{lu (@var{S}(:, @var{p}))} to the extent that
+@code{ccolamd (@var{S}, 1)} can.
+
+@var{stats} is an optional 20-element output vector that provides data
+about the ordering and the validity of the input matrix @var{S}.  Ordering
+statistics are in @code{@var{stats}(1 : 3)}.  @code{@var{stats}(1)} and
+@code{@var{stats}(2)} are the number of dense or empty rows and columns
+ignored by @sc{ccolamd} and @code{@var{stats}(3)} is the number of garbage
+collections performed on the internal data structure used by @sc{ccolamd}
+(roughly of size @code{2.2 * nnz (@var{S}) + 4 * @var{m} + 7 * @var{n}}
+integers).
+
+@code{@var{stats}(4 : 7)} provide information if CCOLAMD was able to
+continue.  The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if
+invalid.  @code{@var{stats}(5)} is the rightmost column index that is
+unsorted or contains duplicate entries, or zero if no such column exists.
+@code{@var{stats}(6)} is the last seen duplicate or out-of-order row
+index in the column index given by @code{@var{stats}(5)}, or zero if no
+such row index exists.  @code{@var{stats}(7)} is the number of duplicate
+or out-of-order row indices.  @code{@var{stats}(8 : 20)} is always zero in
+the current version of @sc{ccolamd} (reserved for future use).
+
+The authors of the code itself are @nospell{S. Larimore, T. Davis} and
+@nospell{S. Rajamanickam} in collaboration with @nospell{J. Bilbert and E. Ng}.
+Supported by the National Science Foundation
+@nospell{(DMS-9504974, DMS-9803599, CCR-0203270)}, and a grant from
+@nospell{Sandia} National Lab.
+See @url{http://faculty.cse.tamu.edu/davis/suitesparse.html} for ccolamd,
+csymamd, amd, colamd, symamd, and other related orderings.
+@seealso{colamd, csymamd}
+@end deftypefn */)
+{
+#if defined (HAVE_CCOLAMD)
+
+  int nargin = args.length ();
+
+  if (nargin < 1 || nargin > 3)
+    print_usage ();
+
+  octave_value_list retval (nargout == 2 ? 2 : 1);
+  int spumoni = 0;
+
+  // Get knobs
+  static_assert (CCOLAMD_KNOBS <= 40,
+                 "ccolamd: # of CCOLAMD_KNOBS exceeded.  Please report this to bugs.octave.org");
+  double knob_storage[CCOLAMD_KNOBS];
+  double *knobs = &knob_storage[0];
+  CCOLAMD_NAME (_set_defaults) (knobs);
+
+  // Check for user-passed knobs
+  if (nargin > 1)
+    {
+      NDArray User_knobs = args(1).array_value ();
+      int nel_User_knobs = User_knobs.numel ();
+
+      if (nel_User_knobs > 0)
+        knobs[CCOLAMD_LU] = (User_knobs(0) != 0);
+      if (nel_User_knobs > 1)
+        knobs[CCOLAMD_DENSE_ROW] = User_knobs(1);
+      if (nel_User_knobs > 2)
+        knobs[CCOLAMD_DENSE_COL] = User_knobs(2);
+      if (nel_User_knobs > 3)
+        knobs[CCOLAMD_AGGRESSIVE] = (User_knobs(3) != 0);
+      if (nel_User_knobs > 4)
+        spumoni = (User_knobs(4) != 0);
+
+      // print knob settings if spumoni is set
+      if (spumoni)
+        {
+          octave_stdout << "\nccolamd version " << CCOLAMD_MAIN_VERSION << '.'
+                        <<  CCOLAMD_SUB_VERSION << ", " << CCOLAMD_DATE
+                        << ":\nknobs(1): " << User_knobs(0) << ", order for ";
+          if (knobs[CCOLAMD_LU] != 0)
+            octave_stdout << "lu (A)\n";
+          else
+            octave_stdout << "chol (A'*A)\n";
+
+          if (knobs[CCOLAMD_DENSE_ROW] >= 0)
+            octave_stdout << "knobs(2): " << User_knobs(1)
+                          << ", rows with > max (16,"
+                          << knobs[CCOLAMD_DENSE_ROW]
+                          << "*sqrt (size(A,2)))"
+                          << " entries removed\n";
+          else
+            octave_stdout << "knobs(2): " << User_knobs(1)
+                          << ", no dense rows removed\n";
+
+          if (knobs[CCOLAMD_DENSE_COL] >= 0)
+            octave_stdout << "knobs(3): " << User_knobs(2)
+                          << ", cols with > max (16,"
+                          << knobs[CCOLAMD_DENSE_COL] << "*sqrt (size(A)))"
+                          << " entries removed\n";
+          else
+            octave_stdout << "knobs(3): " << User_knobs(2)
+                          << ", no dense columns removed\n";
+
+          if (knobs[CCOLAMD_AGGRESSIVE] != 0)
+            octave_stdout << "knobs(4): " << User_knobs(3)
+                          << ", aggressive absorption: yes";
+          else
+            octave_stdout << "knobs(4): " << User_knobs(3)
+                          << ", aggressive absorption: no";
+
+          octave_stdout << "knobs(5): " << User_knobs(4)
+                        << ", statistics and knobs printed\n";
+        }
+    }
+
+  octave_idx_type n_row, n_col, nnz;
+  octave_idx_type *ridx, *cidx;
+  SparseComplexMatrix scm;
+  SparseMatrix sm;
+
+  if (args(0).issparse ())
+    {
+      if (args(0).iscomplex ())
+        {
+          scm = args(0).sparse_complex_matrix_value ();
+          n_row = scm.rows ();
+          n_col = scm.cols ();
+          nnz = scm.nnz ();
+          ridx = scm.xridx ();
+          cidx = scm.xcidx ();
+        }
+      else
+        {
+          sm = args(0).sparse_matrix_value ();
+
+          n_row = sm.rows ();
+          n_col = sm.cols ();
+          nnz = sm.nnz ();
+          ridx = sm.xridx ();
+          cidx = sm.xcidx ();
+        }
+    }
+  else
+    {
+      if (args(0).iscomplex ())
+        sm = SparseMatrix (real (args(0).complex_matrix_value ()));
+      else
+        sm = SparseMatrix (args(0).matrix_value ());
+
+      n_row = sm.rows ();
+      n_col = sm.cols ();
+      nnz = sm.nnz ();
+      ridx = sm.xridx ();
+      cidx = sm.xcidx ();
+    }
+
+  // Allocate workspace for ccolamd
+  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, p, n_col+1);
+  for (octave_idx_type i = 0; i < n_col+1; i++)
+    p[i] = cidx[i];
+
+  octave_idx_type Alen = CCOLAMD_NAME (_recommended) (nnz, n_row, n_col);
+  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, A, Alen);
+  for (octave_idx_type i = 0; i < nnz; i++)
+    A[i] = ridx[i];
+
+  static_assert (CCOLAMD_STATS <= 40,
+                 "ccolamd: # of CCOLAMD_STATS exceeded.  Please report this to bugs.octave.org");
+  octave::suitesparse_integer stats_storage[CCOLAMD_STATS];
+  octave::suitesparse_integer *stats = &stats_storage[0];
+
+  if (nargin > 2)
+    {
+      NDArray in_cmember = args(2).array_value ();
+      octave_idx_type cslen = in_cmember.numel ();
+      OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, cmember, cslen);
+      for (octave_idx_type i = 0; i < cslen; i++)
+        // convert cmember from 1-based to 0-based
+        cmember[i] = static_cast<octave::suitesparse_integer>(in_cmember(i) - 1);
+
+      if (cslen != n_col)
+        error ("ccolamd: CMEMBER must be of length equal to #cols of A");
+
+      // Order the columns (destroys A)
+      if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats,cmember))
+        {
+          CCOLAMD_NAME (_report) (stats);
+
+          error ("ccolamd: internal error!");
+        }
+    }
+  else
+    {
+      // Order the columns (destroys A)
+      if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats, nullptr))
+        {
+          CCOLAMD_NAME (_report) (stats);
+
+          error ("ccolamd: internal error!");
+        }
+    }
+
+  // return the permutation vector
+  NDArray out_perm (dim_vector (1, n_col));
+  for (octave_idx_type i = 0; i < n_col; i++)
+    out_perm(i) = p[i] + 1;
+
+  retval(0) = out_perm;
+
+  // print stats if spumoni > 0
+  if (spumoni > 0)
+    CCOLAMD_NAME (_report) (stats);
+
+  // Return the stats vector
+  if (nargout == 2)
+    {
+      NDArray out_stats (dim_vector (1, CCOLAMD_STATS));
+      for (octave_idx_type i = 0 ; i < CCOLAMD_STATS ; i++)
+        out_stats(i) = stats[i];
+      retval(1) = out_stats;
+
+      // fix stats (5) and (6), for 1-based information on
+      // jumbled matrix.  note that this correction doesn't
+      // occur if symamd returns FALSE
+      out_stats(CCOLAMD_INFO1)++;
+      out_stats(CCOLAMD_INFO2)++;
+    }
+
+  return retval;
+
+#else
+
+  octave_unused_parameter (args);
+  octave_unused_parameter (nargout);
+
+  err_disabled_feature ("ccolamd", "CCOLAMD");
+
+#endif
+}
+
+DEFUN (csymamd, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{p} =} csymamd (@var{S})
+@deftypefnx {} {@var{p} =} csymamd (@var{S}, @var{knobs})
+@deftypefnx {} {@var{p} =} csymamd (@var{S}, @var{knobs}, @var{cmember})
+@deftypefnx {} {[@var{p}, @var{stats}] =} csymamd (@dots{})
+
+For a symmetric positive definite matrix @var{S}, return the permutation
+vector @var{p} such that @code{@var{S}(@var{p},@var{p})} tends to have a
+sparser Cholesky@tie{}factor than @var{S}.
+
+Sometimes @code{csymamd} works well for symmetric indefinite matrices too.
+The matrix @var{S} is assumed to be symmetric; only the strictly lower
+triangular part is referenced.  @var{S} must be square.  The ordering is
+followed by an elimination tree post-ordering.
+
+@var{knobs} is an optional 1-element to 3-element input vector, with a
+default value of @code{[10 1 0]}.  Entries not present are set to their
+defaults.
+
+@table @code
+@item @var{knobs}(1)
+If @var{S} is n-by-n, then rows and columns with more than
+@code{max(16,@var{knobs}(1)*sqrt(n))} entries are ignored, and ordered
+last in the output permutation (subject to the cmember constraints).
+
+@item @var{knobs}(2)
+If nonzero, aggressive absorption is performed.
+
+@item @var{knobs}(3)
+If nonzero, statistics and knobs are printed.
+
+@end table
+
+@var{cmember} is an optional vector of length n.  It defines the constraints
+on the ordering.  If @code{@var{cmember}(j) = @var{S}}, then row/column j is
+in constraint set @var{c} (@var{c} must be in the range 1 to n).  In the
+output permutation @var{p}, rows/columns in set 1 appear first, followed
+by all rows/columns in set 2, and so on.  @code{@var{cmember} = ones (1,n)}
+if not present or empty.  @code{csymamd (@var{S},[],1:n)} returns
+@code{1:n}.
+
+@code{@var{p} = csymamd (@var{S})} is about the same as
+@code{@var{p} = symamd (@var{S})}.  @var{knobs} and its default values
+differ.
+
+@code{@var{stats}(4:7)} provide information if CCOLAMD was able to
+continue.  The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if
+invalid.  @code{@var{stats}(5)} is the rightmost column index that is
+unsorted or contains duplicate entries, or zero if no such column exists.
+@code{@var{stats}(6)} is the last seen duplicate or out-of-order row
+index in the column index given by @code{@var{stats}(5)}, or zero if no
+such row index exists.  @code{@var{stats}(7)} is the number of duplicate
+or out-of-order row indices.  @code{@var{stats}(8:20)} is always zero in
+the current version of @sc{ccolamd} (reserved for future use).
+
+The authors of the code itself are @nospell{S. Larimore, T. Davis} and
+@nospell{S. Rajamanickam} in collaboration with @nospell{J. Bilbert and E. Ng}.
+Supported by the National Science Foundation
+@nospell{(DMS-9504974, DMS-9803599, CCR-0203270)}, and a grant from
+@nospell{Sandia} National Lab.
+See @url{http://faculty.cse.tamu.edu/davis/suitesparse.html} for ccolamd,
+colamd, csymamd, amd, colamd, symamd, and other related orderings.
+@seealso{symamd, ccolamd}
+@end deftypefn */)
+{
+#if defined (HAVE_CCOLAMD)
+
+  int nargin = args.length ();
+
+  if (nargin < 1 || nargin > 3)
+    print_usage ();
+
+  octave_value_list retval (nargout == 2 ? 2 : 1);
+  int spumoni = 0;
+
+  // Get knobs
+  static_assert (CCOLAMD_KNOBS <= 40,
+                 "csymamd: # of CCOLAMD_KNOBS exceeded.  Please report this to bugs.octave.org");
+  double knob_storage[CCOLAMD_KNOBS];
+  double *knobs = &knob_storage[0];
+  CCOLAMD_NAME (_set_defaults) (knobs);
+
+  // Check for user-passed knobs
+  if (nargin > 1)
+    {
+      NDArray User_knobs = args(1).array_value ();
+      int nel_User_knobs = User_knobs.numel ();
+
+      if (nel_User_knobs > 0)
+        knobs[CCOLAMD_DENSE_ROW] = User_knobs(0);
+      if (nel_User_knobs > 1)
+        knobs[CCOLAMD_AGGRESSIVE] = User_knobs(1);
+      if (nel_User_knobs > 2)
+        spumoni = static_cast<int> (User_knobs(2));
+
+      // print knob settings if spumoni is set
+      if (spumoni)
+        {
+          octave_stdout << "\ncsymamd version " << CCOLAMD_MAIN_VERSION
+                        << '.' << CCOLAMD_SUB_VERSION
+                        << ", " << CCOLAMD_DATE << "\n";
+
+          if (knobs[CCOLAMD_DENSE_ROW] >= 0)
+            octave_stdout << "knobs(1): " << User_knobs(0)
+                          << ", rows/cols with > max (16,"
+                          << knobs[CCOLAMD_DENSE_ROW]
+                          << "*sqrt (size(A,2)))"
+                          << " entries removed\n";
+          else
+            octave_stdout << "knobs(1): " << User_knobs(0)
+                          << ", no dense rows/cols removed\n";
+
+          if (knobs[CCOLAMD_AGGRESSIVE] != 0)
+            octave_stdout << "knobs(2): " << User_knobs(1)
+                          << ", aggressive absorption: yes";
+          else
+            octave_stdout << "knobs(2): " << User_knobs(1)
+                          << ", aggressive absorption: no";
+
+          octave_stdout << "knobs(3): " << User_knobs(2)
+                        << ", statistics and knobs printed\n";
+        }
+    }
+
+  octave_idx_type n_row, n_col;
+  octave_idx_type *ridx, *cidx;
+  SparseMatrix sm;
+  SparseComplexMatrix scm;
+
+  if (args(0).issparse ())
+    {
+      if (args(0).iscomplex ())
+        {
+          scm = args(0).sparse_complex_matrix_value ();
+          n_row = scm.rows ();
+          n_col = scm.cols ();
+          ridx = scm.xridx ();
+          cidx = scm.xcidx ();
+        }
+      else
+        {
+          sm = args(0).sparse_matrix_value ();
+          n_row = sm.rows ();
+          n_col = sm.cols ();
+          ridx = sm.xridx ();
+          cidx = sm.xcidx ();
+        }
+    }
+  else
+    {
+      if (args(0).iscomplex ())
+        sm = SparseMatrix (real (args(0).complex_matrix_value ()));
+      else
+        sm = SparseMatrix (args(0).matrix_value ());
+
+      n_row = sm.rows ();
+      n_col = sm.cols ();
+      ridx = sm.xridx ();
+      cidx = sm.xcidx ();
+    }
+
+  if (n_row != n_col)
+    err_square_matrix_required ("csymamd", "S");
+
+  // Allocate workspace for symamd
+  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, perm, n_col+1);
+  static_assert (CCOLAMD_STATS <= 40,
+                 "csymamd: # of CCOLAMD_STATS exceeded.  Please report this to bugs.octave.org");
+  octave::suitesparse_integer stats_storage[CCOLAMD_STATS];
+  octave::suitesparse_integer *stats = &stats_storage[0];
+
+  if (nargin > 2)
+    {
+      NDArray in_cmember = args(2).array_value ();
+      octave_idx_type cslen = in_cmember.numel ();
+      OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, cmember, cslen);
+      for (octave_idx_type i = 0; i < cslen; i++)
+        // convert cmember from 1-based to 0-based
+        cmember[i] = static_cast<octave_idx_type> (in_cmember(i) - 1);
+
+      if (cslen != n_col)
+        error ("csymamd: CMEMBER must be of length equal to #cols of A");
+
+      if (! CSYMAMD_NAME () (n_col,
+                             octave::to_suitesparse_intptr (ridx),
+                             octave::to_suitesparse_intptr (cidx),
+                             perm, knobs, stats, &calloc, &free, cmember, -1))
+        {
+          CSYMAMD_NAME (_report)(stats);
+
+          error ("csymamd: internal error!");
+        }
+    }
+  else
+    {
+      if (! CSYMAMD_NAME () (n_col,
+                             octave::to_suitesparse_intptr (ridx),
+                             octave::to_suitesparse_intptr (cidx),
+                             perm, knobs, stats, &calloc, &free, nullptr, -1))
+        {
+          CSYMAMD_NAME (_report)(stats);
+
+          error ("csymamd: internal error!");
+        }
+    }
+
+  // return the permutation vector
+  NDArray out_perm (dim_vector (1, n_col));
+  for (octave_idx_type i = 0; i < n_col; i++)
+    out_perm(i) = perm[i] + 1;
+
+  retval(0) = out_perm;
+
+  // print stats if spumoni > 0
+  if (spumoni > 0)
+    CSYMAMD_NAME (_report)(stats);
+
+  // Return the stats vector
+  if (nargout == 2)
+    {
+      NDArray out_stats (dim_vector (1, CCOLAMD_STATS));
+      for (octave_idx_type i = 0 ; i < CCOLAMD_STATS ; i++)
+        out_stats(i) = stats[i];
+      retval(1) = out_stats;
+
+      // fix stats (5) and (6), for 1-based information on
+      // jumbled matrix.  note that this correction doesn't
+      // occur if symamd returns FALSE
+      out_stats(CCOLAMD_INFO1)++;
+      out_stats(CCOLAMD_INFO2)++;
+    }
+
+  return retval;
+
+#else
+
+  octave_unused_parameter (args);
+  octave_unused_parameter (nargout);
+
+  err_disabled_feature ("csymamd", "CCOLAMD");
+
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/chol.cc	Wed Jan 29 06:30:40 2020 -0500
@@ -0,0 +1,1331 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 1996-2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <string>
+
+#include "Matrix.h"
+#include "chol.h"
+#include "oct-string.h"
+#include "sparse-chol.h"
+#include "sparse-util.h"
+
+#include "defun.h"
+#include "error.h"
+#include "errwarn.h"
+#include "ov.h"
+#include "ovl.h"
+
+template <typename CHOLT>
+static octave_value
+get_chol (const CHOLT& fact)
+{
+  return octave_value (fact.chol_matrix());
+}
+
+template <typename CHOLT>
+static octave_value
+get_chol_r (const CHOLT& fact)
+{
+  return octave_value (fact.chol_matrix (),
+                       MatrixType (MatrixType::Upper));
+}
+
+template <typename CHOLT>
+static octave_value
+get_chol_l (const CHOLT& fact)
+{
+  return octave_value (fact.chol_matrix ().transpose (),
+                       MatrixType (MatrixType::Lower));
+}
+
+DEFUN (chol, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{R} =} chol (@var{A})
+@deftypefnx {} {[@var{R}, @var{p}] =} chol (@var{A})
+@deftypefnx {} {[@var{R}, @var{p}, @var{Q}] =} chol (@var{A})
+@deftypefnx {} {[@var{R}, @var{p}, @var{Q}] =} chol (@var{A}, "vector")
+@deftypefnx {} {[@var{L}, @dots{}] =} chol (@dots{}, "lower")
+@deftypefnx {} {[@var{R}, @dots{}] =} chol (@dots{}, "upper")
+@cindex Cholesky factorization
+Compute the upper Cholesky@tie{}factor, @var{R}, of the real symmetric
+or complex Hermitian positive definite matrix @var{A}.
+
+The upper Cholesky@tie{}factor @var{R} is computed by using the upper
+triangular part of matrix @var{A} and is defined by
+@tex
+$ R^T R = A $.
+@end tex
+@ifnottex
+
+@example
+@var{R}' * @var{R} = @var{A}.
+@end example
+
+@end ifnottex
+
+Calling @code{chol} using the optional @qcode{"upper"} flag has the
+same behavior.  In contrast, using the optional @qcode{"lower"} flag,
+@code{chol} returns the lower triangular factorization, computed by using
+the lower triangular part of matrix @var{A}, such that
+@tex
+$ L L^T = A $.
+@end tex
+@ifnottex
+
+@example
+@var{L} * @var{L}' = @var{A}.
+@end example
+
+@end ifnottex
+
+Called with one output argument @code{chol} fails if matrix @var{A} is
+not positive definite.  Note that if matrix @var{A} is not real symmetric
+or complex Hermitian then the lower triangular part is considered to be
+the (complex conjugate) transpose of the upper triangular part, or vice
+versa, given the @qcode{"lower"} flag.
+
+Called with two or more output arguments @var{p} flags whether the matrix
+@var{A} was positive definite and @code{chol} does not fail.  A zero value
+of @var{p} indicates that matrix @var{A} is positive definite and @var{R}
+gives the factorization.  Otherwise, @var{p} will have a positive value.
+
+If called with three output arguments matrix @var{A} must be sparse and
+a sparsity preserving row/column permutation is applied to matrix @var{A}
+prior to the factorization.  That is @var{R} is the factorization of
+@code{@var{A}(@var{Q},@var{Q})} such that
+@tex
+$ R^T R = Q^T A Q$.
+@end tex
+@ifnottex
+
+@example
+@var{R}' * @var{R} = @var{Q}' * @var{A} * @var{Q}.
+@end example
+
+@end ifnottex
+
+The sparsity preserving permutation is generally returned as a matrix.
+However, given the optional flag @qcode{"vector"}, @var{Q} will be
+returned as a vector such that
+@tex
+$ R^T R = A (Q, Q)$.
+@end tex
+@ifnottex
+
+@example
+@var{R}' * @var{R} = @var{A}(@var{Q}, @var{Q}).
+@end example
+
+@end ifnottex
+
+In general the lower triangular factorization is significantly faster for
+sparse matrices.
+@seealso{hess, lu, qr, qz, schur, svd, ichol, cholinv, chol2inv, cholupdate, cholinsert, choldelete, cholshift}
+@end deftypefn */)
+{
+  int nargin = args.length ();
+
+  if (nargin < 1 || nargin > 3 || nargout > 3)
+    print_usage ();
+  if (nargout > 2 && ! args(0).issparse ())
+    error ("chol: using three output arguments, matrix A must be sparse");
+
+  bool LLt = false;
+  bool vecout = false;
+
+  int n = 1;
+  while (n < nargin)
+    {
+      std::string tmp = args(n++).xstring_value ("chol: optional arguments must be strings");
+
+      if (octave::string::strcmpi (tmp, "vector"))
+        vecout = true;
+      else if (octave::string::strcmpi (tmp, "lower"))
+        LLt = true;
+      else if (octave::string::strcmpi (tmp, "upper"))
+        LLt = false;
+      else
+        error (R"(chol: optional argument must be one of "vector", "lower", or "upper")");
+    }
+
+  octave_value_list retval;
+  octave_value arg = args(0);
+
+  if (arg.isempty ())
+    return ovl (Matrix ());
+
+  if (arg.issparse ())
+    {
+      octave_idx_type info;
+      bool natural = (nargout != 3);
+      bool force = nargout > 1;
+
+      if (arg.isreal ())
+        {
+          SparseMatrix m = arg.sparse_matrix_value ();
+
+          octave::math::sparse_chol<SparseMatrix> fact (m, info, natural, force);
+
+          if (nargout == 3)
+            {
+              if (vecout)
+                retval(2) = fact.perm ();
+              else
+                retval(2) = fact.Q ();
+            }
+
+          if (nargout >= 2 || info == 0)
+            {
+              retval(1) = info;
+              if (LLt)
+                retval(0) = fact.L ();
+              else
+                retval(0) = fact.R ();
+            }
+          else
+            error ("chol: input matrix must be positive definite");
+        }
+      else if (arg.iscomplex ())
+        {
+          SparseComplexMatrix m = arg.sparse_complex_matrix_value ();
+
+          octave::math::sparse_chol<SparseComplexMatrix> fact (m, info, natural, force);
+
+          if (nargout == 3)
+            {
+              if (vecout)
+                retval(2) = fact.perm ();
+              else
+                retval(2) = fact.Q ();
+            }
+
+          if (nargout >= 2 || info == 0)
+            {
+              retval(1) = info;
+              if (LLt)
+                retval(0) = fact.L ();
+              else
+                retval(0) = fact.R ();
+            }
+          else
+            error ("chol: input matrix must be positive definite");
+        }
+      else
+        err_wrong_type_arg ("chol", arg);
+    }
+  else if (arg.is_single_type ())
+    {
+      if (vecout)
+        error (R"(chol: A must be sparse for the "vector" option)");
+      if (arg.isreal ())
+        {
+          FloatMatrix m = arg.float_matrix_value ();
+
+          octave_idx_type info;
+
+          octave::math::chol<FloatMatrix> fact (m, info, LLt != true);
+
+          if (nargout == 2 || info == 0)
+            retval = ovl (get_chol (fact), info);
+          else
+            error ("chol: input matrix must be positive definite");
+        }
+      else if (arg.iscomplex ())
+        {
+          FloatComplexMatrix m = arg.float_complex_matrix_value ();
+
+          octave_idx_type info;
+
+          octave::math::chol<FloatComplexMatrix> fact (m, info, LLt != true);
+
+          if (nargout == 2 || info == 0)
+            retval = ovl (get_chol (fact), info);
+          else
+            error ("chol: input matrix must be positive definite");
+        }
+      else
+        err_wrong_type_arg ("chol", arg);
+    }
+  else
+    {
+      if (vecout)
+        error (R"(chol: A must be sparse for the "vector" option)");
+      if (arg.isreal ())
+        {
+          Matrix m = arg.matrix_value ();
+
+          octave_idx_type info;
+
+          octave::math::chol<Matrix> fact (m, info, LLt != true);
+
+          if (nargout == 2 || info == 0)
+            retval = ovl (get_chol (fact), info);
+          else
+            error ("chol: input matrix must be positive definite");
+        }
+      else if (arg.iscomplex ())
+        {
+          ComplexMatrix m = arg.complex_matrix_value ();
+
+          octave_idx_type info;
+
+          octave::math::chol<ComplexMatrix> fact (m, info, LLt != true);
+
+          if (nargout == 2 || info == 0)
+            retval = ovl (get_chol (fact), info);
+          else
+            error ("chol: input matrix must be positive definite");
+        }
+      else
+        err_wrong_type_arg ("chol", arg);
+    }
+
+  return retval;
+}
+
+/*
+%!assert (chol ([2, 1; 1, 1]), [sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)], sqrt (eps))
+%!assert (chol (single ([2, 1; 1, 1])), single ([sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)]), sqrt (eps ("single")))
+
+%!assert (chol ([2, 1; 1, 1], "upper"), [sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)],
+%!        sqrt (eps))
+%!assert (chol ([2, 1; 1, 1], "lower"), [sqrt(2), 0; 1/sqrt(2), 1/sqrt(2)],
+%!        sqrt (eps))
+
+%!assert (chol ([2, 1; 1, 1], "lower"), chol ([2, 1; 1, 1], "LoweR"))
+%!assert (chol ([2, 1; 1, 1], "upper"), chol ([2, 1; 1, 1], "Upper"))
+
+## Check the "vector" option which only affects the 3rd argument and
+## is only valid for sparse input.
+%!testif HAVE_CHOLMOD
+%! a = sparse ([2 1; 1 1]);
+%! r = sparse ([sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)]);
+%! [rd, pd, qd] = chol (a);
+%! [rv, pv, qv] = chol (a, "vector");
+%! assert (r, rd, eps)
+%! assert (r, rv, eps)
+%! assert (pd, 0)
+%! assert (pd, pv)
+%! assert (qd, sparse (eye (2)))
+%! assert (qv, [1 2])
+%!
+%! [rv, pv, qv] = chol (a, "Vector"); # check case sensitivity
+%! assert (r, rv, eps)
+%! assert (pd, pv)
+%! assert (qv, [1 2])
+
+%!testif HAVE_CHOLMOD <*42587>
+%! A = sparse ([1 0 8;0 1 8;8 8 1]);
+%! [Q, p] = chol (A);
+%! assert (p != 0);
+
+%!error chol ()
+%!error <matrix must be positive definite> chol ([1, 2; 3, 4])
+%!error <requires square matrix> chol ([1, 2; 3, 4; 5, 6])
+%!error <optional arguments must be strings> chol (1, 2)
+%!error <optional argument must be one of "vector", "lower"> chol (1, "foobar")
+%!error <matrix A must be sparse> [L,p,Q] = chol ([1, 2; 3, 4])
+%!error <A must be sparse> [L, p] = chol ([1, 2; 3, 4], "vector")
+*/
+
+DEFUN (cholinv, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {} cholinv (@var{A})
+Compute the inverse of the symmetric positive definite matrix @var{A} using
+the Cholesky@tie{}factorization.
+@seealso{chol, chol2inv, inv}
+@end deftypefn */)
+{
+  if (args.length () != 1)
+    print_usage ();
+
+  octave_value retval;
+  octave_value arg = args(0);
+
+  octave_idx_type nr = arg.rows ();
+  octave_idx_type nc = arg.columns ();
+
+  if (nr == 0 || nc == 0)
+    retval = Matrix ();
+  else
+    {
+      if (arg.issparse ())
+        {
+          octave_idx_type info;
+
+          if (arg.isreal ())
+            {
+              SparseMatrix m = arg.sparse_matrix_value ();
+
+              octave::math::sparse_chol<SparseMatrix> chol (m, info);
+
+              if (info == 0)
+                retval = chol.inverse ();
+              else
+                error ("cholinv: A must be positive definite");
+            }
+          else if (arg.iscomplex ())
+            {
+              SparseComplexMatrix m = arg.sparse_complex_matrix_value ();
+
+              octave::math::sparse_chol<SparseComplexMatrix> chol (m, info);
+
+              if (info == 0)
+                retval = chol.inverse ();
+              else
+                error ("cholinv: A must be positive definite");
+            }
+          else
+            err_wrong_type_arg ("cholinv", arg);
+        }
+      else if (arg.is_single_type ())
+        {
+          if (arg.isreal ())
+            {
+              FloatMatrix m = arg.float_matrix_value ();
+
+              octave_idx_type info;
+              octave::math::chol<FloatMatrix> chol (m, info);
+              if (info == 0)
+                retval = chol.inverse ();
+              else
+                error ("cholinv: A must be positive definite");
+            }
+          else if (arg.iscomplex ())
+            {
+              FloatComplexMatrix m = arg.float_complex_matrix_value ();
+
+              octave_idx_type info;
+              octave::math::chol<FloatComplexMatrix> chol (m, info);
+              if (info == 0)
+                retval = chol.inverse ();
+              else
+                error ("cholinv: A must be positive definite");
+            }
+          else
+            err_wrong_type_arg ("chol", arg);
+        }
+      else
+        {
+          if (arg.isreal ())
+            {
+              Matrix m = arg.matrix_value ();
+
+              octave_idx_type info;
+              octave::math::chol<Matrix> chol (m, info);
+              if (info == 0)
+                retval = chol.inverse ();
+              else
+                error ("cholinv: A must be positive definite");
+            }
+          else if (arg.iscomplex ())
+            {
+              ComplexMatrix m = arg.complex_matrix_value ();
+
+              octave_idx_type info;
+              octave::math::chol<ComplexMatrix> chol (m, info);
+              if (info == 0)
+                retval = chol.inverse ();
+              else
+                error ("cholinv: A must be positive definite");
+            }
+          else
+            err_wrong_type_arg ("chol", arg);
+        }
+    }
+
+  return retval;
+}
+
+/*
+%!shared A, Ainv
+%! A = [2,0.2;0.2,1];
+%! Ainv = inv (A);
+%!test
+%! Ainv1 = cholinv (A);
+%! assert (norm (Ainv-Ainv1), 0, 1e-10);
+%!testif HAVE_CHOLMOD
+%! Ainv2 = inv (sparse (A));
+%! assert (norm (Ainv-Ainv2), 0, 1e-10);
+%!testif HAVE_CHOLMOD
+%! Ainv3 = cholinv (sparse (A));
+%! assert (norm (Ainv-Ainv3), 0, 1e-10);
+*/
+
+DEFUN (chol2inv, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {} chol2inv (@var{U})
+Invert a symmetric, positive definite square matrix from its Cholesky
+decomposition, @var{U}.
+
+Note that @var{U} should be an upper-triangular matrix with positive
+diagonal elements.  @code{chol2inv (@var{U})} provides
+@code{inv (@var{U}'*@var{U})} but it is much faster than using @code{inv}.
+@seealso{chol, cholinv, inv}
+@end deftypefn */)
+{
+  if (args.length () != 1)
+    print_usage ();
+
+  octave_value retval;
+
+  octave_value arg = args(0);
+
+  octave_idx_type nr = arg.rows ();
+  octave_idx_type nc = arg.columns ();
+
+  if (nr == 0 || nc == 0)
+    retval = Matrix ();
+  else
+    {
+      if (arg.issparse ())
+        {
+          if (arg.isreal ())
+            {
+              SparseMatrix r = arg.sparse_matrix_value ();
+
+              retval = octave::math::chol2inv (r);
+            }
+          else if (arg.iscomplex ())
+            {
+              SparseComplexMatrix r = arg.sparse_complex_matrix_value ();
+
+              retval = octave::math::chol2inv (r);
+            }
+          else
+            err_wrong_type_arg ("chol2inv", arg);
+        }
+      else if (arg.is_single_type ())
+        {
+          if (arg.isreal ())
+            {
+              FloatMatrix r = arg.float_matrix_value ();
+
+              retval = octave::math::chol2inv (r);
+            }
+          else if (arg.iscomplex ())
+            {
+              FloatComplexMatrix r = arg.float_complex_matrix_value ();
+
+              retval = octave::math::chol2inv (r);
+            }
+          else
+            err_wrong_type_arg ("chol2inv", arg);
+
+        }
+      else
+        {
+          if (arg.isreal ())
+            {
+              Matrix r = arg.matrix_value ();
+
+              retval = octave::math::chol2inv (r);
+            }
+          else if (arg.iscomplex ())
+            {
+              ComplexMatrix r = arg.complex_matrix_value ();
+
+              retval = octave::math::chol2inv (r);
+            }
+          else
+            err_wrong_type_arg ("chol2inv", arg);
+        }
+    }
+
+  return retval;
+}
+
+/*
+
+## Test for bug #36437
+%!function sparse_chol2inv (T, tol)
+%!  iT = inv (T);
+%!  ciT = chol2inv (chol (T));
+%!  assert (ciT, iT, tol);
+%!  assert (chol2inv (chol ( full (T))), ciT, tol*2);
+%!endfunction
+
+%!testif HAVE_CHOLMOD
+%! A = gallery ("poisson", 3);
+%! sparse_chol2inv (A, eps);
+
+%!testif HAVE_CHOLMOD
+%! n = 10;
+%! B = spdiags (ones (n, 1) * [1 2 1], [-1 0 1], n, n);
+%! sparse_chol2inv (B, eps*100);
+
+%!testif HAVE_CHOLMOD
+%! C = gallery("tridiag", 5);
+%! sparse_chol2inv (C, eps*10);
+
+%!testif HAVE_CHOLMOD
+%! D = gallery("wathen", 1, 1);
+%! sparse_chol2inv (D, eps*10^4);
+
+*/
+
+DEFUN (cholupdate, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {[@var{R1}, @var{info}] =} cholupdate (@var{R}, @var{u}, @var{op})
+Update or downdate a Cholesky@tie{}factorization.
+
+Given an upper triangular matrix @var{R} and a column vector @var{u},
+attempt to determine another upper triangular matrix @var{R1} such that
+
+@itemize @bullet
+@item
+@var{R1}'*@var{R1} = @var{R}'*@var{R} + @var{u}*@var{u}'
+if @var{op} is @qcode{"+"}
+
+@item
+@var{R1}'*@var{R1} = @var{R}'*@var{R} - @var{u}*@var{u}'
+if @var{op} is @qcode{"-"}
+@end itemize
+
+If @var{op} is @qcode{"-"}, @var{info} is set to
+
+@itemize
+@item 0 if the downdate was successful,
+
+@item 1 if @var{R}'*@var{R} - @var{u}*@var{u}' is not positive definite,
+
+@item 2 if @var{R} is singular.
+@end itemize
+
+If @var{info} is not present, an error message is printed in cases 1 and 2.
+@seealso{chol, cholinsert, choldelete, cholshift}
+@end deftypefn */)
+{
+  int nargin = args.length ();
+
+  if (nargin < 2 || nargin > 3)
+    print_usage ();
+
+  octave_value argr = args(0);
+  octave_value argu = args(1);
+
+  if (! argr.isnumeric () || ! argu.isnumeric ()
+      || (nargin > 2 && ! args(2).is_string ()))
+    print_usage ();
+
+  octave_value_list retval (nargout == 2 ? 2 : 1);
+
+  octave_idx_type n = argr.rows ();
+
+  std::string op = (nargin < 3) ? "+" : args(2).string_value ();
+
+  bool down = (op == "-");
+
+  if (! down && op != "+")
+    error (R"(cholupdate: OP must be "+" or "-")");
+
+  if (argr.columns () != n || argu.rows () != n || argu.columns () != 1)
+    error ("cholupdate: dimension mismatch between R and U");
+
+  int err = 0;
+  if (argr.is_single_type () || argu.is_single_type ())
+    {
+      if (argr.isreal () && argu.isreal ())
+        {
+          // real case
+          FloatMatrix R = argr.float_matrix_value ();
+          FloatColumnVector u = argu.float_column_vector_value ();
+
+          octave::math::chol<FloatMatrix> fact;
+          fact.set (R);
+
+          if (down)
+            err = fact.downdate (u);
+          else
+            fact.update (u);
+
+          retval = ovl (get_chol_r (fact));
+        }
+      else
+        {
+          // complex case
+          FloatComplexMatrix R = argr.float_complex_matrix_value ();
+          FloatComplexColumnVector u
+            = argu.float_complex_column_vector_value ();
+
+          octave::math::chol<FloatComplexMatrix> fact;
+          fact.set (R);
+
+          if (down)
+            err = fact.downdate (u);
+          else
+            fact.update (u);
+
+          retval = ovl (get_chol_r (fact));
+        }
+    }
+  else
+    {
+      if (argr.isreal () && argu.isreal ())
+        {
+          // real case
+          Matrix R = argr.matrix_value ();
+          ColumnVector u = argu.column_vector_value ();
+
+          octave::math::chol<Matrix> fact;
+          fact.set (R);
+
+          if (down)
+            err = fact.downdate (u);
+          else
+            fact.update (u);
+
+          retval = ovl (get_chol_r (fact));
+        }
+      else
+        {
+          // complex case
+          ComplexMatrix R = argr.complex_matrix_value ();
+          ComplexColumnVector u = argu.complex_column_vector_value ();
+
+          octave::math::chol<ComplexMatrix> fact;
+          fact.set (R);
+
+          if (down)
+            err = fact.downdate (u);
+          else
+            fact.update (u);
+
+          retval = ovl (get_chol_r (fact));
+        }
+    }
+
+  if (nargout > 1)
+    retval(1) = err;
+  else if (err == 1)
+    error ("cholupdate: downdate violates positiveness");
+  else if (err == 2)
+    error ("cholupdate: singular matrix");
+
+  return retval;
+}
+
+/*
+%!shared A, u, Ac, uc
+%! A = [  0.436997  -0.131721   0.124120  -0.061673 ;
+%!       -0.131721   0.738529   0.019851  -0.140295 ;
+%!        0.124120   0.019851   0.354879  -0.059472 ;
+%!       -0.061673  -0.140295  -0.059472   0.600939 ];
+%!
+%! u = [  0.98950 ;
+%!        0.39844 ;
+%!        0.63484 ;
+%!        0.13351 ];
+%! Ac = [  0.5585528 + 0.0000000i  -0.1662088 - 0.0315341i   0.0107873 + 0.0236411i  -0.0276775 - 0.0186073i ;
+%!        -0.1662088 + 0.0315341i   0.6760061 + 0.0000000i   0.0011452 - 0.0475528i   0.0145967 + 0.0247641i ;
+%!         0.0107873 - 0.0236411i   0.0011452 + 0.0475528i   0.6263149 - 0.0000000i  -0.1585837 - 0.0719763i ;
+%!        -0.0276775 + 0.0186073i   0.0145967 - 0.0247641i  -0.1585837 + 0.0719763i   0.6034234 - 0.0000000i ];
+%!
+%! uc = [ 0.54267 + 0.91519i ;
+%!        0.99647 + 0.43141i ;
+%!        0.83760 + 0.68977i ;
+%!        0.39160 + 0.90378i ];
+
+%!test
+%! R = chol (A);
+%! R1 = cholupdate (R, u);
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1'*R1 - R'*R - u*u', Inf) < 1e1*eps);
+%!
+%! R1 = cholupdate (R1, u, "-");
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1 - R, Inf) < 1e1*eps);
+
+%!test
+%! R = chol (Ac);
+%! R1 = cholupdate (R, uc);
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1'*R1 - R'*R - uc*uc', Inf) < 1e1*eps);
+%!
+%! R1 = cholupdate (R1, uc, "-");
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1 - R, Inf) < 1e1*eps);
+
+%!test
+%! R = chol (single (A));
+%! R1 = cholupdate (R, single (u));
+%! assert (norm (triu (R1)-R1, Inf), single (0));
+%! assert (norm (R1'*R1 - R'*R - single (u*u'), Inf) < 1e1*eps ("single"));
+%!
+%! R1 = cholupdate (R1, single (u), "-");
+%! assert (norm (triu (R1)-R1, Inf), single (0));
+%! assert (norm (R1 - R, Inf) < 2e1*eps ("single"));
+
+%!test
+%! R = chol (single (Ac));
+%! R1 = cholupdate (R, single (uc));
+%! assert (norm (triu (R1)-R1, Inf), single (0));
+%! assert (norm (R1'*R1 - R'*R - single (uc*uc'), Inf) < 1e1*eps ("single"));
+%!
+%! R1 = cholupdate (R1, single (uc), "-");
+%! assert (norm (triu (R1)-R1, Inf), single (0));
+%! assert (norm (R1 - R, Inf) < 2e1*eps ("single"));
+*/
+
+DEFUN (cholinsert, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{R1} =} cholinsert (@var{R}, @var{j}, @var{u})
+@deftypefnx {} {[@var{R1}, @var{info}] =} cholinsert (@var{R}, @var{j}, @var{u})
+Update a Cholesky factorization given a row or column to insert in the
+original factored matrix.
+
+Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian
+positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper
+triangular, return the Cholesky@tie{}factorization of
+@var{A1}, where @w{A1(p,p) = A}, @w{A1(:,j) = A1(j,:)' = u} and
+@w{p = [1:j-1,j+1:n+1]}.  @w{u(j)} should be positive.
+
+On return, @var{info} is set to
+
+@itemize
+@item 0 if the insertion was successful,
+
+@item 1 if @var{A1} is not positive definite,
+
+@item 2 if @var{R} is singular.
+@end itemize
+
+If @var{info} is not present, an error message is printed in cases 1 and 2.
+@seealso{chol, cholupdate, choldelete, cholshift}
+@end deftypefn */)
+{
+  if (args.length () != 3)
+    print_usage ();
+
+  octave_value argr = args(0);
+  octave_value argj = args(1);
+  octave_value argu = args(2);
+
+  if (! argr.isnumeric () || ! argu.isnumeric ()
+      || ! argj.is_real_scalar ())
+    print_usage ();
+
+  octave_idx_type n = argr.rows ();
+  octave_idx_type j = argj.scalar_value ();
+
+  if (argr.columns () != n || argu.rows () != n+1 || argu.columns () != 1)
+    error ("cholinsert: dimension mismatch between R and U");
+
+  if (j < 1 || j > n+1)
+    error ("cholinsert: index J out of range");
+
+  octave_value_list retval (nargout == 2 ? 2 : 1);
+
+  int err = 0;
+  if (argr.is_single_type () || argu.is_single_type ())
+    {
+      if (argr.isreal () && argu.isreal ())
+        {
+          // real case
+          FloatMatrix R = argr.float_matrix_value ();
+          FloatColumnVector u = argu.float_column_vector_value ();
+
+          octave::math::chol<FloatMatrix> fact;
+          fact.set (R);
+          err = fact.insert_sym (u, j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+      else
+        {
+          // complex case
+          FloatComplexMatrix R = argr.float_complex_matrix_value ();
+          FloatComplexColumnVector u
+            = argu.float_complex_column_vector_value ();
+
+          octave::math::chol<FloatComplexMatrix> fact;
+          fact.set (R);
+          err = fact.insert_sym (u, j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+    }
+  else
+    {
+      if (argr.isreal () && argu.isreal ())
+        {
+          // real case
+          Matrix R = argr.matrix_value ();
+          ColumnVector u = argu.column_vector_value ();
+
+          octave::math::chol<Matrix> fact;
+          fact.set (R);
+          err = fact.insert_sym (u, j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+      else
+        {
+          // complex case
+          ComplexMatrix R = argr.complex_matrix_value ();
+          ComplexColumnVector u = argu.complex_column_vector_value ();
+
+          octave::math::chol<ComplexMatrix> fact;
+          fact.set (R);
+          err = fact.insert_sym (u, j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+    }
+
+  if (nargout > 1)
+    retval(1) = err;
+  else if (err == 1)
+    error ("cholinsert: insertion violates positiveness");
+  else if (err == 2)
+    error ("cholinsert: singular matrix");
+  else if (err == 3)
+    error ("cholinsert: diagonal element must be real");
+
+  return retval;
+}
+
+/*
+%!test
+%! u2 = [  0.35080 ;
+%!         0.63930 ;
+%!         3.31057 ;
+%!        -0.13825 ;
+%!         0.45266 ];
+%!
+%! R = chol (A);
+%!
+%! j = 3;  p = [1:j-1, j+1:5];
+%! R1 = cholinsert (R, j, u2);
+%! A1 = R1'*R1;
+%!
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (A1(p,p) - A, Inf) < 1e1*eps);
+
+%!test
+%! u2 = [  0.35080  + 0.04298i;
+%!         0.63930  + 0.23778i;
+%!         3.31057  + 0.00000i;
+%!        -0.13825  + 0.19879i;
+%!         0.45266  + 0.50020i];
+%!
+%! R = chol (Ac);
+%!
+%! j = 3;  p = [1:j-1, j+1:5];
+%! R1 = cholinsert (R, j, u2);
+%! A1 = R1'*R1;
+%!
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (A1(p,p) - Ac, Inf) < 1e1*eps);
+
+%!test
+%! u2 = single ([  0.35080 ;
+%!                 0.63930 ;
+%!                 3.31057 ;
+%!                -0.13825 ;
+%!                 0.45266 ]);
+%!
+%! R = chol (single (A));
+%!
+%! j = 3;  p = [1:j-1, j+1:5];
+%! R1 = cholinsert (R, j, u2);
+%! A1 = R1'*R1;
+%!
+%! assert (norm (triu (R1)-R1, Inf), single (0));
+%! assert (norm (A1(p,p) - A, Inf) < 1e1*eps ("single"));
+
+%!test
+%! u2 = single ([  0.35080  + 0.04298i;
+%!                 0.63930  + 0.23778i;
+%!                 3.31057  + 0.00000i;
+%!                -0.13825  + 0.19879i;
+%!                 0.45266  + 0.50020i]);
+%!
+%! R = chol (single (Ac));
+%!
+%! j = 3;  p = [1:j-1, j+1:5];
+%! R1 = cholinsert (R, j, u2);
+%! A1 = R1'*R1;
+%!
+%! assert (norm (triu (R1)-R1, Inf), single (0));
+%! assert (norm (A1(p,p) - single (Ac), Inf) < 2e1*eps ("single"));
+
+%!test
+%! cu = chol (triu (A), "upper");
+%! cl = chol (tril (A), "lower");
+%! assert (cu, cl', eps);
+
+%!test
+%! cca   = chol (Ac);
+%!
+%! ccal  = chol (Ac, "lower");
+%! ccal2 = chol (tril (Ac), "lower");
+%!
+%! ccau  = chol (Ac, "upper");
+%! ccau2 = chol (triu (Ac), "upper");
+%!
+%! assert (cca'*cca,     Ac, eps);
+%! assert (ccau'*ccau,   Ac, eps);
+%! assert (ccau2'*ccau2, Ac, eps);
+%!
+%! assert (cca, ccal',  eps);
+%! assert (cca, ccau,   eps);
+%! assert (cca, ccal2', eps);
+%! assert (cca, ccau2,  eps);
+
+%!test
+%! cca   = chol (single (Ac));
+%!
+%! ccal  = chol (single (Ac), "lower");
+%! ccal2 = chol (tril (single (Ac)), "lower");
+%!
+%! ccau  = chol (single (Ac), "upper");
+%! ccau2 = chol (triu (single (Ac)), "upper");
+%!
+%! assert (cca'*cca,     single (Ac), eps ("single"));
+%! assert (ccau'*ccau,   single (Ac), eps ("single"));
+%! assert (ccau2'*ccau2, single (Ac), eps ("single"));
+%!
+%! assert (cca, ccal',  eps ("single"));
+%! assert (cca, ccau,   eps ("single"));
+%! assert (cca, ccal2', eps ("single"));
+%! assert (cca, ccau2,  eps ("single"));
+
+%!test
+%! a = [12,  2,  3,  4;
+%!       2, 14,  5,  3;
+%!       3,  5, 16,  6;
+%!       4,  3,  6, 16];
+%!
+%! b = [0,  1,  2,  3;
+%!     -1,  0,  1,  2;
+%!     -2, -1,  0,  1;
+%!     -3, -2, -1,  0];
+%!
+%! ca = a + i*b;
+%!
+%! cca   = chol (ca);
+%!
+%! ccal  = chol (ca, "lower");
+%! ccal2 = chol (tril (ca), "lower");
+%!
+%! ccau  = chol (ca, "upper");
+%! ccau2 = chol (triu (ca), "upper");
+%!
+%! assert (cca'*cca,     ca, 16*eps);
+%! assert (ccau'*ccau,   ca, 16*eps);
+%! assert (ccau2'*ccau2, ca, 16*eps);
+%!
+%! assert (cca, ccal',  16*eps);
+%! assert (cca, ccau,   16*eps);
+%! assert (cca, ccal2', 16*eps);
+%! assert (cca, ccau2,  16*eps);
+*/
+
+DEFUN (choldelete, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {@var{R1} =} choldelete (@var{R}, @var{j})
+Update a Cholesky factorization given a row or column to delete from the
+original factored matrix.
+
+Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian
+positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper
+triangular, return the Cholesky@tie{}factorization of @w{A(p,p)}, where
+@w{p = [1:j-1,j+1:n+1]}.
+@seealso{chol, cholupdate, cholinsert, cholshift}
+@end deftypefn */)
+{
+  if (args.length () != 2)
+    print_usage ();
+
+  octave_value argr = args(0);
+  octave_value argj = args(1);
+
+  if (! argr.isnumeric () || ! argj.is_real_scalar ())
+    print_usage ();
+
+  octave_idx_type n = argr.rows ();
+  octave_idx_type j = argj.scalar_value ();
+
+  if (argr.columns () != n)
+    err_square_matrix_required ("choldelete", "R");
+
+  if (j < 0 && j > n)
+    error ("choldelete: index J out of range");
+
+  octave_value_list retval;
+
+  if (argr.is_single_type ())
+    {
+      if (argr.isreal ())
+        {
+          // real case
+          FloatMatrix R = argr.float_matrix_value ();
+
+          octave::math::chol<FloatMatrix> fact;
+          fact.set (R);
+          fact.delete_sym (j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+      else
+        {
+          // complex case
+          FloatComplexMatrix R = argr.float_complex_matrix_value ();
+
+          octave::math::chol<FloatComplexMatrix> fact;
+          fact.set (R);
+          fact.delete_sym (j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+    }
+  else
+    {
+      if (argr.isreal ())
+        {
+          // real case
+          Matrix R = argr.matrix_value ();
+
+          octave::math::chol<Matrix> fact;
+          fact.set (R);
+          fact.delete_sym (j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+      else
+        {
+          // complex case
+          ComplexMatrix R = argr.complex_matrix_value ();
+
+          octave::math::chol<ComplexMatrix> fact;
+          fact.set (R);
+          fact.delete_sym (j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+    }
+
+  return retval;
+}
+
+/*
+%!test
+%! R = chol (A);
+%!
+%! j = 3;  p = [1:j-1,j+1:4];
+%! R1 = choldelete (R, j);
+%!
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps);
+
+%!test
+%! R = chol (Ac);
+%!
+%! j = 3;  p = [1:j-1,j+1:4];
+%! R1 = choldelete (R, j);
+%!
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps);
+
+%!test
+%! R = chol (single (A));
+%!
+%! j = 3;  p = [1:j-1,j+1:4];
+%! R1 = choldelete (R, j);
+%!
+%! assert (norm (triu (R1)-R1, Inf), single (0));
+%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single"));
+
+%!test
+%! R = chol (single (Ac));
+%!
+%! j = 3;  p = [1:j-1,j+1:4];
+%! R1 = choldelete (R,j);
+%!
+%! assert (norm (triu (R1)-R1, Inf), single (0));
+%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single"));
+*/
+
+DEFUN (cholshift, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {@var{R1} =} cholshift (@var{R}, @var{i}, @var{j})
+Update a Cholesky factorization given a range of columns to shift in the
+original factored matrix.
+
+Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian
+positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper
+triangular, return the Cholesky@tie{}factorization of
+@w{@var{A}(p,p)}, where @w{p} is the permutation @*
+@code{p = [1:i-1, shift(i:j, 1), j+1:n]} if @w{@var{i} < @var{j}} @*
+ or @*
+@code{p = [1:j-1, shift(j:i,-1), i+1:n]} if @w{@var{j} < @var{i}}.  @*
+
+@seealso{chol, cholupdate, cholinsert, choldelete}
+@end deftypefn */)
+{
+  if (args.length () != 3)
+    print_usage ();
+
+  octave_value argr = args(0);
+  octave_value argi = args(1);
+  octave_value argj = args(2);
+
+  if (! argr.isnumeric () || ! argi.is_real_scalar ()
+      || ! argj.is_real_scalar ())
+    print_usage ();
+
+  octave_idx_type n = argr.rows ();
+  octave_idx_type i = argi.scalar_value ();
+  octave_idx_type j = argj.scalar_value ();
+
+  if (argr.columns () != n)
+    err_square_matrix_required ("cholshift", "R");
+
+  if (j < 0 || j > n+1 || i < 0 || i > n+1)
+    error ("cholshift: index I or J is out of range");
+
+  octave_value_list retval;
+
+  if (argr.is_single_type () && argi.is_single_type ()
+      && argj.is_single_type ())
+    {
+      if (argr.isreal ())
+        {
+          // real case
+          FloatMatrix R = argr.float_matrix_value ();
+
+          octave::math::chol<FloatMatrix> fact;
+          fact.set (R);
+          fact.shift_sym (i-1, j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+      else
+        {
+          // complex case
+          FloatComplexMatrix R = argr.float_complex_matrix_value ();
+
+          octave::math::chol<FloatComplexMatrix> fact;
+          fact.set (R);
+          fact.shift_sym (i-1, j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+    }
+  else
+    {
+      if (argr.isreal ())
+        {
+          // real case
+          Matrix R = argr.matrix_value ();
+
+          octave::math::chol<Matrix> fact;
+          fact.set (R);
+          fact.shift_sym (i-1, j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+      else
+        {
+          // complex case
+          ComplexMatrix R = argr.complex_matrix_value ();
+
+          octave::math::chol<ComplexMatrix> fact;
+          fact.set (R);
+          fact.shift_sym (i-1, j-1);
+
+          retval = ovl (get_chol_r (fact));
+        }
+    }
+
+  return retval;
+}
+
+/*
+%!test
+%! R = chol (A);
+%!
+%! i = 1;  j = 3;  p = [1:i-1, shift(i:j,-1), j+1:4];
+%! R1 = cholshift (R, i, j);
+%!
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps);
+%!
+%! j = 1;  i = 3;  p = [1:j-1, shift(j:i,+1), i+1:4];
+%! R1 = cholshift (R, i, j);
+%!
+%! assert (norm (triu (R1) - R1, Inf), 0);
+%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps);
+
+%!test
+%! R = chol (Ac);
+%!
+%! i = 1;  j = 3;  p = [1:i-1, shift(i:j,-1), j+1:4];
+%! R1 = cholshift (R, i, j);
+%!
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps);
+%!
+%! j = 1;  i = 3;  p = [1:j-1, shift(j:i,+1), i+1:4];
+%! R1 = cholshift (R, i, j);
+%!
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps);
+
+%!test
+%! R = chol (single (A));
+%!
+%! i = 1;  j = 3;  p = [1:i-1, shift(i:j,-1), j+1:4];
+%! R1 = cholshift (R, i, j);
+%!
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single"));
+%!
+%! j = 1;  i = 3;  p = [1:j-1, shift(j:i,+1), i+1:4];
+%! R1 = cholshift (R, i, j);
+%!
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single"));
+
+%!test
+%! R = chol (single (Ac));
+%!
+%! i = 1;  j = 3;  p = [1:i-1, shift(i:j,-1), j+1:4];
+%! R1 = cholshift (R, i, j);
+%!
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single"));
+%!
+%! j = 1; i = 3; p = [1:j-1, shift(j:i,+1), i+1:4];
+%! R1 = cholshift (R, i, j);
+%!
+%! assert (norm (triu (R1)-R1, Inf), 0);
+%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single"));
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/colamd.cc	Wed Jan 29 06:30:40 2020 -0500
@@ -0,0 +1,774 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 1998-2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+// This is the octave interface to colamd, which bore the copyright given
+// in the help of the functions.
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <cstdlib>
+
+#include <string>
+
+#include "CSparse.h"
+#include "dNDArray.h"
+#include "dSparse.h"
+#include "oct-locbuf.h"
+#include "oct-sparse.h"
+
+#include "defun.h"
+#include "error.h"
+#include "errwarn.h"
+#include "ovl.h"
+#include "pager.h"
+
+// The symmetric column elimination tree code take from the Davis LDL code.
+// Copyright given elsewhere in this file.
+static void
+symetree (const octave_idx_type *ridx, const octave_idx_type *cidx,
+          octave_idx_type *Parent, octave_idx_type *P, octave_idx_type n)
+{
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, Flag, n);
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, Pinv, (P ? n : 0));
+  if (P)
+    // If P is present then compute Pinv, the inverse of P
+    for (octave_idx_type k = 0 ; k < n ; k++)
+      Pinv[P[k]] = k;
+
+  for (octave_idx_type k = 0 ; k < n ; k++)
+    {
+      // L(k,:) pattern: all nodes reachable in etree from nz in A(0:k-1,k)
+      Parent[k] = n ;                // parent of k is not yet known
+      Flag[k] = k ;                  // mark node k as visited
+      octave_idx_type kk = (P ? P[k] : k); // kth original, or permuted, column
+      octave_idx_type p2 = cidx[kk+1];
+      for (octave_idx_type p = cidx[kk] ; p < p2 ; p++)
+        {
+          // A (i,k) is nonzero (original or permuted A)
+          octave_idx_type i = (P ? Pinv[ridx[p]] : ridx[p]);
+          if (i < k)
+            {
+              // follow path from i to root of etree, stop at flagged node
+              for ( ; Flag[i] != k ; i = Parent[i])
+                {
+                  // find parent of i if not yet determined
+                  if (Parent[i] == n)
+                    Parent[i] = k;
+                  Flag[i] = k ;        // mark i as visited
+                }
+            }
+        }
+    }
+}
+
+// The elimination tree post-ordering code below is taken from SuperLU
+static inline octave_idx_type
+make_set (octave_idx_type i, octave_idx_type *pp)
+{
+  pp[i] = i;
+  return i;
+}
+
+static inline octave_idx_type
+link (octave_idx_type s, octave_idx_type t, octave_idx_type *pp)
+{
+  pp[s] = t;
+  return t;
+}
+
+static inline octave_idx_type
+find (octave_idx_type i, octave_idx_type *pp)
+{
+  octave_idx_type p = pp[i];
+  octave_idx_type gp = pp[p];
+
+  while (gp != p)
+    {
+      pp[i] = gp;
+      i = gp;
+      p = pp[i];
+      gp = pp[p];
+    }
+
+  return p;
+}
+
+static octave_idx_type
+etdfs (octave_idx_type v, octave_idx_type *first_kid,
+       octave_idx_type *next_kid, octave_idx_type *post,
+       octave_idx_type postnum)
+{
+  for (octave_idx_type w = first_kid[v]; w != -1; w = next_kid[w])
+    postnum = etdfs (w, first_kid, next_kid, post, postnum);
+
+  post[postnum++] = v;
+
+  return postnum;
+}
+
+static void
+tree_postorder (octave_idx_type n, octave_idx_type *parent,
+                octave_idx_type *post)
+{
+  // Allocate storage for working arrays and results
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, first_kid, n+1);
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, next_kid, n+1);
+
+  // Set up structure describing children
+  for (octave_idx_type v = 0; v <= n; first_kid[v++] = -1)
+    ; // do nothing
+
+  for (octave_idx_type v = n-1; v >= 0; v--)
+    {
+      octave_idx_type dad = parent[v];
+      next_kid[v] = first_kid[dad];
+      first_kid[dad] = v;
+    }
+
+  // Depth-first search from dummy root vertex #n
+  etdfs (n, first_kid, next_kid, post, 0);
+}
+
+static void
+coletree (const octave_idx_type *ridx, const octave_idx_type *colbeg,
+          octave_idx_type *colend, octave_idx_type *parent,
+          octave_idx_type nr, octave_idx_type nc)
+{
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, root, nc);
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, pp, nc);
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, firstcol, nr);
+
+  // Compute firstcol[row] = first nonzero column in row
+  for (octave_idx_type row = 0; row < nr; firstcol[row++] = nc)
+    ; // do nothing
+
+  for (octave_idx_type col = 0; col < nc; col++)
+    for (octave_idx_type p = colbeg[col]; p < colend[col]; p++)
+      {
+        octave_idx_type row = ridx[p];
+        if (firstcol[row] > col)
+          firstcol[row] = col;
+      }
+
+  // Compute etree by Liu's algorithm for symmetric matrices,
+  // except use (firstcol[r],c) in place of an edge (r,c) of A.
+  // Thus each row clique in A'*A is replaced by a star
+  // centered at its first vertex, which has the same fill.
+  for (octave_idx_type col = 0; col < nc; col++)
+    {
+      octave_idx_type cset = make_set (col, pp);
+      root[cset] = col;
+      parent[col] = nc;
+      for (octave_idx_type p = colbeg[col]; p < colend[col]; p++)
+        {
+          octave_idx_type row = firstcol[ridx[p]];
+          if (row >= col)
+            continue;
+          octave_idx_type rset = find (row, pp);
+          octave_idx_type rroot = root[rset];
+          if (rroot != col)
+            {
+              parent[rroot] = col;
+              cset = link (cset, rset, pp);
+              root[cset] = col;
+            }
+        }
+    }
+}
+
+DEFUN (colamd, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{p} =} colamd (@var{S})
+@deftypefnx {} {@var{p} =} colamd (@var{S}, @var{knobs})
+@deftypefnx {} {[@var{p}, @var{stats}] =} colamd (@var{S})
+@deftypefnx {} {[@var{p}, @var{stats}] =} colamd (@var{S}, @var{knobs})
+
+Compute the column approximate minimum degree permutation.
+
+@code{@var{p} = colamd (@var{S})} returns the column approximate minimum
+degree permutation vector for the sparse matrix @var{S}.  For a
+non-symmetric matrix @var{S}, @code{@var{S}(:,@var{p})} tends to have
+sparser LU@tie{}factors than @var{S}.  The Cholesky@tie{}factorization of
+@code{@var{S}(:,@var{p})' * @var{S}(:,@var{p})} also tends to be sparser
+than that of @code{@var{S}' * @var{S}}.
+
+@var{knobs} is an optional one- to three-element input vector.  If @var{S}
+is m-by-n, then rows with more than @code{max(16,@var{knobs}(1)*sqrt(n))}
+entries are ignored.  Columns with more than
+@code{max (16,@var{knobs}(2)*sqrt(min(m,n)))} entries are removed prior to
+ordering, and ordered last in the output permutation @var{p}.  Only
+completely dense rows or columns are removed if @code{@var{knobs}(1)} and
+@code{@var{knobs}(2)} are < 0, respectively.  If @code{@var{knobs}(3)} is
+nonzero, @var{stats} and @var{knobs} are printed.  The default is
+@code{@var{knobs} = [10 10 0]}.  Note that @var{knobs} differs from earlier
+versions of colamd.
+
+@var{stats} is an optional 20-element output vector that provides data
+about the ordering and the validity of the input matrix @var{S}.  Ordering
+statistics are in @code{@var{stats}(1:3)}.  @code{@var{stats}(1)} and
+@code{@var{stats}(2)} are the number of dense or empty rows and columns
+ignored by @sc{colamd} and @code{@var{stats}(3)} is the number of garbage
+collections performed on the internal data structure used by @sc{colamd}
+(roughly of size @code{2.2 * nnz(@var{S}) + 4 * @var{m} + 7 * @var{n}}
+integers).
+
+Octave built-in functions are intended to generate valid sparse matrices,
+with no duplicate entries, with ascending row indices of the nonzeros
+in each column, with a non-negative number of entries in each column (!)
+and so on.  If a matrix is invalid, then @sc{colamd} may or may not be able
+to continue.  If there are duplicate entries (a row index appears two or
+more times in the same column) or if the row indices in a column are out
+of order, then @sc{colamd} can correct these errors by ignoring the
+duplicate entries and sorting each column of its internal copy of the
+matrix @var{S} (the input matrix @var{S} is not repaired, however).  If a
+matrix is invalid in other ways then @sc{colamd} cannot continue, an error
+message is printed, and no output arguments (@var{p} or @var{stats}) are
+returned.
+@sc{colamd} is thus a simple way to check a sparse matrix to see if it's
+valid.
+
+@code{@var{stats}(4:7)} provide information if @sc{colamd} was able to
+continue.  The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if
+invalid.  @code{@var{stats}(5)} is the rightmost column index that is
+unsorted or contains duplicate entries, or zero if no such column exists.
+@code{@var{stats}(6)} is the last seen duplicate or out-of-order row
+index in the column index given by @code{@var{stats}(5)}, or zero if no
+such row index exists.  @code{@var{stats}(7)} is the number of duplicate
+or out-of-order row indices.  @code{@var{stats}(8:20)} is always zero in
+the current version of @sc{colamd} (reserved for future use).
+
+The ordering is followed by a column elimination tree post-ordering.
+
+The authors of the code itself are @nospell{Stefan I. Larimore} and
+@nospell{Timothy A. Davis}.  The algorithm was developed in collaboration with
+@nospell{John Gilbert}, Xerox PARC, and @nospell{Esmond Ng}, Oak Ridge National
+Laboratory.  (see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html})
+@seealso{colperm, symamd, ccolamd}
+@end deftypefn */)
+{
+#if defined (HAVE_COLAMD)
+
+  int nargin = args.length ();
+
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+
+  octave_value_list retval (nargout == 2 ? 2 : 1);
+  int spumoni = 0;
+
+  // Get knobs
+  static_assert (COLAMD_KNOBS <= 40,
+                 "colamd: # of COLAMD_KNOBS exceeded.  Please report this to bugs.octave.org");
+  double knob_storage[COLAMD_KNOBS];
+  double *knobs = &knob_storage[0];
+  COLAMD_NAME (_set_defaults) (knobs);
+
+  // Check for user-passed knobs
+  if (nargin == 2)
+    {
+      NDArray User_knobs = args(1).array_value ();
+      int nel_User_knobs = User_knobs.numel ();
+
+      if (nel_User_knobs > 0)
+        knobs[COLAMD_DENSE_ROW] = User_knobs(0);
+      if (nel_User_knobs > 1)
+        knobs[COLAMD_DENSE_COL] = User_knobs(1);
+      if (nel_User_knobs > 2)
+        spumoni = static_cast<int> (User_knobs(2));
+
+      // print knob settings if spumoni is set
+      if (spumoni)
+        {
+
+          octave_stdout << "\ncolamd version " << COLAMD_MAIN_VERSION
+                        << '.' <<  COLAMD_SUB_VERSION
+                        << ", " << COLAMD_DATE << ":\n";
+
+          if (knobs[COLAMD_DENSE_ROW] >= 0)
+            octave_stdout << "knobs(1): " << User_knobs (0)
+                          << ", rows with > max (16,"
+                          << knobs[COLAMD_DENSE_ROW] << "*sqrt (size(A,2)))"
+                          << " entries removed\n";
+          else
+            octave_stdout << "knobs(1): " << User_knobs (0)
+                          << ", only completely dense rows removed\n";
+
+          if (knobs[COLAMD_DENSE_COL] >= 0)
+            octave_stdout << "knobs(2): " << User_knobs (1)
+                          << ", cols with > max (16,"
+                          << knobs[COLAMD_DENSE_COL] << "*sqrt (size(A)))"
+                          << " entries removed\n";
+          else
+            octave_stdout << "knobs(2): " << User_knobs (1)
+                          << ", only completely dense columns removed\n";
+
+          octave_stdout << "knobs(3): " << User_knobs (2)
+                        << ", statistics and knobs printed\n";
+
+        }
+    }
+
+  octave_idx_type n_row, n_col, nnz;
+  octave_idx_type *ridx, *cidx;
+  SparseComplexMatrix scm;
+  SparseMatrix sm;
+
+  if (args(0).issparse ())
+    {
+      if (args(0).iscomplex ())
+        {
+          scm = args(0).sparse_complex_matrix_value ();
+          n_row = scm.rows ();
+          n_col = scm.cols ();
+          nnz = scm.nnz ();
+          ridx = scm.xridx ();
+          cidx = scm.xcidx ();
+        }
+      else
+        {
+          sm = args(0).sparse_matrix_value ();
+
+          n_row = sm.rows ();
+          n_col = sm.cols ();
+          nnz = sm.nnz ();
+          ridx = sm.xridx ();
+          cidx = sm.xcidx ();
+        }
+    }
+  else
+    {
+      if (args(0).iscomplex ())
+        sm = SparseMatrix (real (args(0).complex_matrix_value ()));
+      else
+        sm = SparseMatrix (args(0).matrix_value ());
+
+      n_row = sm.rows ();
+      n_col = sm.cols ();
+      nnz = sm.nnz ();
+      ridx = sm.xridx ();
+      cidx = sm.xcidx ();
+    }
+
+  // Allocate workspace for colamd
+  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, p, n_col+1);
+  for (octave_idx_type i = 0; i < n_col+1; i++)
+    p[i] = cidx[i];
+
+  octave_idx_type Alen = COLAMD_NAME (_recommended) (nnz, n_row, n_col);
+  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, A, Alen);
+  for (octave_idx_type i = 0; i < nnz; i++)
+    A[i] = ridx[i];
+
+  // Order the columns (destroys A)
+  static_assert (COLAMD_STATS <= 40,
+                 "colamd: # of COLAMD_STATS exceeded.  Please report this to bugs.octave.org");
+  octave::suitesparse_integer stats_storage[COLAMD_STATS];
+  octave::suitesparse_integer *stats = &stats_storage[0];
+  if (! COLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats))
+    {
+      COLAMD_NAME (_report)(stats);
+
+      error ("colamd: internal error!");
+    }
+
+  // column elimination tree post-ordering (reuse variables)
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, colbeg, n_col + 1);
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, colend, n_col + 1);
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1);
+
+  for (octave_idx_type i = 0; i < n_col; i++)
+    {
+      colbeg[i] = cidx[p[i]];
+      colend[i] = cidx[p[i]+1];
+    }
+
+  coletree (ridx, colbeg, colend, etree, n_row, n_col);
+
+  // Calculate the tree post-ordering
+  tree_postorder (n_col, etree, colbeg);
+
+  // return the permutation vector
+  NDArray out_perm (dim_vector (1, n_col));
+  for (octave_idx_type i = 0; i < n_col; i++)
+    out_perm(i) = p[colbeg[i]] + 1;
+
+  retval(0) = out_perm;
+
+  // print stats if spumoni > 0
+  if (spumoni > 0)
+    COLAMD_NAME (_report)(stats);
+
+  // Return the stats vector
+  if (nargout == 2)
+    {
+      NDArray out_stats (dim_vector (1, COLAMD_STATS));
+      for (octave_idx_type i = 0 ; i < COLAMD_STATS ; i++)
+        out_stats(i) = stats[i];
+      retval(1) = out_stats;
+
+      // fix stats (5) and (6), for 1-based information on
+      // jumbled matrix.  note that this correction doesn't
+      // occur if symamd returns FALSE
+      out_stats(COLAMD_INFO1)++;
+      out_stats(COLAMD_INFO2)++;
+    }
+
+  return retval;
+
+#else
+
+  octave_unused_parameter (args);
+  octave_unused_parameter (nargout);
+
+  err_disabled_feature ("colamd", "COLAMD");
+
+#endif
+}
+
+DEFUN (symamd, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{p} =} symamd (@var{S})
+@deftypefnx {} {@var{p} =} symamd (@var{S}, @var{knobs})
+@deftypefnx {} {[@var{p}, @var{stats}] =} symamd (@var{S})
+@deftypefnx {} {[@var{p}, @var{stats}] =} symamd (@var{S}, @var{knobs})
+
+For a symmetric positive definite matrix @var{S}, returns the permutation
+vector p such that @code{@var{S}(@var{p}, @var{p})} tends to have a
+sparser Cholesky@tie{}factor than @var{S}.
+
+Sometimes @code{symamd} works well for symmetric indefinite matrices too.
+The matrix @var{S} is assumed to be symmetric; only the strictly lower
+triangular part is referenced.  @var{S} must be square.
+
+@var{knobs} is an optional one- to two-element input vector.  If @var{S} is
+n-by-n, then rows and columns with more than
+@code{max (16,@var{knobs}(1)*sqrt(n))} entries are removed prior to
+ordering, and ordered last in the output permutation @var{p}.  No
+rows/columns are removed if @code{@var{knobs}(1) < 0}.  If
+@code{@var{knobs}(2)} is nonzero, @var{stats} and @var{knobs} are
+printed.  The default is @code{@var{knobs} = [10 0]}.  Note that
+@var{knobs} differs from earlier versions of @code{symamd}.
+
+@var{stats} is an optional 20-element output vector that provides data
+about the ordering and the validity of the input matrix @var{S}.  Ordering
+statistics are in @code{@var{stats}(1:3)}.
+@code{@var{stats}(1) = @var{stats}(2)} is the number of dense or empty rows
+and columns ignored by SYMAMD and @code{@var{stats}(3)} is the number of
+garbage collections performed on the internal data structure used by SYMAMD
+(roughly of size @code{8.4 * nnz (tril (@var{S}, -1)) + 9 * @var{n}}
+integers).
+
+Octave built-in functions are intended to generate valid sparse matrices,
+with no duplicate entries, with ascending row indices of the nonzeros
+in each column, with a non-negative number of entries in each column (!)
+and so on.  If a matrix is invalid, then SYMAMD may or may not be able
+to continue.  If there are duplicate entries (a row index appears two or
+more times in the same column) or if the row indices in a column are out
+of order, then SYMAMD can correct these errors by ignoring the duplicate
+entries and sorting each column of its internal copy of the matrix S (the
+input matrix S is not repaired, however).  If a matrix is invalid in
+other ways then SYMAMD cannot continue, an error message is printed, and
+no output arguments (@var{p} or @var{stats}) are returned.  SYMAMD is
+thus a simple way to check a sparse matrix to see if it's valid.
+
+@code{@var{stats}(4:7)} provide information if SYMAMD was able to
+continue.  The matrix is OK if @code{@var{stats} (4)} is zero, or 1
+if invalid.  @code{@var{stats}(5)} is the rightmost column index that
+is unsorted or contains duplicate entries, or zero if no such column
+exists.  @code{@var{stats}(6)} is the last seen duplicate or out-of-order
+row index in the column index given by @code{@var{stats}(5)}, or zero
+if no such row index exists.  @code{@var{stats}(7)} is the number of
+duplicate or out-of-order row indices.  @code{@var{stats}(8:20)} is
+always zero in the current version of SYMAMD (reserved for future use).
+
+The ordering is followed by a column elimination tree post-ordering.
+
+The authors of the code itself are @nospell{Stefan I. Larimore} and
+@nospell{Timothy A. Davis}.  The algorithm was developed in collaboration with
+@nospell{John Gilbert}, Xerox PARC, and @nospell{Esmond Ng}, Oak Ridge National
+Laboratory.  (see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html})
+@seealso{colperm, colamd}
+@end deftypefn */)
+{
+#if defined (HAVE_COLAMD)
+
+  int nargin = args.length ();
+
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+
+  octave_value_list retval (nargin == 2 ? 2 : 1);
+  int spumoni = 0;
+
+  // Get knobs
+  static_assert (COLAMD_KNOBS <= 40,
+                 "symamd: # of COLAMD_KNOBS exceeded.  Please report this to bugs.octave.org");
+  double knob_storage[COLAMD_KNOBS];
+  double *knobs = &knob_storage[0];
+  COLAMD_NAME (_set_defaults) (knobs);
+
+  // Check for user-passed knobs
+  if (nargin == 2)
+    {
+      NDArray User_knobs = args(1).array_value ();
+      int nel_User_knobs = User_knobs.numel ();
+
+      if (nel_User_knobs > 0)
+        knobs[COLAMD_DENSE_ROW] = User_knobs(COLAMD_DENSE_ROW);
+      if (nel_User_knobs > 1)
+        spumoni = static_cast<int> (User_knobs (1));
+    }
+
+  // print knob settings if spumoni is set
+  if (spumoni > 0)
+    octave_stdout << "symamd: dense row/col fraction: "
+                  << knobs[COLAMD_DENSE_ROW] << std::endl;
+
+  octave_idx_type n_row, n_col;
+  octave_idx_type *ridx, *cidx;
+  SparseMatrix sm;
+  SparseComplexMatrix scm;
+
+  if (args(0).issparse ())
+    {
+      if (args(0).iscomplex ())
+        {
+          scm = args(0).sparse_complex_matrix_value ();
+          n_row = scm.rows ();
+          n_col = scm.cols ();
+          ridx = scm.xridx ();
+          cidx = scm.xcidx ();
+        }
+      else
+        {
+          sm = args(0).sparse_matrix_value ();
+          n_row = sm.rows ();
+          n_col = sm.cols ();
+          ridx = sm.xridx ();
+          cidx = sm.xcidx ();
+        }
+    }
+  else
+    {
+      if (args(0).iscomplex ())
+        sm = SparseMatrix (real (args(0).complex_matrix_value ()));
+      else
+        sm = SparseMatrix (args(0).matrix_value ());
+
+      n_row = sm.rows ();
+      n_col = sm.cols ();
+      ridx = sm.xridx ();
+      cidx = sm.xcidx ();
+    }
+
+  if (n_row != n_col)
+    err_square_matrix_required ("symamd", "S");
+
+  // Allocate workspace for symamd
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, perm, n_col+1);
+  static_assert (COLAMD_STATS <= 40,
+                 "symamd: # of COLAMD_STATS exceeded.  Please report this to bugs.octave.org");
+  octave::suitesparse_integer stats_storage[COLAMD_STATS];
+  octave::suitesparse_integer *stats = &stats_storage[0];
+  if (! SYMAMD_NAME () (n_col, octave::to_suitesparse_intptr (ridx),
+                        octave::to_suitesparse_intptr (cidx),
+                        octave::to_suitesparse_intptr (perm),
+                        knobs, stats, &calloc, &free))
+    {
+      SYMAMD_NAME (_report)(stats);
+
+      error ("symamd: internal error!");
+    }
+
+  // column elimination tree post-ordering
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1);
+  symetree (ridx, cidx, etree, perm, n_col);
+
+  // Calculate the tree post-ordering
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, post, n_col + 1);
+  tree_postorder (n_col, etree, post);
+
+  // return the permutation vector
+  NDArray out_perm (dim_vector (1, n_col));
+  for (octave_idx_type i = 0; i < n_col; i++)
+    out_perm(i) = perm[post[i]] + 1;
+
+  retval(0) = out_perm;
+
+  // print stats if spumoni > 0
+  if (spumoni > 0)
+    SYMAMD_NAME (_report)(stats);
+
+  // Return the stats vector
+  if (nargout == 2)
+    {
+      NDArray out_stats (dim_vector (1, COLAMD_STATS));
+      for (octave_idx_type i = 0 ; i < COLAMD_STATS ; i++)
+        out_stats(i) = stats[i];
+      retval(1) = out_stats;
+
+      // fix stats (5) and (6), for 1-based information on
+      // jumbled matrix.  note that this correction doesn't
+      // occur if symamd returns FALSE
+      out_stats(COLAMD_INFO1)++;
+      out_stats(COLAMD_INFO2)++;
+    }
+
+  return retval;
+
+#else
+
+  octave_unused_parameter (args);
+  octave_unused_parameter (nargout);
+
+  err_disabled_feature ("symamd", "COLAMD");
+
+#endif
+}
+
+DEFUN (etree, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{p} =} etree (@var{S})
+@deftypefnx {} {@var{p} =} etree (@var{S}, @var{typ})
+@deftypefnx {} {[@var{p}, @var{q}] =} etree (@var{S}, @var{typ})
+
+Return the elimination tree for the matrix @var{S}.
+
+By default @var{S} is assumed to be symmetric and the symmetric elimination
+tree is returned.  The argument @var{typ} controls whether a symmetric or
+column elimination tree is returned.  Valid values of @var{typ} are
+@qcode{"sym"} or @qcode{"col"}, for symmetric or column elimination tree
+respectively.
+
+Called with a second argument, @code{etree} also returns the postorder
+permutations on the tree.
+@end deftypefn */)
+{
+  int nargin = args.length ();
+
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+
+  octave_value_list retval (nargout == 2 ? 2 : 1);
+
+  octave_idx_type n_row = 0;
+  octave_idx_type n_col = 0;
+  octave_idx_type *ridx = nullptr;
+  octave_idx_type *cidx = nullptr;
+
+  if (! args(0).issparse ())
+    error ("etree: S must be a sparse matrix");
+
+  if (args(0).iscomplex ())
+    {
+      SparseComplexMatrix scm = args(0).sparse_complex_matrix_value ();
+
+      n_row = scm.rows ();
+      n_col = scm.cols ();
+      ridx = scm.xridx ();
+      cidx = scm.xcidx ();
+    }
+  else
+    {
+      SparseMatrix sm = args(0).sparse_matrix_value ();
+
+      n_row = sm.rows ();
+      n_col = sm.cols ();
+      ridx = sm.xridx ();
+      cidx = sm.xcidx ();
+    }
+
+  bool is_sym = true;
+
+  if (nargin == 2)
+    {
+      std::string str = args(1).xstring_value ("etree: TYP must be a string");
+      if (str.find ('C') == 0 || str.find ('c') == 0)
+        is_sym = false;
+    }
+
+  // column elimination tree post-ordering (reuse variables)
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1);
+
+  if (is_sym)
+    {
+      if (n_row != n_col)
+        error ("etree: S is marked as symmetric, but is not square");
+
+      symetree (ridx, cidx, etree, nullptr, n_col);
+    }
+  else
+    {
+      OCTAVE_LOCAL_BUFFER (octave_idx_type, colbeg, n_col);
+      OCTAVE_LOCAL_BUFFER (octave_idx_type, colend, n_col);
+
+      for (octave_idx_type i = 0; i < n_col; i++)
+        {
+          colbeg[i] = cidx[i];
+          colend[i] = cidx[i+1];
+        }
+
+      coletree (ridx, colbeg, colend, etree, n_row, n_col);
+    }
+
+  NDArray tree (dim_vector (1, n_col));
+  for (octave_idx_type i = 0; i < n_col; i++)
+    // We flag a root with n_col while Matlab does it with zero
+    // Convert for matlab compatible output
+    if (etree[i] == n_col)
+      tree(i) = 0;
+    else
+      tree(i) = etree[i] + 1;
+
+  retval(0) = tree;
+
+  if (nargout == 2)
+    {
+      // Calculate the tree post-ordering
+      OCTAVE_LOCAL_BUFFER (octave_idx_type, post, n_col + 1);
+      tree_postorder (n_col, etree, post);
+
+      NDArray postorder (dim_vector (1, n_col));
+      for (octave_idx_type i = 0; i < n_col; i++)
+        postorder(i) = post[i] + 1;
+
+      retval(1) = postorder;
+    }
+
+  return retval;
+}
+
+/*
+%!assert (etree (speye (2)), [0, 0]);
+%!assert (etree (gallery ("poisson", 16)), [2:256, 0]);
+
+%!error etree ()
+%!error etree (1, 2, 3)
+%!error <S must be a sparse matrix> etree ([1, 2; 3, 4])
+%!error <TYP must be a string> etree (speye (2), 3)
+%!error <is not square> etree (sprand (2, 4, .25))
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/dmperm.cc	Wed Jan 29 06:30:40 2020 -0500
@@ -0,0 +1,215 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 1998-2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include "CSparse.h"
+#include "dRowVector.h"
+#include "dSparse.h"
+#include "oct-sparse.h"
+
+#include "defun.h"
+#include "errwarn.h"
+#include "ov.h"
+#include "ovl.h"
+#include "utils.h"
+
+#if defined (OCTAVE_ENABLE_64)
+#  define CXSPARSE_NAME(name) cs_dl ## name
+#else
+#  define CXSPARSE_NAME(name) cs_di ## name
+#endif
+
+#if defined (HAVE_CXSPARSE)
+
+static RowVector
+put_int (octave::suitesparse_integer *p, octave_idx_type n)
+{
+  RowVector ret (n);
+  for (octave_idx_type i = 0; i < n; i++)
+    ret.xelem (i) = p[i] + 1;
+  return ret;
+}
+
+static octave_value_list
+dmperm_internal (bool rank, const octave_value arg, int nargout)
+{
+  octave_value_list retval;
+  octave_idx_type nr = arg.rows ();
+  octave_idx_type nc = arg.columns ();
+  SparseMatrix m;
+  SparseComplexMatrix cm;
+  CXSPARSE_NAME () csm;
+  csm.m = nr;
+  csm.n = nc;
+  csm.x = nullptr;
+  csm.nz = -1;
+
+  if (arg.isreal ())
+    {
+      m = arg.sparse_matrix_value ();
+      csm.nzmax = m.nnz ();
+      csm.p = octave::to_suitesparse_intptr (m.xcidx ());
+      csm.i = octave::to_suitesparse_intptr (m.xridx ());
+    }
+  else
+    {
+      cm = arg.sparse_complex_matrix_value ();
+      csm.nzmax = cm.nnz ();
+      csm.p = octave::to_suitesparse_intptr (cm.xcidx ());
+      csm.i = octave::to_suitesparse_intptr (cm.xridx ());
+    }
+
+  if (nargout <= 1 || rank)
+    {
+      octave::suitesparse_integer *jmatch = CXSPARSE_NAME (_maxtrans) (&csm, 0);
+      if (rank)
+        {
+          octave_idx_type r = 0;
+          for (octave_idx_type i = 0; i < nc; i++)
+            if (jmatch[nr+i] >= 0)
+              r++;
+          retval(0) = static_cast<double> (r);
+        }
+      else
+        retval(0) = put_int (jmatch + nr, nc);
+      CXSPARSE_NAME (_free) (jmatch);
+    }
+  else
+    {
+      CXSPARSE_NAME (d) *dm = CXSPARSE_NAME(_dmperm) (&csm, 0);
+
+      //retval(5) = put_int (dm->rr, 5);
+      //retval(4) = put_int (dm->cc, 5);
+      retval = ovl (put_int (dm->p, nr), put_int (dm->q, nc),
+                    put_int (dm->r, dm->nb+1), put_int (dm->s, dm->nb+1));
+
+      CXSPARSE_NAME (_dfree) (dm);
+    }
+
+  return retval;
+}
+
+#endif
+
+DEFUN (dmperm, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{p} =} dmperm (@var{S})
+@deftypefnx {} {[@var{p}, @var{q}, @var{r}, @var{S}] =} dmperm (@var{S})
+
+@cindex @nospell{Dulmage-Mendelsohn} decomposition
+Perform a @nospell{Dulmage-Mendelsohn} permutation of the sparse matrix
+@var{S}.
+
+With a single output argument @code{dmperm} performs the row permutations
+@var{p} such that @code{@var{S}(@var{p},:)} has no zero elements on the
+diagonal.
+
+Called with two or more output arguments, returns the row and column
+permutations, such that @code{@var{S}(@var{p}, @var{q})} is in block
+triangular form.  The values of @var{r} and @var{S} define the boundaries
+of the blocks.  If @var{S} is square then @code{@var{r} == @var{S}}.
+
+The method used is described in: @nospell{A. Pothen & C.-J. Fan.}
+@cite{Computing the Block Triangular Form of a Sparse Matrix}.
+@nospell{ACM} Trans.@: Math.@: Software, 16(4):303--324, 1990.
+@seealso{colamd, ccolamd}
+@end deftypefn */)
+{
+#if defined (HAVE_CXSPARSE)
+
+  if (args.length () != 1)
+    print_usage ();
+
+  return dmperm_internal (false, args(0), nargout);
+
+#else
+
+  octave_unused_parameter (args);
+  octave_unused_parameter (nargout);
+
+  err_disabled_feature ("dmperm", "CXSparse");
+
+#endif
+}
+
+/*
+%!testif HAVE_CXSPARSE
+%! n = 20;
+%! a = speye (n,n);
+%! a = a(randperm (n),:);
+%! assert (a(dmperm (a),:), speye (n));
+
+%!testif HAVE_CXSPARSE
+%! n = 20;
+%! d = 0.2;
+%! a = tril (sprandn (n,n,d), -1) + speye (n,n);
+%! a = a(randperm (n), randperm (n));
+%! [p,q,r,s] = dmperm (a);
+%! assert (tril (a(p,q), -1), sparse (n, n));
+*/
+
+DEFUN (sprank, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {@var{p} =} sprank (@var{S})
+@cindex structural rank
+
+Calculate the structural rank of the sparse matrix @var{S}.
+
+Note that only the structure of the matrix is used in this calculation based
+on a @nospell{Dulmage-Mendelsohn} permutation to block triangular form.  As
+such the numerical rank of the matrix @var{S} is bounded by
+@code{sprank (@var{S}) >= rank (@var{S})}.  Ignoring floating point errors
+@code{sprank (@var{S}) == rank (@var{S})}.
+@seealso{dmperm}
+@end deftypefn */)
+{
+#if defined (HAVE_CXSPARSE)
+
+  if (args.length () != 1)
+    print_usage ();
+
+  return dmperm_internal (true, args(0), nargout);
+
+#else
+
+  octave_unused_parameter (args);
+  octave_unused_parameter (nargout);
+
+  err_disabled_feature ("sprank", "CXSparse");
+
+#endif
+}
+
+/*
+%!testif HAVE_CXSPARSE
+%! assert (sprank (speye (20)), 20);
+%!testif HAVE_CXSPARSE
+%! assert (sprank ([1,0,2,0;2,0,4,0]), 2);
+
+%!error sprank (1,2)
+*/
--- a/libinterp/corefcn/module.mk	Sat Jan 25 15:26:07 2020 +0100
+++ b/libinterp/corefcn/module.mk	Wed Jan 29 06:30:40 2020 -0500
@@ -113,6 +113,7 @@
   %reldir%/__betainc__.cc \
   %reldir%/__contourc__.cc \
   %reldir%/__dsearchn__.cc \
+  %reldir%/__eigs__.cc \
   %reldir%/__expint__.cc \
   %reldir%/__ftp__.cc \
   %reldir%/__gammainc__.cc \
@@ -122,16 +123,20 @@
   %reldir%/__magick_read__.cc \
   %reldir%/__pchip_deriv__.cc \
   %reldir%/__qp__.cc \
+  %reldir%/amd.cc \
   %reldir%/balance.cc \
   %reldir%/besselj.cc \
   %reldir%/bitfcns.cc \
   %reldir%/bsxfun.cc \
   %reldir%/c-file-ptr-stream.cc \
   %reldir%/call-stack.cc \
+  %reldir%/ccolamd.cc \
   %reldir%/cdisplay.c \
   %reldir%/cellfun.cc \
+  %reldir%/chol.cc \
+  %reldir%/coct-hdf5-types.c \
+  %reldir%/colamd.cc \
   %reldir%/colloc.cc \
-  %reldir%/coct-hdf5-types.c \
   %reldir%/conv2.cc \
   %reldir%/daspk.cc \
   %reldir%/dasrt.cc \
@@ -144,6 +149,7 @@
   %reldir%/dirfns.cc \
   %reldir%/display.cc \
   %reldir%/dlmread.cc \
+  %reldir%/dmperm.cc \
   %reldir%/dot.cc \
   %reldir%/dynamic-ld.cc \
   %reldir%/eig.cc \
@@ -178,9 +184,9 @@
   %reldir%/hex2num.cc \
   %reldir%/hook-fcn.cc \
   %reldir%/input.cc \
-  %reldir%/inv.cc \
   %reldir%/interpreter-private.cc \
   %reldir%/interpreter.cc \
+  %reldir%/inv.cc \
   %reldir%/kron.cc \
   %reldir%/load-path.cc \
   %reldir%/load-save.cc \
@@ -190,8 +196,8 @@
   %reldir%/ls-mat-ascii.cc \
   %reldir%/ls-mat4.cc \
   %reldir%/ls-mat5.cc \
+  %reldir%/ls-oct-binary.cc \
   %reldir%/ls-oct-text.cc \
-  %reldir%/ls-oct-binary.cc \
   %reldir%/ls-utils.cc \
   %reldir%/lsode.cc \
   %reldir%/lu.cc \
@@ -221,6 +227,7 @@
   %reldir%/pr-output.cc \
   %reldir%/procstream.cc \
   %reldir%/psi.cc \
+  %reldir%/qr.cc \
   %reldir%/quad.cc \
   %reldir%/quadcc.cc \
   %reldir%/qz.cc \
@@ -242,21 +249,23 @@
   %reldir%/sub2ind.cc \
   %reldir%/svd.cc \
   %reldir%/sylvester.cc \
+  %reldir%/symbfact.cc \
   %reldir%/syminfo.cc \
+  %reldir%/symrcm.cc \
   %reldir%/symrec.cc \
   %reldir%/symscope.cc \
   %reldir%/symtab.cc \
   %reldir%/syscalls.cc \
   %reldir%/sysdep.cc \
-  %reldir%/time.cc \
   %reldir%/text-engine.cc \
   %reldir%/text-renderer.cc \
+  %reldir%/time.cc \
   %reldir%/toplev.cc \
   %reldir%/tril.cc \
   %reldir%/tsearch.cc \
   %reldir%/typecast.cc \
+  %reldir%/url-handle-manager.cc \
   %reldir%/urlwrite.cc \
-  %reldir%/url-handle-manager.cc \
   %reldir%/utils.cc \
   %reldir%/variables.cc \
   %reldir%/xdiv.cc \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/qr.cc	Wed Jan 29 06:30:40 2020 -0500
@@ -0,0 +1,1751 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 1996-2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <string>
+
+#include "MArray.h"
+#include "Matrix.h"
+#include "qr.h"
+#include "qrp.h"
+#include "sparse-qr.h"
+
+#include "defun.h"
+#include "error.h"
+#include "errwarn.h"
+#include "ov.h"
+#include "ovl.h"
+
+/*
+## Restore all rand* "state" values
+%!function restore_rand_states (state)
+%!  rand ("state", state.rand);
+%!  randn ("state", state.randn);
+%!endfunction
+
+%!shared old_state, restore_state
+%! ## Save and restore the states of both random number generators that are
+%! ## tested by the unit tests in this file.
+%! old_state.rand = rand ("state");
+%! old_state.randn = randn ("state");
+%! restore_state = onCleanup (@() restore_rand_states (old_state));
+*/
+
+template <typename MT>
+static octave_value
+get_qr_r (const octave::math::qr<MT>& fact)
+{
+  MT R = fact.R ();
+  if (R.issquare () && fact.regular ())
+    return octave_value (R, MatrixType (MatrixType::Upper));
+  else
+    return R;
+}
+
+template <typename T>
+static typename octave::math::qr<T>::type
+qr_type (int nargout, bool economy)
+{
+  if (nargout == 0 || nargout == 1)
+    return octave::math::qr<T>::raw;
+  else if (economy)
+    return octave::math::qr<T>::economy;
+  else
+    return octave::math::qr<T>::std;
+}
+
+// [Q, R] = qr (X):       form Q unitary and R upper triangular such
+//                        that Q * R = X
+//
+// [Q, R] = qr (X, 0):    form the economy decomposition such that if X is
+//                        m by n then only the first n columns of Q are
+//                        computed.
+//
+// [Q, R, P] = qr (X):    form QRP factorization of X where
+//                        P is a permutation matrix such that
+//                        A * P = Q * R
+//
+// [Q, R, P] = qr (X, 0): form the economy decomposition with
+//                        permutation vector P such that Q * R = X(:, P)
+//
+// qr (X) alone returns the output of the LAPACK routine dgeqrf, such
+// that R = triu (qr (X))
+
+DEFUN (qr, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {[@var{Q}, @var{R}] =} qr (@var{A})
+@deftypefnx {} {[@var{Q}, @var{R}, @var{P}] =} qr (@var{A})  # non-sparse A
+@deftypefnx {} {@var{X} =} qr (@var{A})  # non-sparse A
+@deftypefnx {} {@var{R} =} qr (@var{A})  # sparse A
+@deftypefnx {} {[@var{C}, @var{R}] =} qr (@var{A}, @var{B})
+@deftypefnx {} {[@dots{}] =} qr (@dots{}, 0)
+@deftypefnx {} {[@dots{}] =} qr (@dots{}, "vector")
+@deftypefnx {} {[@dots{}] =} qr (@dots{}, "matrix")
+@cindex QR factorization
+Compute the QR@tie{}factorization of @var{A}, using standard @sc{lapack}
+subroutines.
+
+The QR@tie{}factorization is
+@tex
+$QR = A$ where $Q$ is an orthogonal matrix and $R$ is upper triangular.
+@end tex
+@ifnottex
+
+@example
+@var{Q} * @var{R} = @var{A}
+@end example
+
+@noindent
+where @var{Q} is an orthogonal matrix and @var{R} is upper triangular.
+@end ifnottex
+
+For example, given the matrix @code{@var{A} = [1, 2; 3, 4]},
+
+@example
+[@var{Q}, @var{R}] = qr (@var{A})
+@end example
+
+@noindent
+returns
+
+@example
+@group
+@var{Q} =
+
+  -0.31623  -0.94868
+  -0.94868   0.31623
+
+@var{R} =
+
+  -3.16228  -4.42719
+   0.00000  -0.63246
+@end group
+@end example
+
+@noindent
+which multiplied together return the original matrix
+
+@example
+@group
+@var{Q} * @var{R}
+  @result{}
+     1.0000   2.0000
+     3.0000   4.0000
+@end group
+@end example
+
+If just a single return value is requested then it is either @var{R}, if
+@var{A} is sparse, or @var{X}, such that @code{@var{R} = triu (@var{X})} if
+@var{A} is full.  (Note: unlike most commands, the single return value is not
+the first return value when multiple values are requested.)
+
+If the matrix @var{A} is full, and a third output @var{P} is requested, then
+@code{qr} calculates the permuted QR@tie{}factorization
+@tex
+$QR = AP$ where $Q$ is an orthogonal matrix, $R$ is upper triangular, and $P$
+is a permutation matrix.
+@end tex
+@ifnottex
+
+@example
+@var{Q} * @var{R} = @var{A} * @var{P}
+@end example
+
+@noindent
+where @var{Q} is an orthogonal matrix, @var{R} is upper triangular, and
+@var{P} is a permutation matrix.
+@end ifnottex
+
+The permuted QR@tie{}factorization has the additional property that the
+diagonal entries of @var{R} are ordered by decreasing magnitude.  In other
+words, @code{abs (diag (@var{R}))} will be ordered from largest to smallest.
+
+For example, given the matrix @code{@var{A} = [1, 2; 3, 4]},
+
+@example
+[@var{Q}, @var{R}, @var{P}] = qr (@var{A})
+@end example
+
+@noindent
+returns
+
+@example
+@group
+@var{Q} =
+
+  -0.44721  -0.89443
+  -0.89443   0.44721
+
+@var{R} =
+
+  -4.47214  -3.13050
+   0.00000   0.44721
+
+@var{P} =
+
+   0  1
+   1  0
+@end group
+@end example
+
+If the input matrix @var{A} is sparse then the sparse QR@tie{}factorization
+is computed using @sc{CSparse}.  Because the matrix @var{Q} is, in general, a
+full matrix, it is recommended to request only one return value @var{R}.  In
+that case, the computation avoids the construction of @var{Q} and returns
+@var{R} such that @code{@var{R} = chol (@var{A}' * @var{A})}.
+
+If an additional matrix @var{B} is supplied and two return values are
+requested, then @code{qr} returns @var{C}, where
+@code{@var{C} = @var{Q}' * @var{B}}.  This allows the least squares
+approximation of @code{@var{A} \ @var{B}} to be calculated as
+
+@example
+@group
+[@var{C}, @var{R}] = qr (@var{A}, @var{B})
+@var{x} = @var{R} \ @var{C}
+@end group
+@end example
+
+If the final argument is the string @qcode{"vector"} then @var{P} is a
+permutation vector (of the columns of @var{A}) instead of a permutation matrix.
+In this case, the defining relationship is
+
+@example
+@var{Q} * @var{R} = @var{A}(:, @var{P})
+@end example
+
+The default, however, is to return a permutation matrix and this may be
+explicitly specified by using a final argument of @qcode{"matrix"}.
+
+If the final argument is the scalar 0 an @qcode{"economy"} factorization is
+returned.  When the original matrix @var{A} has size MxN and M > N then the
+@qcode{"economy"} factorization will calculate just N rows in @var{R} and N
+columns in @var{Q} and omit the zeros in @var{R}.  If M @leq{} N there is no
+difference between the economy and standard factorizations.  When calculating
+an @qcode{"economy"} factorization the output @var{P} is always a vector
+rather than a matrix.
+
+Background: The QR factorization has applications in the solution of least
+squares problems
+@tex
+$$
+\min_x \left\Vert A x - b \right\Vert_2
+$$
+@end tex
+@ifnottex
+
+@example
+min norm (A*x - b)
+@end example
+
+@end ifnottex
+for overdetermined systems of equations (i.e.,
+@tex
+$A$
+@end tex
+@ifnottex
+@var{A}
+@end ifnottex
+is a tall, thin matrix).
+
+The permuted QR@tie{}factorization
+@code{[@var{Q}, @var{R}, @var{P}] = qr (@var{A})} allows the construction of an
+orthogonal basis of @code{span (A)}.
+
+@seealso{chol, hess, lu, qz, schur, svd, qrupdate, qrinsert, qrdelete, qrshift}
+@end deftypefn */)
+{
+  int nargin = args.length ();
+
+  if (nargin < 1 || nargin > 3)
+    print_usage ();
+
+  octave_value_list retval;
+
+  octave_value arg = args(0);
+
+  bool economy = false;
+  bool is_cmplx = false;
+  bool have_b = 0;
+  bool vector_p = 0;
+
+  if (arg.iscomplex ())
+    is_cmplx = true;
+  if (nargin > 1)
+    {
+      have_b = true;
+      if (args(nargin-1).is_scalar_type ())
+        {
+          int val = args(nargin-1).int_value ();
+          if (val == 0)
+            {
+              economy = true;
+              have_b = (nargin > 2);
+            }
+          else if (nargin == 3)   // argument 3 should be 0 or a string
+            print_usage ();
+        }
+      else if (args(nargin-1).is_string ())
+        {
+          std::string str = args(nargin-1).string_value ();
+          if (str == "vector")
+            vector_p = true;
+          else if (str != "matrix")
+            error ("qr: type for P must be 'matrix' or 'vector', not %s",
+                   str.c_str ());
+          have_b = (nargin > 2);
+        }
+      else if (! args(nargin-1).is_matrix_type ())
+        err_wrong_type_arg ("qr", args(nargin-1));
+      else if (nargin == 3)   // should be caught by is_scalar_type or is_string
+        print_usage ();
+
+      if (have_b && args(1).iscomplex ())
+        is_cmplx = true;
+    }
+
+  if (arg.issparse ())
+    {
+      if (nargout > 2)
+        error ("qr: Permutation output is not supported for sparse input");
+
+      if (is_cmplx)
+        {
+          octave::math::sparse_qr<SparseComplexMatrix> q (arg.sparse_complex_matrix_value ());
+
+          if (have_b)
+            {
+              retval = ovl (q.C (args(1).complex_matrix_value ()),
+                            q.R (economy));
+              if (arg.rows () < arg.columns ())
+                warning ("qr: non minimum norm solution for under-determined "
+                         "problem %" OCTAVE_IDX_TYPE_FORMAT
+                         "x%" OCTAVE_IDX_TYPE_FORMAT,
+                         arg.rows (), arg.columns ());
+            }
+          else if (nargout > 1)
+            retval = ovl (q.Q (), q.R (economy));
+          else
+            retval = ovl (q.R (economy));
+        }
+      else
+        {
+          octave::math::sparse_qr<SparseMatrix> q (arg.sparse_matrix_value ());
+
+          if (have_b)
+            {
+              retval = ovl (q.C (args(1).matrix_value ()), q.R (economy));
+              if (arg.rows () < arg.columns ())
+                warning ("qr: non minimum norm solution for under-determined "
+                         "problem %" OCTAVE_IDX_TYPE_FORMAT
+                         "x%" OCTAVE_IDX_TYPE_FORMAT,
+                         arg.rows (), arg.columns ());
+            }
+          else if (nargout > 1)
+            retval = ovl (q.Q (), q.R (economy));
+          else
+            retval = ovl (q.R (economy));
+        }
+    }
+  else
+    {
+      if (arg.is_single_type ())
+        {
+          if (arg.isreal ())
+            {
+              octave::math::qr<FloatMatrix>::type type
+                = qr_type<FloatMatrix> (nargout, economy);
+
+              FloatMatrix m = arg.float_matrix_value ();
+
+              switch (nargout)
+                {
+                case 0:
+                case 1:
+                  {
+                    octave::math::qr<FloatMatrix> fact (m, type);
+                    retval = ovl (fact.R ());
+                  }
+                  break;
+
+                case 2:
+                  {
+                    octave::math::qr<FloatMatrix> fact (m, type);
+                    retval = ovl (fact.Q (), get_qr_r (fact));
+                    if (have_b)
+                      {
+                        if (is_cmplx)
+                          retval(0) = fact.Q ().transpose ()
+                                      * args(1).float_complex_matrix_value ();
+                        else
+                          retval(0) = fact.Q ().transpose ()
+                                      * args(1).float_matrix_value ();
+                      }
+                  }
+                  break;
+
+                default:
+                  {
+                    octave::math::qrp<FloatMatrix> fact (m, type);
+
+                    if (economy || vector_p)
+                      retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
+                    else
+                      retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
+                  }
+                  break;
+                }
+            }
+          else if (arg.iscomplex ())
+            {
+              octave::math::qr<FloatComplexMatrix>::type type
+                = qr_type<FloatComplexMatrix> (nargout, economy);
+
+              FloatComplexMatrix m = arg.float_complex_matrix_value ();
+
+              switch (nargout)
+                {
+                case 0:
+                case 1:
+                  {
+                    octave::math::qr<FloatComplexMatrix> fact (m, type);
+                    retval = ovl (fact.R ());
+                  }
+                  break;
+
+                case 2:
+                  {
+                    octave::math::qr<FloatComplexMatrix> fact (m, type);
+                    retval = ovl (fact.Q (), get_qr_r (fact));
+                    if (have_b)
+                      retval (0) = conj (fact.Q ().transpose ())
+                                   * args(1).float_complex_matrix_value ();
+                  }
+                  break;
+
+                default:
+                  {
+                    octave::math::qrp<FloatComplexMatrix> fact (m, type);
+                    if (economy || vector_p)
+                      retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
+                    else
+                      retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
+                  }
+                  break;
+                }
+            }
+        }
+      else
+        {
+          if (arg.isreal ())
+            {
+              octave::math::qr<Matrix>::type type
+                = qr_type<Matrix> (nargout, economy);
+
+              Matrix m = arg.matrix_value ();
+
+              switch (nargout)
+                {
+                case 0:
+                case 1:
+                  {
+                    octave::math::qr<Matrix> fact (m, type);
+                    retval = ovl (fact.R ());
+                  }
+                  break;
+
+                case 2:
+                  {
+                    octave::math::qr<Matrix> fact (m, type);
+                    retval = ovl (fact.Q (), get_qr_r (fact));
+                    if (have_b)
+                      {
+                        if (is_cmplx)
+                          retval(0) = fact.Q ().transpose ()
+                                      * args(1).complex_matrix_value ();
+                        else
+                          retval(0) = fact.Q ().transpose ()
+                                      * args(1).matrix_value ();
+                      }
+                  }
+                  break;
+
+                default:
+                  {
+                    octave::math::qrp<Matrix> fact (m, type);
+                    if (economy || vector_p)
+                      retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
+                    else
+                      retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
+                  }
+                  break;
+                }
+            }
+          else if (arg.iscomplex ())
+            {
+              octave::math::qr<ComplexMatrix>::type type
+                = qr_type<ComplexMatrix> (nargout, economy);
+
+              ComplexMatrix m = arg.complex_matrix_value ();
+
+              switch (nargout)
+                {
+                case 0:
+                case 1:
+                  {
+                    octave::math::qr<ComplexMatrix> fact (m, type);
+                    retval = ovl (fact.R ());
+                  }
+                  break;
+
+                case 2:
+                  {
+                    octave::math::qr<ComplexMatrix> fact (m, type);
+                    retval = ovl (fact.Q (), get_qr_r (fact));
+                    if (have_b)
+                      retval (0) = conj (fact.Q ().transpose ())
+                                   * args(1).complex_matrix_value ();
+                  }
+                  break;
+
+                default:
+                  {
+                    octave::math::qrp<ComplexMatrix> fact (m, type);
+                    if (economy || vector_p)
+                      retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
+                    else
+                      retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
+                  }
+                  break;
+                }
+            }
+          else
+            err_wrong_type_arg ("qr", arg);
+        }
+    }
+
+  return retval;
+}
+
+/*
+%!test
+%! a = [0, 2, 1; 2, 1, 2];
+%!
+%! [q, r] = qr (a);
+%! [qe, re] = qr (a, 0);
+%!
+%! assert (q * r, a, sqrt (eps));
+%! assert (qe * re, a, sqrt (eps));
+
+%!test
+%! a = [0, 2, 1; 2, 1, 2];
+%!
+%! [q, r] = qr (a);
+%! [qe, re] = qr (a, 0);
+%!
+%! assert (q * r, a, sqrt (eps));
+%! assert (qe * re, a, sqrt (eps));
+
+%!test
+%! a = [0, 2, 1; 2, 1, 2];
+%!
+%! [q, r, p] = qr (a);  # FIXME: not giving right dimensions.
+%! [qe, re, pe] = qr (a, 0);
+%!
+%! assert (q * r, a * p, sqrt (eps));
+%! assert (qe * re, a(:, pe), sqrt (eps));
+
+%!test
+%! a = [0, 2; 2, 1; 1, 2];
+%!
+%! [q, r] = qr (a);
+%! [qe, re] = qr (a, 0);
+%!
+%! assert (q * r, a, sqrt (eps));
+%! assert (qe * re, a, sqrt (eps));
+
+%!test
+%! a = [0, 2; 2, 1; 1, 2];
+%!
+%! [q, r, p] = qr (a);
+%! [qe, re, pe] = qr (a, 0);
+%!
+%! assert (q * r, a * p, sqrt (eps));
+%! assert (qe * re, a(:, pe), sqrt (eps));
+
+%!test
+%! a = [0, 2, 1; 2, 1, 2; 3, 1, 2];
+%! b = [1, 3, 2; 1, 1, 0; 3, 0, 2];
+%!
+%! [q, r] = qr (a);
+%! [c, re] = qr (a, b);
+%!
+%! assert (r, re, sqrt (eps));
+%! assert (q'*b, c, sqrt (eps));
+
+%!test
+%! a = [0, 2, i; 2, 1, 2; 3, 1, 2];
+%! b = [1, 3, 2; 1, i, 0; 3, 0, 2];
+%!
+%! [q, r] = qr (a);
+%! [c, re] = qr (a, b);
+%!
+%! assert (r, re, sqrt (eps));
+%! assert (q'*b, c, sqrt (eps));
+
+%!test
+%! a = [0, 2, i; 2, 1, 2; 3, 1, 2];
+%! b = [1, 3, 2; 1, 1, 0; 3, 0, 2];
+%!
+%! [q, r] = qr (a);
+%! [c, re] = qr (a, b);
+%!
+%! assert (r, re, sqrt (eps));
+%! assert (q'*b, c, sqrt (eps));
+
+%!test
+%! a = [0, 2, 1; 2, 1, 2; 3, 1, 2];
+%! b = [1, 3, 2; 1, i, 0; 3, 0, 2];
+%!
+%! [q, r] = qr (a);
+%! [c, re] = qr (a, b);
+%!
+%! assert (r, re, sqrt (eps));
+%! assert (q'*b, c, sqrt (eps));
+
+%!test
+%! assert (qr (zeros (0, 0)), zeros (0, 0))
+%! assert (qr (zeros (1, 0)), zeros (1, 0))
+%! assert (qr (zeros (0, 1)), zeros (0, 1))
+
+%!error qr ()
+%!error qr ([1, 2; 3, 4], 0, 2)
+
+%!function retval = __testqr (q, r, a, p)
+%!  tol = 100*eps (class (q));
+%!  retval = 0;
+%!  if (nargin == 3)
+%!    n1 = norm (q*r - a);
+%!    n2 = norm (q'*q - eye (columns (q)));
+%!    retval = (n1 < tol && n2 < tol);
+%!  else
+%!    n1 = norm (q'*q - eye (columns (q)));
+%!    retval = (n1 < tol);
+%!    if (isvector (p))
+%!      n2 = norm (q*r - a(:,p));
+%!      retval = (retval && n2 < tol);
+%!    else
+%!      n2 = norm (q*r - a*p);
+%!      retval = (retval && n2 < tol);
+%!    endif
+%!  endif
+%!endfunction
+
+%!test
+%! t = ones (24, 1);
+%! j = 1;
+%!
+%! if (false)  # eliminate big matrix tests
+%!   a = rand (5000, 20);
+%!   [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
+%!   [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
+%!   [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
+%!   [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
+%!
+%!   a = a+1i*eps;
+%!   [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
+%!   [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
+%!   [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
+%!   [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
+%! endif
+%!
+%! a = [ ones(1,15); sqrt(eps)*eye(15) ];
+%! [q,r]   = qr (a);   t(j++) = __testqr (q, r, a);
+%! [q,r]   = qr (a');  t(j++) = __testqr (q, r, a');
+%! [q,r,p] = qr (a);   t(j++) = __testqr (q, r, a, p);
+%! [q,r,p] = qr (a');  t(j++) = __testqr (q, r, a', p);
+%!
+%! a = a+1i*eps;
+%! [q,r]   = qr (a);   t(j++) = __testqr (q, r, a);
+%! [q,r]   = qr (a');  t(j++) = __testqr (q, r, a');
+%! [q,r,p] = qr (a);   t(j++) = __testqr (q, r, a, p);
+%! [q,r,p] = qr (a');  t(j++) = __testqr (q, r, a', p);
+%!
+%! a = [ ones(1,15); sqrt(eps)*eye(15) ];
+%! [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
+%! [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
+%! [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
+%! [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
+%!
+%! a = a+1i*eps;
+%! [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
+%! [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
+%! [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
+%! [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
+%!
+%! a = [ 611   196  -192   407    -8   -52   -49    29
+%!       196   899   113  -192   -71   -43    -8   -44
+%!      -192   113   899   196    61    49     8    52
+%!       407  -192   196   611     8    44    59   -23
+%!        -8   -71    61     8   411  -599   208   208
+%!       -52   -43    49    44  -599   411   208   208
+%!       -49    -8     8    59   208   208    99  -911
+%!        29   -44    52   -23   208   208  -911    99 ];
+%! [q,r] = qr (a);
+%!
+%! assert (all (t) && norm (q*r - a) < 5000*eps);
+
+%!test
+%! a = single ([0, 2, 1; 2, 1, 2]);
+%!
+%! [q, r] = qr (a);
+%! [qe, re] = qr (a, 0);
+%!
+%! assert (q * r, a, sqrt (eps ("single")));
+%! assert (qe * re, a, sqrt (eps ("single")));
+
+%!test
+%! a = single ([0, 2, 1; 2, 1, 2]);
+%!
+%! [q, r, p] = qr (a);  # FIXME: not giving right dimensions.
+%! [qe, re, pe] = qr (a, 0);
+%!
+%! assert (q * r, a * p, sqrt (eps ("single")));
+%! assert (qe * re, a(:, pe), sqrt (eps ("single")));
+
+%!test
+%! a = single ([0, 2; 2, 1; 1, 2]);
+%!
+%! [q, r] = qr (a);
+%! [qe, re] = qr (a, 0);
+%!
+%! assert (q * r, a, sqrt (eps ("single")));
+%! assert (qe * re, a, sqrt (eps ("single")));
+
+%!test
+%! a = single ([0, 2; 2, 1; 1, 2]);
+%!
+%! [q, r, p] = qr (a);
+%! [qe, re, pe] = qr (a, 0);
+%!
+%! assert (q * r, a * p, sqrt (eps ("single")));
+%! assert (qe * re, a(:, pe), sqrt (eps ("single")));
+
+%!test
+%! a = single([0, 2, 1; 2, 1, 2; 3, 1, 2]);
+%! b = single([1, 3, 2; 1, 1, 0; 3, 0, 2]);
+%!
+%! [q, r] = qr (a);
+%! [c, re] = qr (a, b);
+%!
+%! assert (r, re, sqrt (eps ("single")));
+%! assert (q'*b, c, sqrt (eps ("single")));
+
+%!test
+%! a = single([0, 2, i; 2, 1, 2; 3, 1, 2]);
+%! b = single([1, 3, 2; 1, i, 0; 3, 0, 2]);
+%!
+%! [q, r] = qr (a);
+%! [c, re] = qr (a, b);
+%!
+%! assert (r, re, sqrt (eps ("single")));
+%! assert (q'*b, c, sqrt (eps ("single")));
+
+%!test
+%! a = single([0, 2, i; 2, 1, 2; 3, 1, 2]);
+%! b = single([1, 3, 2; 1, 1, 0; 3, 0, 2]);
+%!
+%! [q, r] = qr (a);
+%! [c, re] = qr (a, b);
+%!
+%! assert (r, re, sqrt (eps));
+%! assert (q'*b, c, sqrt (eps));
+
+%!test
+%! a = single([0, 2, 1; 2, 1, 2; 3, 1, 2]);
+%! b = single([1, 3, 2; 1, i, 0; 3, 0, 2]);
+%!
+%! [q, r] = qr (a);
+%! [c, re] = qr (a, b);
+%!
+%! assert (r, re, sqrt (eps ("single")));
+%! assert (q'*b, c, sqrt (eps ("single")));
+
+%!error qr ()
+%!error qr ([1, 2; 3, 4], 0, 2)
+
+%!test
+%! t = ones (24, 1);
+%! j = 1;
+%!
+%! if (false)  # eliminate big matrix tests
+%!   a = rand (5000,20);
+%!   [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
+%!   [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
+%!   [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
+%!   [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
+%!
+%!   a = a+1i*eps ("single");
+%!   [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
+%!   [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
+%!   [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
+%!   [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
+%! endif
+%!
+%! a = [ ones(1,15); sqrt(eps("single"))*eye(15) ];
+%! [q,r]   = qr (a);   t(j++) = __testqr (q, r, a);
+%! [q,r]   = qr (a');  t(j++) = __testqr (q, r, a');
+%! [q,r,p] = qr (a);   t(j++) = __testqr (q, r, a, p);
+%! [q,r,p] = qr (a');  t(j++) = __testqr (q, r, a', p);
+%!
+%! a = a+1i*eps ("single");
+%! [q,r]   = qr (a);   t(j++) = __testqr (q, r, a);
+%! [q,r]   = qr (a');  t(j++) = __testqr (q, r, a');
+%! [q,r,p] = qr (a);   t(j++) = __testqr (q, r, a, p);
+%! [q,r,p] = qr (a');  t(j++) = __testqr (q, r, a', p);
+%!
+%! a = [ ones(1,15); sqrt(eps("single"))*eye(15) ];
+%! [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
+%! [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
+%! [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
+%! [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
+%!
+%! a = a+1i*eps ("single");
+%! [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
+%! [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
+%! [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
+%! [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a',p);
+%!
+%! a = [ 611   196  -192   407    -8   -52   -49    29
+%!       196   899   113  -192   -71   -43    -8   -44
+%!      -192   113   899   196    61    49     8    52
+%!       407  -192   196   611     8    44    59   -23
+%!        -8   -71    61     8   411  -599   208   208
+%!       -52   -43    49    44  -599   411   208   208
+%!       -49    -8     8    59   208   208    99  -911
+%!        29   -44    52   -23   208   208  -911    99 ];
+%! [q,r] = qr (a);
+%!
+%! assert (all (t) && norm (q*r-a) < 5000*eps ("single"));
+
+## The deactivated tests below can't be tested till rectangular back-subs is
+## implemented for sparse matrices.
+
+%!testif HAVE_CXSPARSE
+%! n = 20;  d = 0.2;
+%! ## initialize generators to make behavior reproducible
+%! rand ("state", 42);
+%! randn ("state", 42);
+%! a = sprandn (n,n,d) + speye (n,n);
+%! r = qr (a);
+%! assert (r'*r, a'*a, 1e-10);
+
+%!testif HAVE_COLAMD
+%! n = 20;  d = 0.2;
+%! ## initialize generators to make behavior reproducible
+%! rand ("state", 42);
+%! randn ("state", 42);
+%! a = sprandn (n,n,d) + speye (n,n);
+%! q = symamd (a);
+%! a = a(q,q);
+%! r = qr (a);
+%! assert (r'*r, a'*a, 1e-10);
+
+%!testif HAVE_CXSPARSE
+%! n = 20;  d = 0.2;
+%! ## initialize generators to make behavior reproducible
+%! rand ("state", 42);
+%! randn ("state", 42);
+%! a = sprandn (n,n,d) + speye (n,n);
+%! [c,r] = qr (a, ones (n,1));
+%! assert (r\c, full (a)\ones (n,1), 10e-10);
+
+%!testif HAVE_CXSPARSE
+%! n = 20;  d = 0.2;
+%! ## initialize generators to make behavior reproducible
+%! rand ("state", 42);
+%! randn ("state", 42);
+%! a = sprandn (n,n,d) + speye (n,n);
+%! b = randn (n,2);
+%! [c,r] = qr (a, b);
+%! assert (r\c, full (a)\b, 10e-10);
+
+## Test under-determined systems!!
+%!#testif HAVE_CXSPARSE
+%! n = 20;  d = 0.2;
+%! ## initialize generators to make behavior reproducible
+%! rand ("state", 42);
+%! randn ("state", 42);
+%! a = sprandn (n,n+1,d) + speye (n,n+1);
+%! b = randn (n,2);
+%! [c,r] = qr (a, b);
+%! assert (r\c, full (a)\b, 10e-10);
+
+%!testif HAVE_CXSPARSE
+%! n = 20;  d = 0.2;
+%! ## initialize generators to make behavior reproducible
+%! rand ("state", 42);
+%! randn ("state", 42);
+%! a = 1i*sprandn (n,n,d) + speye (n,n);
+%! r = qr (a);
+%! assert (r'*r,a'*a,1e-10);
+
+%!testif HAVE_COLAMD
+%! n = 20;  d = 0.2;
+%! ## initialize generators to make behavior reproducible
+%! rand ("state", 42);
+%! randn ("state", 42);
+%! a = 1i*sprandn (n,n,d) + speye (n,n);
+%! q = symamd (a);
+%! a = a(q,q);
+%! r = qr (a);
+%! assert (r'*r, a'*a, 1e-10);
+
+%!testif HAVE_CXSPARSE
+%! n = 20;  d = 0.2;
+%! ## initialize generators to make behavior reproducible
+%! rand ("state", 42);
+%! randn ("state", 42);
+%! a = 1i*sprandn (n,n,d) + speye (n,n);
+%! [c,r] = qr (a, ones (n,1));
+%! assert (r\c, full (a)\ones (n,1), 10e-10);
+
+%!testif HAVE_CXSPARSE
+%! n = 20;  d = 0.2;
+%! ## initialize generators to make behavior reproducible
+%! rand ("state", 42);
+%! randn ("state", 42);
+%! a = 1i*sprandn (n,n,d) + speye (n,n);
+%! b = randn (n,2);
+%! [c,r] = qr (a, b);
+%! assert (r\c, full (a)\b, 10e-10);
+
+## Test under-determined systems!!
+%!#testif HAVE_CXSPARSE
+%! n = 20;  d = 0.2;
+%! ## initialize generators to make behavior reproducible
+%! rand ("state", 42);
+%! randn ("state", 42);
+%! a = 1i*sprandn (n,n+1,d) + speye (n,n+1);
+%! b = randn (n,2);
+%! [c,r] = qr (a, b);
+%! assert (r\c, full (a)\b, 10e-10);
+
+*/
+
+static
+bool check_qr_dims (const octave_value& q, const octave_value& r,
+                    bool allow_ecf = false)
+{
+  octave_idx_type m = q.rows ();
+  octave_idx_type k = r.rows ();
+  octave_idx_type n = r.columns ();
+  return ((q.ndims () == 2 && r.ndims () == 2 && k == q.columns ())
+          && (m == k || (allow_ecf && k == n && k < m)));
+}
+
+static
+bool check_index (const octave_value& i, bool vector_allowed = false)
+{
+  return ((i.isreal () || i.isinteger ())
+          && (i.is_scalar_type () || vector_allowed));
+}
+
+DEFUN (qrupdate, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {[@var{Q1}, @var{R1}] =} qrupdate (@var{Q}, @var{R}, @var{u}, @var{v})
+Update a QR factorization given update vectors or matrices.
+
+Given a QR@tie{}factorization of a real or complex matrix
+@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and
+@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of
+@w{@var{A} + @var{u}*@var{v}'}, where @var{u} and @var{v} are column vectors
+(rank-1 update) or matrices with equal number of columns
+(rank-k update).  Notice that the latter case is done as a sequence of
+rank-1 updates; thus, for k large enough, it will be both faster and more
+accurate to recompute the factorization from scratch.
+
+The QR@tie{}factorization supplied may be either full (Q is square) or
+economized (R is square).
+
+@seealso{qr, qrinsert, qrdelete, qrshift}
+@end deftypefn */)
+{
+  octave_value_list retval;
+
+  if (args.length () != 4)
+    print_usage ();
+
+  octave_value argq = args(0);
+  octave_value argr = args(1);
+  octave_value argu = args(2);
+  octave_value argv = args(3);
+
+  if (! argq.isnumeric () || ! argr.isnumeric ()
+      || ! argu.isnumeric () || ! argv.isnumeric ())
+    print_usage ();
+
+  if (! check_qr_dims (argq, argr, true))
+    error ("qrupdate: Q and R dimensions don't match");
+
+  if (argq.isreal () && argr.isreal () && argu.isreal ()
+      && argv.isreal ())
+    {
+      // all real case
+      if (argq.is_single_type () || argr.is_single_type ()
+          || argu.is_single_type () || argv.is_single_type ())
+        {
+          FloatMatrix Q = argq.float_matrix_value ();
+          FloatMatrix R = argr.float_matrix_value ();
+          FloatMatrix u = argu.float_matrix_value ();
+          FloatMatrix v = argv.float_matrix_value ();
+
+          octave::math::qr<FloatMatrix> fact (Q, R);
+          fact.update (u, v);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+      else
+        {
+          Matrix Q = argq.matrix_value ();
+          Matrix R = argr.matrix_value ();
+          Matrix u = argu.matrix_value ();
+          Matrix v = argv.matrix_value ();
+
+          octave::math::qr<Matrix> fact (Q, R);
+          fact.update (u, v);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+    }
+  else
+    {
+      // complex case
+      if (argq.is_single_type () || argr.is_single_type ()
+          || argu.is_single_type () || argv.is_single_type ())
+        {
+          FloatComplexMatrix Q = argq.float_complex_matrix_value ();
+          FloatComplexMatrix R = argr.float_complex_matrix_value ();
+          FloatComplexMatrix u = argu.float_complex_matrix_value ();
+          FloatComplexMatrix v = argv.float_complex_matrix_value ();
+
+          octave::math::qr<FloatComplexMatrix> fact (Q, R);
+          fact.update (u, v);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+      else
+        {
+          ComplexMatrix Q = argq.complex_matrix_value ();
+          ComplexMatrix R = argr.complex_matrix_value ();
+          ComplexMatrix u = argu.complex_matrix_value ();
+          ComplexMatrix v = argv.complex_matrix_value ();
+
+          octave::math::qr<ComplexMatrix> fact (Q, R);
+          fact.update (u, v);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+    }
+
+  return retval;
+}
+
+/*
+%!shared A, u, v, Ac, uc, vc
+%! A = [0.091364  0.613038  0.999083;
+%!      0.594638  0.425302  0.603537;
+%!      0.383594  0.291238  0.085574;
+%!      0.265712  0.268003  0.238409;
+%!      0.669966  0.743851  0.445057 ];
+%!
+%! u = [0.85082;
+%!      0.76426;
+%!      0.42883;
+%!      0.53010;
+%!      0.80683 ];
+%!
+%! v = [0.98810;
+%!      0.24295;
+%!      0.43167 ];
+%!
+%! Ac = [0.620405 + 0.956953i  0.480013 + 0.048806i  0.402627 + 0.338171i;
+%!      0.589077 + 0.658457i  0.013205 + 0.279323i  0.229284 + 0.721929i;
+%!      0.092758 + 0.345687i  0.928679 + 0.241052i  0.764536 + 0.832406i;
+%!      0.912098 + 0.721024i  0.049018 + 0.269452i  0.730029 + 0.796517i;
+%!      0.112849 + 0.603871i  0.486352 + 0.142337i  0.355646 + 0.151496i ];
+%!
+%! uc = [0.20351 + 0.05401i;
+%!      0.13141 + 0.43708i;
+%!      0.29808 + 0.08789i;
+%!      0.69821 + 0.38844i;
+%!      0.74871 + 0.25821i ];
+%!
+%! vc = [0.85839 + 0.29468i;
+%!      0.20820 + 0.93090i;
+%!      0.86184 + 0.34689i ];
+%!
+
+%!test
+%! [Q,R] = qr (A);
+%! [Q,R] = qrupdate (Q, R, u, v);
+%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R)-R), Inf) == 0);
+%! assert (norm (vec (Q*R - A - u*v'), Inf) < norm (A)*1e1*eps);
+%!
+%!test
+%! [Q,R] = qr (Ac);
+%! [Q,R] = qrupdate (Q, R, uc, vc);
+%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R)-R), Inf) == 0);
+%! assert (norm (vec (Q*R - Ac - uc*vc'), Inf) < norm (Ac)*1e1*eps);
+
+%!test
+%! [Q,R] = qr (single (A));
+%! [Q,R] = qrupdate (Q, R, single (u), single (v));
+%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R)-R), Inf) == 0);
+%! assert (norm (vec (Q*R - single (A) - single (u)*single (v)'), Inf) < norm (single (A))*1e1*eps ("single"));
+%!
+%!test
+%! [Q,R] = qr (single (Ac));
+%! [Q,R] = qrupdate (Q, R, single (uc), single (vc));
+%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R)-R), Inf) == 0);
+%! assert (norm (vec (Q*R - single (Ac) - single (uc)*single (vc)'), Inf) < norm (single (Ac))*1e1*eps ("single"));
+*/
+
+DEFUN (qrinsert, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {[@var{Q1}, @var{R1}] =} qrinsert (@var{Q}, @var{R}, @var{j}, @var{x}, @var{orient})
+Update a QR factorization given a row or column to insert in the original
+factored matrix.
+
+
+Given a QR@tie{}factorization of a real or complex matrix
+@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and
+@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of
+@w{[A(:,1:j-1) x A(:,j:n)]}, where @var{u} is a column vector to be inserted
+into @var{A} (if @var{orient} is @qcode{"col"}), or the
+QR@tie{}factorization of @w{[A(1:j-1,:);x;A(:,j:n)]}, where @var{x} is a row
+vector to be inserted into @var{A} (if @var{orient} is @qcode{"row"}).
+
+The default value of @var{orient} is @qcode{"col"}.  If @var{orient} is
+@qcode{"col"}, @var{u} may be a matrix and @var{j} an index vector
+resulting in the QR@tie{}factorization of a matrix @var{B} such that
+@w{B(:,@var{j})} gives @var{u} and @w{B(:,@var{j}) = []} gives @var{A}.
+Notice that the latter case is done as a sequence of k insertions;
+thus, for k large enough, it will be both faster and more accurate to
+recompute the factorization from scratch.
+
+If @var{orient} is @qcode{"col"}, the QR@tie{}factorization supplied may
+be either full (Q is square) or economized (R is square).
+
+If @var{orient} is @qcode{"row"}, full factorization is needed.
+@seealso{qr, qrupdate, qrdelete, qrshift}
+@end deftypefn */)
+{
+  int nargin = args.length ();
+
+  if (nargin < 4 || nargin > 5)
+    print_usage ();
+
+  octave_value argq = args(0);
+  octave_value argr = args(1);
+  octave_value argj = args(2);
+  octave_value argx = args(3);
+
+  if (! argq.isnumeric () || ! argr.isnumeric ()
+      || ! argx.isnumeric ()
+      || (nargin > 4 && ! args(4).is_string ()))
+    print_usage ();
+
+  std::string orient = (nargin < 5) ? "col" : args(4).string_value ();
+  bool col = (orient == "col");
+
+  if (! col && orient != "row")
+    error (R"(qrinsert: ORIENT must be "col" or "row")");
+
+  if (! check_qr_dims (argq, argr, col) || (! col && argx.rows () != 1))
+    error ("qrinsert: dimension mismatch");
+
+  if (! check_index (argj, col))
+    error ("qrinsert: invalid index J");
+
+  octave_value_list retval;
+
+  MArray<octave_idx_type> j = argj.octave_idx_type_vector_value ();
+
+  octave_idx_type one = 1;
+
+  if (argq.isreal () && argr.isreal () && argx.isreal ())
+    {
+      // real case
+      if (argq.is_single_type () || argr.is_single_type ()
+          || argx.is_single_type ())
+        {
+          FloatMatrix Q = argq.float_matrix_value ();
+          FloatMatrix R = argr.float_matrix_value ();
+          FloatMatrix x = argx.float_matrix_value ();
+
+          octave::math::qr<FloatMatrix> fact (Q, R);
+
+          if (col)
+            fact.insert_col (x, j-one);
+          else
+            fact.insert_row (x.row (0), j(0)-one);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+      else
+        {
+          Matrix Q = argq.matrix_value ();
+          Matrix R = argr.matrix_value ();
+          Matrix x = argx.matrix_value ();
+
+          octave::math::qr<Matrix> fact (Q, R);
+
+          if (col)
+            fact.insert_col (x, j-one);
+          else
+            fact.insert_row (x.row (0), j(0)-one);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+    }
+  else
+    {
+      // complex case
+      if (argq.is_single_type () || argr.is_single_type ()
+          || argx.is_single_type ())
+        {
+          FloatComplexMatrix Q = argq.float_complex_matrix_value ();
+          FloatComplexMatrix R = argr.float_complex_matrix_value ();
+          FloatComplexMatrix x = argx.float_complex_matrix_value ();
+
+          octave::math::qr<FloatComplexMatrix> fact (Q, R);
+
+          if (col)
+            fact.insert_col (x, j-one);
+          else
+            fact.insert_row (x.row (0), j(0)-one);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+      else
+        {
+          ComplexMatrix Q = argq.complex_matrix_value ();
+          ComplexMatrix R = argr.complex_matrix_value ();
+          ComplexMatrix x = argx.complex_matrix_value ();
+
+          octave::math::qr<ComplexMatrix> fact (Q, R);
+
+          if (col)
+            fact.insert_col (x, j-one);
+          else
+            fact.insert_row (x.row (0), j(0)-one);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+    }
+
+  return retval;
+}
+
+/*
+%!test
+%! [Q,R] = qr (A);
+%! [Q,R] = qrinsert (Q, R, 3, u);
+%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [A(:,1:2) u A(:,3)]), Inf) < norm (A)*1e1*eps);
+%!test
+%! [Q,R] = qr (Ac);
+%! [Q,R] = qrinsert (Q, R, 3, uc);
+%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [Ac(:,1:2) uc Ac(:,3)]), Inf) < norm (Ac)*1e1*eps);
+%!test
+%! x = [0.85082  0.76426  0.42883 ];
+%!
+%! [Q,R] = qr (A);
+%! [Q,R] = qrinsert (Q, R, 3, x, "row");
+%! assert (norm (vec (Q'*Q - eye (6)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [A(1:2,:);x;A(3:5,:)]), Inf) < norm (A)*1e1*eps);
+%!test
+%! x = [0.20351 + 0.05401i  0.13141 + 0.43708i  0.29808 + 0.08789i ];
+%!
+%! [Q,R] = qr (Ac);
+%! [Q,R] = qrinsert (Q, R, 3, x, "row");
+%! assert (norm (vec (Q'*Q - eye (6)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [Ac(1:2,:);x;Ac(3:5,:)]), Inf) < norm (Ac)*1e1*eps);
+
+%!test
+%! [Q,R] = qr (single (A));
+%! [Q,R] = qrinsert (Q, R, 3, single (u));
+%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - single ([A(:,1:2) u A(:,3)])), Inf) < norm (single (A))*1e1*eps ("single"));
+%!test
+%! [Q,R] = qr (single (Ac));
+%! [Q,R] = qrinsert (Q, R, 3, single (uc));
+%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - single ([Ac(:,1:2) uc Ac(:,3)])), Inf) < norm (single (Ac))*1e1*eps ("single"));
+%!test
+%! x = single ([0.85082  0.76426  0.42883 ]);
+%!
+%! [Q,R] = qr (single (A));
+%! [Q,R] = qrinsert (Q, R, 3, x, "row");
+%! assert (norm (vec (Q'*Q - eye (6,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - single ([A(1:2,:);x;A(3:5,:)])), Inf) < norm (single (A))*1e1*eps ("single"));
+%!test
+%! x = single ([0.20351 + 0.05401i  0.13141 + 0.43708i  0.29808 + 0.08789i ]);
+%!
+%! [Q,R] = qr (single (Ac));
+%! [Q,R] = qrinsert (Q, R, 3, x, "row");
+%! assert (norm (vec (Q'*Q - eye (6,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - single ([Ac(1:2,:);x;Ac(3:5,:)])), Inf) < norm (single (Ac))*1e1*eps ("single"));
+*/
+
+DEFUN (qrdelete, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {[@var{Q1}, @var{R1}] =} qrdelete (@var{Q}, @var{R}, @var{j}, @var{orient})
+Update a QR factorization given a row or column to delete from the original
+factored matrix.
+
+Given a QR@tie{}factorization of a real or complex matrix
+@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and
+@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of
+@w{[A(:,1:j-1), U, A(:,j:n)]},
+where @var{u} is a column vector to be inserted into @var{A}
+(if @var{orient} is @qcode{"col"}),
+or the QR@tie{}factorization of @w{[A(1:j-1,:);X;A(:,j:n)]},
+where @var{x} is a row @var{orient} is @qcode{"row"}).
+The default value of @var{orient} is @qcode{"col"}.
+
+If @var{orient} is @qcode{"col"}, @var{j} may be an index vector
+resulting in the QR@tie{}factorization of a matrix @var{B} such that
+@w{A(:,@var{j}) = []} gives @var{B}.  Notice that the latter case is done as
+a sequence of k deletions; thus, for k large enough, it will be both faster
+and more accurate to recompute the factorization from scratch.
+
+If @var{orient} is @qcode{"col"}, the QR@tie{}factorization supplied may
+be either full (Q is square) or economized (R is square).
+
+If @var{orient} is @qcode{"row"}, full factorization is needed.
+@seealso{qr, qrupdate, qrinsert, qrshift}
+@end deftypefn */)
+{
+  int nargin = args.length ();
+
+  if (nargin < 3 || nargin > 4)
+    print_usage ();
+
+  octave_value argq = args(0);
+  octave_value argr = args(1);
+  octave_value argj = args(2);
+
+  if (! argq.isnumeric () || ! argr.isnumeric ()
+      || (nargin > 3 && ! args(3).is_string ()))
+    print_usage ();
+
+  std::string orient = (nargin < 4) ? "col" : args(3).string_value ();
+  bool col = orient == "col";
+
+  if (! col && orient != "row")
+    error (R"(qrdelete: ORIENT must be "col" or "row")");
+
+  if (! check_qr_dims (argq, argr, col))
+    error ("qrdelete: dimension mismatch");
+
+  MArray<octave_idx_type> j = argj.octave_idx_type_vector_value ();
+  if (! check_index (argj, col))
+    error ("qrdelete: invalid index J");
+
+  octave_value_list retval;
+
+  octave_idx_type one = 1;
+
+  if (argq.isreal () && argr.isreal ())
+    {
+      // real case
+      if (argq.is_single_type () || argr.is_single_type ())
+        {
+          FloatMatrix Q = argq.float_matrix_value ();
+          FloatMatrix R = argr.float_matrix_value ();
+
+          octave::math::qr<FloatMatrix> fact (Q, R);
+
+          if (col)
+            fact.delete_col (j-one);
+          else
+            fact.delete_row (j(0)-one);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+      else
+        {
+          Matrix Q = argq.matrix_value ();
+          Matrix R = argr.matrix_value ();
+
+          octave::math::qr<Matrix> fact (Q, R);
+
+          if (col)
+            fact.delete_col (j-one);
+          else
+            fact.delete_row (j(0)-one);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+    }
+  else
+    {
+      // complex case
+      if (argq.is_single_type () || argr.is_single_type ())
+        {
+          FloatComplexMatrix Q = argq.float_complex_matrix_value ();
+          FloatComplexMatrix R = argr.float_complex_matrix_value ();
+
+          octave::math::qr<FloatComplexMatrix> fact (Q, R);
+
+          if (col)
+            fact.delete_col (j-one);
+          else
+            fact.delete_row (j(0)-one);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+      else
+        {
+          ComplexMatrix Q = argq.complex_matrix_value ();
+          ComplexMatrix R = argr.complex_matrix_value ();
+
+          octave::math::qr<ComplexMatrix> fact (Q, R);
+
+          if (col)
+            fact.delete_col (j-one);
+          else
+            fact.delete_row (j(0)-one);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+    }
+
+  return retval;
+}
+
+/*
+%!test
+%! AA = [0.091364  0.613038  0.027504  0.999083;
+%!       0.594638  0.425302  0.562834  0.603537;
+%!       0.383594  0.291238  0.742073  0.085574;
+%!       0.265712  0.268003  0.783553  0.238409;
+%!       0.669966  0.743851  0.457255  0.445057 ];
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrdelete (Q, R, 3);
+%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 16*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps);
+%!
+%!test
+%! AA = [0.364554 + 0.993117i  0.669818 + 0.510234i  0.426568 + 0.041337i  0.847051 + 0.233291i;
+%!       0.049600 + 0.242783i  0.448946 + 0.484022i  0.141155 + 0.074420i  0.446746 + 0.392706i;
+%!       0.581922 + 0.657416i  0.581460 + 0.030016i  0.219909 + 0.447288i  0.201144 + 0.069132i;
+%!       0.694986 + 0.000571i  0.682327 + 0.841712i  0.807537 + 0.166086i  0.192767 + 0.358098i;
+%!       0.945002 + 0.066788i  0.350492 + 0.642638i  0.579629 + 0.048102i  0.600170 + 0.636938i ] * I;
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrdelete (Q, R, 3);
+%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 16*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps);
+%!
+%!test
+%! AA = [0.091364  0.613038  0.027504  0.999083;
+%!       0.594638  0.425302  0.562834  0.603537;
+%!       0.383594  0.291238  0.742073  0.085574;
+%!       0.265712  0.268003  0.783553  0.238409;
+%!       0.669966  0.743851  0.457255  0.445057 ];
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrdelete (Q, R, 3, "row");
+%! assert (norm (vec (Q'*Q - eye (4)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps);
+%!
+%!test
+%! AA = [0.364554 + 0.993117i  0.669818 + 0.510234i  0.426568 + 0.041337i  0.847051 + 0.233291i;
+%!       0.049600 + 0.242783i  0.448946 + 0.484022i  0.141155 + 0.074420i  0.446746 + 0.392706i;
+%!       0.581922 + 0.657416i  0.581460 + 0.030016i  0.219909 + 0.447288i  0.201144 + 0.069132i;
+%!       0.694986 + 0.000571i  0.682327 + 0.841712i  0.807537 + 0.166086i  0.192767 + 0.358098i;
+%!       0.945002 + 0.066788i  0.350492 + 0.642638i  0.579629 + 0.048102i  0.600170 + 0.636938i ] * I;
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrdelete (Q, R, 3, "row");
+%! assert (norm (vec (Q'*Q - eye (4)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps);
+
+%!test
+%! AA = single ([0.091364  0.613038  0.027504  0.999083;
+%!               0.594638  0.425302  0.562834  0.603537;
+%!               0.383594  0.291238  0.742073  0.085574;
+%!               0.265712  0.268003  0.783553  0.238409;
+%!               0.669966  0.743851  0.457255  0.445057 ]);
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrdelete (Q, R, 3);
+%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps ("single"));
+%!
+%!test
+%! AA = single ([0.364554 + 0.993117i  0.669818 + 0.510234i  0.426568 + 0.041337i  0.847051 + 0.233291i;
+%!               0.049600 + 0.242783i  0.448946 + 0.484022i  0.141155 + 0.074420i  0.446746 + 0.392706i;
+%!               0.581922 + 0.657416i  0.581460 + 0.030016i  0.219909 + 0.447288i  0.201144 + 0.069132i;
+%!               0.694986 + 0.000571i  0.682327 + 0.841712i  0.807537 + 0.166086i  0.192767 + 0.358098i;
+%!               0.945002 + 0.066788i  0.350492 + 0.642638i  0.579629 + 0.048102i  0.600170 + 0.636938i ]) * I;
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrdelete (Q, R, 3);
+%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps ("single"));
+%!
+%!test
+%! AA = single ([0.091364  0.613038  0.027504  0.999083;
+%!               0.594638  0.425302  0.562834  0.603537;
+%!               0.383594  0.291238  0.742073  0.085574;
+%!               0.265712  0.268003  0.783553  0.238409;
+%!               0.669966  0.743851  0.457255  0.445057 ]);
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrdelete (Q, R, 3, "row");
+%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1.5e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single"));
+%!testif HAVE_QRUPDATE
+%! ## Same test as above but with more precicision
+%! AA = single ([0.091364  0.613038  0.027504  0.999083;
+%!               0.594638  0.425302  0.562834  0.603537;
+%!               0.383594  0.291238  0.742073  0.085574;
+%!               0.265712  0.268003  0.783553  0.238409;
+%!               0.669966  0.743851  0.457255  0.445057 ]);
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrdelete (Q, R, 3, "row");
+%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single"));
+%!
+%!test
+%! AA = single ([0.364554 + 0.993117i  0.669818 + 0.510234i  0.426568 + 0.041337i  0.847051 + 0.233291i;
+%!              0.049600 + 0.242783i  0.448946 + 0.484022i  0.141155 + 0.074420i  0.446746 + 0.392706i;
+%!              0.581922 + 0.657416i  0.581460 + 0.030016i  0.219909 + 0.447288i  0.201144 + 0.069132i;
+%!              0.694986 + 0.000571i  0.682327 + 0.841712i  0.807537 + 0.166086i  0.192767 + 0.358098i;
+%!              0.945002 + 0.066788i  0.350492 + 0.642638i  0.579629 + 0.048102i  0.600170 + 0.636938i ]) * I;
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrdelete (Q, R, 3, "row");
+%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single"));
+*/
+
+DEFUN (qrshift, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {[@var{Q1}, @var{R1}] =} qrshift (@var{Q}, @var{R}, @var{i}, @var{j})
+Update a QR factorization given a range of columns to shift in the original
+factored matrix.
+
+Given a QR@tie{}factorization of a real or complex matrix
+@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and
+@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization
+of @w{@var{A}(:,p)}, where @w{p} is the permutation @*
+@code{p = [1:i-1, shift(i:j, 1), j+1:n]} if @w{@var{i} < @var{j}} @*
+ or @*
+@code{p = [1:j-1, shift(j:i,-1), i+1:n]} if @w{@var{j} < @var{i}}.  @*
+
+@seealso{qr, qrupdate, qrinsert, qrdelete}
+@end deftypefn */)
+{
+  if (args.length () != 4)
+    print_usage ();
+
+  octave_value argq = args(0);
+  octave_value argr = args(1);
+  octave_value argi = args(2);
+  octave_value argj = args(3);
+
+  if (! argq.isnumeric () || ! argr.isnumeric ())
+    print_usage ();
+
+  if (! check_qr_dims (argq, argr, true))
+    error ("qrshift: dimensions mismatch");
+
+  octave_idx_type i = argi.idx_type_value ();
+  octave_idx_type j = argj.idx_type_value ();
+
+  if (! check_index (argi) || ! check_index (argj))
+    error ("qrshift: invalid index I or J");
+
+  octave_value_list retval;
+
+  if (argq.isreal () && argr.isreal ())
+    {
+      // all real case
+      if (argq.is_single_type ()
+          && argr.is_single_type ())
+        {
+          FloatMatrix Q = argq.float_matrix_value ();
+          FloatMatrix R = argr.float_matrix_value ();
+
+          octave::math::qr<FloatMatrix> fact (Q, R);
+          fact.shift_cols (i-1, j-1);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+      else
+        {
+          Matrix Q = argq.matrix_value ();
+          Matrix R = argr.matrix_value ();
+
+          octave::math::qr<Matrix> fact (Q, R);
+          fact.shift_cols (i-1, j-1);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+    }
+  else
+    {
+      // complex case
+      if (argq.is_single_type ()
+          && argr.is_single_type ())
+        {
+          FloatComplexMatrix Q = argq.float_complex_matrix_value ();
+          FloatComplexMatrix R = argr.float_complex_matrix_value ();
+
+          octave::math::qr<FloatComplexMatrix> fact (Q, R);
+          fact.shift_cols (i-1, j-1);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+      else
+        {
+          ComplexMatrix Q = argq.complex_matrix_value ();
+          ComplexMatrix R = argr.complex_matrix_value ();
+
+          octave::math::qr<ComplexMatrix> fact (Q, R);
+          fact.shift_cols (i-1, j-1);
+
+          retval = ovl (fact.Q (), get_qr_r (fact));
+        }
+    }
+
+  return retval;
+}
+
+/*
+%!test
+%! AA = A.';
+%! i = 2;  j = 4;  p = [1:i-1, shift(i:j,-1), j+1:5];
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrshift (Q, R, i, j);
+%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps);
+%!
+%! j = 2;  i = 4;  p = [1:j-1, shift(j:i,+1), i+1:5];
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrshift (Q, R, i, j);
+%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps);
+%!
+%!test
+%! AA = Ac.';
+%! i = 2;  j = 4;  p = [1:i-1, shift(i:j,-1), j+1:5];
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrshift (Q, R, i, j);
+%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps);
+%!
+%! j = 2;  i = 4;  p = [1:j-1, shift(j:i,+1), i+1:5];
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrshift (Q, R, i, j);
+%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps);
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps);
+
+%!test
+%! AA = single (A).';
+%! i = 2;  j = 4;  p = [1:i-1, shift(i:j,-1), j+1:5];
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrshift (Q, R, i, j);
+%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single"));
+%!
+%! j = 2;  i = 4;  p = [1:j-1, shift(j:i,+1), i+1:5];
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrshift (Q, R, i, j);
+%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single"));
+%!
+%!test
+%! AA = single (Ac).';
+%! i = 2;  j = 4;  p = [1:i-1, shift(i:j,-1), j+1:5];
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrshift (Q, R, i, j);
+%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single"));
+%!
+%! j = 2;  i = 4;  p = [1:j-1, shift(j:i,+1), i+1:5];
+%!
+%! [Q,R] = qr (AA);
+%! [Q,R] = qrshift (Q, R, i, j);
+%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single"));
+%! assert (norm (vec (triu (R) - R), Inf) == 0);
+%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single"));
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/symbfact.cc	Wed Jan 29 06:30:40 2020 -0500
@@ -0,0 +1,428 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 1998-2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <cmath>
+
+#include <algorithm>
+#include <string>
+
+#include "CSparse.h"
+#include "boolSparse.h"
+#include "dColVector.h"
+#include "dSparse.h"
+#include "oct-locbuf.h"
+#include "oct-sparse.h"
+#include "oct-spparms.h"
+#include "sparse-util.h"
+
+#include "defun.h"
+#include "error.h"
+#include "errwarn.h"
+#include "ovl.h"
+#include "parse.h"
+#include "utils.h"
+
+DEFUN (symbfact, args, nargout,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {[@var{count}, @var{h}, @var{parent}, @var{post}, @var{R}] =} symbfact (@var{S})
+@deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ})
+@deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ}, @var{mode})
+
+Perform a symbolic factorization analysis of the sparse matrix @var{S}.
+
+The input variables are
+
+@table @var
+@item S
+@var{S} is a real or complex sparse matrix.
+
+@item typ
+Is the type of the factorization and can be one of
+
+@table @asis
+@item @qcode{"sym"} (default)
+Factorize @var{S}.  Assumes @var{S} is symmetric and uses the upper
+triangular portion of the matrix.
+
+@item @qcode{"col"}
+Factorize @tcode{@var{S}' * @var{S}}.
+
+@item @qcode{"row"}
+Factorize @tcode{@var{S} * @var{S}'}.
+
+@item @qcode{"lo"}
+Factorize @tcode{@var{S}'}.  Assumes @var{S} is symmetric and uses the lower
+triangular portion of the matrix.
+@end table
+
+@item mode
+When @var{mode} is unspecified return the Cholesky@tie{}factorization for
+@var{R}.  If @var{mode} is @qcode{"lower"} or @qcode{"L"} then return
+the conjugate transpose @tcode{@var{R}'} which is a lower triangular factor.
+The conjugate transpose version is faster and uses less memory, but still
+returns the same values for all other outputs: @var{count}, @var{h},
+@var{parent}, and @var{post}.
+@end table
+
+The output variables are:
+
+@table @var
+@item count
+The row counts of the Cholesky@tie{}factorization as determined by
+@var{typ}.  The computational difficulty of performing the true
+factorization using @code{chol} is @code{sum (@var{count} .^ 2)}.
+
+@item h
+The height of the elimination tree.
+
+@item parent
+The elimination tree itself.
+
+@item post
+A sparse boolean matrix whose structure is that of the
+Cholesky@tie{}factorization as determined by @var{typ}.
+@end table
+@seealso{chol, etree, treelayout}
+@end deftypefn */)
+{
+#if defined (HAVE_CHOLMOD)
+
+  int nargin = args.length ();
+
+  if (nargin < 1 || nargin > 3)
+    print_usage ();
+
+  octave_value_list retval;
+
+  double dummy;
+  cholmod_sparse Astore;
+  cholmod_sparse *A = &Astore;
+  A->packed = true;
+  A->sorted = true;
+  A->nz = nullptr;
+#if defined (OCTAVE_ENABLE_64)
+  A->itype = CHOLMOD_LONG;
+#else
+  A->itype = CHOLMOD_INT;
+#endif
+  A->dtype = CHOLMOD_DOUBLE;
+  A->stype = 1;
+  A->x = &dummy;
+
+  if (args(0).isreal ())
+    {
+      const SparseMatrix a = args(0).sparse_matrix_value ();
+      A->nrow = a.rows ();
+      A->ncol = a.cols ();
+      A->p = a.cidx ();
+      A->i = a.ridx ();
+      A->nzmax = a.nnz ();
+      A->xtype = CHOLMOD_REAL;
+
+      if (a.rows () > 0 && a.cols () > 0)
+        A->x = a.data ();
+    }
+  else if (args(0).iscomplex ())
+    {
+      const SparseComplexMatrix a = args(0).sparse_complex_matrix_value ();
+      A->nrow = a.rows ();
+      A->ncol = a.cols ();
+      A->p = a.cidx ();
+      A->i = a.ridx ();
+      A->nzmax = a.nnz ();
+      A->xtype = CHOLMOD_COMPLEX;
+
+      if (a.rows () > 0 && a.cols () > 0)
+        A->x = a.data ();
+    }
+  else
+    err_wrong_type_arg ("symbfact", args(0));
+
+  bool coletree = false;
+  octave_idx_type n = A->nrow;
+
+  if (nargin > 1)
+    {
+      std::string str = args(1).xstring_value ("TYP must be a string");
+      // FIXME: The input validation could be improved to use strncmp
+      char ch;
+      ch = tolower (str[0]);
+      if (ch == 'r')          // 'row'
+        A->stype = 0;
+      else if (ch == 'c')     // 'col'
+        {
+          n = A->ncol;
+          coletree = true;
+          A->stype = 0;
+        }
+      else if (ch == 's')     // 'sym' (default)
+        A->stype = 1;
+      else if (ch == 'l')     // 'lo'
+        A->stype = -1;
+      else
+        error (R"(symbfact: unrecognized TYP "%s")", str.c_str ());
+    }
+
+  if (nargin == 3)
+    {
+      std::string str = args(2).xstring_value ("MODE must be a string");
+      // FIXME: The input validation could be improved to use strncmp
+      char ch;
+      ch = toupper (str[0]);
+      if (ch != 'L')
+        error (R"(symbfact: unrecognized MODE "%s")", str.c_str ());
+    }
+
+  if (A->stype && A->nrow != A->ncol)
+    err_square_matrix_required ("symbfact", "S");
+
+  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, Parent, n);
+  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, Post, n);
+  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, ColCount, n);
+  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, First, n);
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, Level, n);
+
+  cholmod_common Common;
+  cholmod_common *cm = &Common;
+  CHOLMOD_NAME(start) (cm);
+
+  double spu = octave_sparse_params::get_key ("spumoni");
+  if (spu == 0.0)
+    {
+      cm->print = -1;
+      SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, nullptr);
+    }
+  else
+    {
+      cm->print = static_cast<int> (spu) + 2;
+      SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, &SparseCholPrint);
+    }
+
+  cm->error_handler = &SparseCholError;
+  SUITESPARSE_ASSIGN_FPTR2 (divcomplex_func, cm->complex_divide, divcomplex);
+  SUITESPARSE_ASSIGN_FPTR2 (hypot_func, cm->hypotenuse, hypot);
+
+  cholmod_sparse *F = CHOLMOD_NAME(transpose) (A, 0, cm);
+  cholmod_sparse *Aup, *Alo;
+
+  if (A->stype == 1 || coletree)
+    {
+      Aup = A;
+      Alo = F;
+    }
+  else
+    {
+      Aup = F;
+      Alo = A;
+    }
+
+  CHOLMOD_NAME(etree) (Aup, Parent, cm);
+
+  ColumnVector tmp (n);    // Declaration must precede any goto cleanup.
+  std::string err_msg;
+
+  if (cm->status < CHOLMOD_OK)
+    {
+      err_msg = "symbfact: matrix corrupted";
+      goto cleanup;
+    }
+
+  if (CHOLMOD_NAME(postorder) (Parent, n, nullptr, Post, cm) != n)
+    {
+      err_msg = "symbfact: postorder failed";
+      goto cleanup;
+    }
+
+  CHOLMOD_NAME(rowcolcounts) (Alo, nullptr, 0, Parent, Post, nullptr, ColCount,
+                              First, octave::to_suitesparse_intptr (Level), cm);
+
+  if (cm->status < CHOLMOD_OK)
+    {
+      err_msg = "symbfact: matrix corrupted";
+      goto cleanup;
+    }
+
+  if (nargout > 4)
+    {
+      cholmod_sparse *A1, *A2;
+
+      if (A->stype == 1)
+        {
+          A1 = A;
+          A2 = nullptr;
+        }
+      else if (A->stype == -1)
+        {
+          A1 = F;
+          A2 = nullptr;
+        }
+      else if (coletree)
+        {
+          A1 = F;
+          A2 = A;
+        }
+      else
+        {
+          A1 = A;
+          A2 = F;
+        }
+
+      // count the total number of entries in L
+      octave_idx_type lnz = 0;
+      for (octave_idx_type j = 0 ; j < n ; j++)
+        lnz += ColCount[j];
+
+      // allocate the output matrix L (pattern-only)
+      SparseBoolMatrix L (dim_vector (n, n), lnz);
+
+      // initialize column pointers
+      lnz = 0;
+      for (octave_idx_type j = 0 ; j < n ; j++)
+        {
+          L.xcidx(j) = lnz;
+          lnz += ColCount[j];
+        }
+      L.xcidx(n) = lnz;
+
+      // create a copy of the column pointers
+      octave::suitesparse_integer *W = First;
+      for (octave_idx_type j = 0 ; j < n ; j++)
+        W[j] = L.xcidx (j);
+
+      // get workspace for computing one row of L
+      cholmod_sparse *R
+        = CHOLMOD_NAME(allocate_sparse) (n, 1, n, false, true,
+                                         0, CHOLMOD_PATTERN, cm);
+      octave_idx_type *Rp = static_cast<octave_idx_type *> (R->p);
+      octave_idx_type *Ri = static_cast<octave_idx_type *> (R->i);
+
+      // compute L one row at a time
+      for (octave_idx_type k = 0 ; k < n ; k++)
+        {
+          // get the kth row of L and store in the columns of L
+          CHOLMOD_NAME(row_subtree) (A1, A2, k, Parent, R, cm);
+          for (octave_idx_type p = 0 ; p < Rp[1] ; p++)
+            L.xridx (W[Ri[p]]++) = k;
+
+          // add the diagonal entry
+          L.xridx (W[k]++) = k;
+        }
+
+      // free workspace
+      CHOLMOD_NAME(free_sparse) (&R, cm);
+
+      // fill L with one's
+      std::fill_n (L.xdata (), lnz, true);
+
+      // transpose L to get R, or leave as is
+      if (nargin < 3)
+        L = L.transpose ();
+
+      retval(4) = L;
+    }
+
+  if (nargout > 3)
+    {
+      for (octave_idx_type i = 0; i < n; i++)
+        tmp(i) = Post[i] + 1;
+      retval(3) = tmp;
+    }
+
+  if (nargout > 2)
+    {
+      for (octave_idx_type i = 0; i < n; i++)
+        tmp(i) = Parent[i] + 1;
+      retval(2) = tmp;
+    }
+
+  if (nargout > 1)
+    {
+      // compute the elimination tree height
+      octave_idx_type height = 0;
+      for (int i = 0 ; i < n ; i++)
+        height = std::max (height, Level[i]);
+      height++;
+      retval(1) = static_cast<double> (height);
+    }
+
+  for (octave_idx_type i = 0; i < n; i++)
+    tmp(i) = ColCount[i];
+  retval(0) = tmp;
+
+cleanup:
+  CHOLMOD_NAME(free_sparse) (&F, cm);
+  CHOLMOD_NAME(finish) (cm);
+
+  if (! err_msg.empty ())
+    error ("%s", err_msg.c_str ());
+
+  return retval;
+
+#else
+
+  octave_unused_parameter (args);
+  octave_unused_parameter (nargout);
+
+  err_disabled_feature ("symbfact", "CHOLMOD");
+
+#endif
+}
+
+/*
+%!testif HAVE_CHOLMOD
+%! A = sparse (magic (3));
+%! [count, h, parent, post, r] = symbfact (A);
+%! assert (count, [3; 2; 1]);
+%! assert (h, 3);
+%! assert (parent, [2; 3; 0]);
+%! assert (r, sparse (triu (true (3))));
+
+%!testif HAVE_CHOLMOD
+%! ## Test MODE "lower"
+%! A = sparse (magic (3));
+%! [~, ~, ~, ~, l] = symbfact (A, "sym", "lower");
+%! assert (l, sparse (tril (true (3))));
+
+%!testif HAVE_CHOLMOD <*42587>
+%! ## singular matrix
+%! A = sparse ([1 0 8;0 1 8;8 8 1]);
+%! [count, h, parent, post, r] = symbfact (A);
+
+## Test input validation
+%!testif HAVE_CHOLMOD
+%! fail ("symbfact ()");
+%! fail ("symbfact (1,2,3,4)");
+%! fail ("symbfact ({1})", "wrong type argument 'cell'");
+%! fail ("symbfact (sparse (1), {1})", "TYP must be a string");
+%! fail ("symbfact (sparse (1), 'foobar')", 'unrecognized TYP "foobar"');
+%! fail ("symbfact (sparse (1), 'sym', {'L'})", "MODE must be a string");
+%! fail ('symbfact (sparse (1), "sym", "foobar")', 'unrecognized MODE "foobar"');
+%! fail ("symbfact (sparse ([1, 2; 3, 4; 5, 6]))", "S must be a square matrix");
+
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/symrcm.cc	Wed Jan 29 06:30:40 2020 -0500
@@ -0,0 +1,705 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2007-2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+/*
+An implementation of the Reverse Cuthill-McKee algorithm (symrcm)
+
+The implementation of this algorithm is based in the descriptions found in
+
+@INPROCEEDINGS{,
+        author = {E. Cuthill and J. McKee},
+        title = {Reducing the Bandwidth of Sparse Symmetric Matrices},
+        booktitle = {Proceedings of the 24th ACM National Conference},
+        publisher = {Brandon Press},
+        pages = {157 -- 172},
+        location = {New Jersey},
+        year = {1969}
+}
+
+@BOOK{,
+        author = {Alan George and Joseph W. H. Liu},
+        title = {Computer Solution of Large Sparse Positive Definite Systems},
+        publisher = {Prentice Hall Series in Computational Mathematics},
+        ISBN = {0-13-165274-5},
+        year = {1981}
+}
+
+The algorithm represents a heuristic approach to the NP-complete minimum
+bandwidth problem.
+
+Written by Michael Weitzel <michael.weitzel@@uni-siegen.de>
+                           <weitzel@@ldknet.org>
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <algorithm>
+
+#include "CSparse.h"
+#include "boolNDArray.h"
+#include "dNDArray.h"
+#include "dSparse.h"
+#include "oct-locbuf.h"
+#include "oct-sparse.h"
+#include "quit.h"
+
+#include "defun.h"
+#include "errwarn.h"
+#include "ov.h"
+#include "ovl.h"
+
+// A node struct for the Cuthill-McKee algorithm
+struct CMK_Node
+{
+  // the node's id (matrix row index)
+  octave_idx_type id;
+  // the node's degree
+  octave_idx_type deg;
+  // minimal distance to the root of the spanning tree
+  octave_idx_type dist;
+};
+
+// A simple queue.
+// Queues Q have a fixed maximum size N (rows,cols of the matrix) and are
+// stored in an array.  qh and qt point to queue head and tail.
+
+// Enqueue operation (adds a node "o" at the tail)
+
+inline static void
+Q_enq (CMK_Node *Q, octave_idx_type N, octave_idx_type& qt, const CMK_Node& o)
+{
+  Q[qt] = o;
+  qt = (qt + 1) % (N + 1);
+}
+
+// Dequeue operation (removes a node from the head)
+
+inline static CMK_Node
+Q_deq (CMK_Node * Q, octave_idx_type N, octave_idx_type& qh)
+{
+  CMK_Node r = Q[qh];
+  qh = (qh + 1) % (N + 1);
+  return r;
+}
+
+// Predicate (queue empty)
+#define Q_empty(Q, N, qh, qt)   ((qh) == (qt))
+
+// A simple, array-based binary heap (used as a priority queue for nodes)
+
+// the left descendant of entry i
+#define LEFT(i)         (((i) << 1) + 1)        // = (2*(i)+1)
+// the right descendant of entry i
+#define RIGHT(i)        (((i) << 1) + 2)        // = (2*(i)+2)
+// the parent of entry i
+#define PARENT(i)       (((i) - 1) >> 1)        // = floor(((i)-1)/2)
+
+// Builds a min-heap (the root contains the smallest element).  A is an array
+// with the graph's nodes, i is a starting position, size is the length of A.
+
+static void
+H_heapify_min (CMK_Node *A, octave_idx_type i, octave_idx_type size)
+{
+  octave_idx_type j = i;
+  for (;;)
+    {
+      octave_idx_type l = LEFT(j);
+      octave_idx_type r = RIGHT(j);
+
+      octave_idx_type smallest;
+      if (l < size && A[l].deg < A[j].deg)
+        smallest = l;
+      else
+        smallest = j;
+
+      if (r < size && A[r].deg < A[smallest].deg)
+        smallest = r;
+
+      if (smallest != j)
+        {
+          std::swap (A[j], A[smallest]);
+          j = smallest;
+        }
+      else
+        break;
+    }
+}
+
+// Heap operation insert.  Running time is O(log(n))
+
+static void
+H_insert (CMK_Node *H, octave_idx_type& h, const CMK_Node& o)
+{
+  octave_idx_type i = h++;
+
+  H[i] = o;
+
+  if (i == 0)
+    return;
+  do
+    {
+      octave_idx_type p = PARENT(i);
+      if (H[i].deg < H[p].deg)
+        {
+          std::swap (H[i], H[p]);
+
+          i = p;
+        }
+      else
+        break;
+    }
+  while (i > 0);
+}
+
+// Heap operation remove-min.  Removes the smallest element in O(1) and
+// reorganizes the heap optionally in O(log(n))
+
+inline static CMK_Node
+H_remove_min (CMK_Node *H, octave_idx_type& h, int reorg/*=1*/)
+{
+  CMK_Node r = H[0];
+  H[0] = H[--h];
+  if (reorg)
+    H_heapify_min (H, 0, h);
+  return r;
+}
+
+// Predicate (heap empty)
+#define H_empty(H, h)   ((h) == 0)
+
+// Helper function for the Cuthill-McKee algorithm.  Tries to determine a
+// pseudo-peripheral node of the graph as starting node.
+
+static octave_idx_type
+find_starting_node (octave_idx_type N, const octave_idx_type *ridx,
+                    const octave_idx_type *cidx, const octave_idx_type *ridx2,
+                    const octave_idx_type *cidx2, octave_idx_type *D,
+                    octave_idx_type start)
+{
+  CMK_Node w;
+
+  OCTAVE_LOCAL_BUFFER (CMK_Node, Q, N+1);
+  boolNDArray btmp (dim_vector (1, N), false);
+  bool *visit = btmp.fortran_vec ();
+
+  octave_idx_type qh = 0;
+  octave_idx_type qt = 0;
+  CMK_Node x;
+  x.id = start;
+  x.deg = D[start];
+  x.dist = 0;
+  Q_enq (Q, N, qt, x);
+  visit[start] = true;
+
+  // distance level
+  octave_idx_type level = 0;
+  // current largest "eccentricity"
+  octave_idx_type max_dist = 0;
+
+  for (;;)
+    {
+      while (! Q_empty (Q, N, qh, qt))
+        {
+          CMK_Node v = Q_deq (Q, N, qh);
+
+          if (v.dist > x.dist || (v.id != x.id && v.deg > x.deg))
+            x = v;
+
+          octave_idx_type i = v.id;
+
+          // add all unvisited neighbors to the queue
+          octave_idx_type j1 = cidx[i];
+          octave_idx_type j2 = cidx2[i];
+          while (j1 < cidx[i+1] || j2 < cidx2[i+1])
+            {
+              octave_quit ();
+
+              if (j1 == cidx[i+1])
+                {
+                  octave_idx_type r2 = ridx2[j2++];
+                  if (! visit[r2])
+                    {
+                      // the distance of node j is dist(i)+1
+                      w.id = r2;
+                      w.deg = D[r2];
+                      w.dist = v.dist+1;
+                      Q_enq (Q, N, qt, w);
+                      visit[r2] = true;
+
+                      if (w.dist > level)
+                        level = w.dist;
+                    }
+                }
+              else if (j2 == cidx2[i+1])
+                {
+                  octave_idx_type r1 = ridx[j1++];
+                  if (! visit[r1])
+                    {
+                      // the distance of node j is dist(i)+1
+                      w.id = r1;
+                      w.deg = D[r1];
+                      w.dist = v.dist+1;
+                      Q_enq (Q, N, qt, w);
+                      visit[r1] = true;
+
+                      if (w.dist > level)
+                        level = w.dist;
+                    }
+                }
+              else
+                {
+                  octave_idx_type r1 = ridx[j1];
+                  octave_idx_type r2 = ridx2[j2];
+                  if (r1 <= r2)
+                    {
+                      if (! visit[r1])
+                        {
+                          w.id = r1;
+                          w.deg = D[r1];
+                          w.dist = v.dist+1;
+                          Q_enq (Q, N, qt, w);
+                          visit[r1] = true;
+
+                          if (w.dist > level)
+                            level = w.dist;
+                        }
+                      j1++;
+                      if (r1 == r2)
+                        j2++;
+                    }
+                  else
+                    {
+                      if (! visit[r2])
+                        {
+                          w.id = r2;
+                          w.deg = D[r2];
+                          w.dist = v.dist+1;
+                          Q_enq (Q, N, qt, w);
+                          visit[r2] = true;
+
+                          if (w.dist > level)
+                            level = w.dist;
+                        }
+                      j2++;
+                    }
+                }
+            }
+        } // finish of BFS
+
+      if (max_dist < x.dist)
+        {
+          max_dist = x.dist;
+
+          for (octave_idx_type i = 0; i < N; i++)
+            visit[i] = false;
+
+          visit[x.id] = true;
+          x.dist = 0;
+          qt = qh = 0;
+          Q_enq (Q, N, qt, x);
+        }
+      else
+        break;
+    }
+  return x.id;
+}
+
+// Calculates the node's degrees.  This means counting the nonzero elements
+// in the symmetric matrix' rows.  This works for non-symmetric matrices
+// as well.
+
+static octave_idx_type
+calc_degrees (octave_idx_type N, const octave_idx_type *ridx,
+              const octave_idx_type *cidx, octave_idx_type *D)
+{
+  octave_idx_type max_deg = 0;
+
+  for (octave_idx_type i = 0; i < N; i++)
+    D[i] = 0;
+
+  for (octave_idx_type j = 0; j < N; j++)
+    {
+      for (octave_idx_type i = cidx[j]; i < cidx[j+1]; i++)
+        {
+          octave_quit ();
+
+          octave_idx_type k = ridx[i];
+          // there is a nonzero element (k,j)
+          D[k]++;
+          if (D[k] > max_deg)
+            max_deg = D[k];
+          // if there is no element (j,k) there is one in
+          // the symmetric matrix:
+          if (k != j)
+            {
+              bool found = false;
+              for (octave_idx_type l = cidx[k]; l < cidx[k + 1]; l++)
+                {
+                  octave_quit ();
+
+                  if (ridx[l] == j)
+                    {
+                      found = true;
+                      break;
+                    }
+                  else if (ridx[l] > j)
+                    break;
+                }
+
+              if (! found)
+                {
+                  // A(j,k) == 0
+                  D[j]++;
+                  if (D[j] > max_deg)
+                    max_deg = D[j];
+                }
+            }
+        }
+    }
+  return max_deg;
+}
+
+// Transpose of the structure of a square sparse matrix
+
+static void
+transpose (octave_idx_type N, const octave_idx_type *ridx,
+           const octave_idx_type *cidx, octave_idx_type *ridx2,
+           octave_idx_type *cidx2)
+{
+  octave_idx_type nz = cidx[N];
+
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, w, N + 1);
+  for (octave_idx_type i = 0; i < N; i++)
+    w[i] = 0;
+  for (octave_idx_type i = 0; i < nz; i++)
+    w[ridx[i]]++;
+  nz = 0;
+  for (octave_idx_type i = 0; i < N; i++)
+    {
+      octave_quit ();
+
+      cidx2[i] = nz;
+      nz += w[i];
+      w[i] = cidx2[i];
+    }
+  cidx2[N] = nz;
+  w[N] = nz;
+
+  for (octave_idx_type j = 0; j < N; j++)
+    for (octave_idx_type k = cidx[j]; k < cidx[j + 1]; k++)
+      {
+        octave_quit ();
+
+        octave_idx_type q = w[ridx[k]]++;
+        ridx2[q] = j;
+      }
+}
+
+// An implementation of the Cuthill-McKee algorithm.
+DEFUN (symrcm, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {@var{p} =} symrcm (@var{S})
+Return the symmetric reverse @nospell{Cuthill-McKee} permutation of @var{S}.
+
+@var{p} is a permutation vector such that
+@code{@var{S}(@var{p}, @var{p})} tends to have its diagonal elements closer
+to the diagonal than @var{S}.  This is a good preordering for LU or
+Cholesky@tie{}factorization of matrices that come from ``long, skinny''
+problems.  It works for both symmetric and asymmetric @var{S}.
+
+The algorithm represents a heuristic approach to the NP-complete bandwidth
+minimization problem.  The implementation is based in the descriptions found
+in
+
+@nospell{E. Cuthill, J. McKee}.
+@cite{Reducing the Bandwidth of Sparse Symmetric Matrices}.
+Proceedings of the 24th @nospell{ACM} National Conference,
+157--172 1969, Brandon Press, New Jersey.
+
+@nospell{A. George, J.W.H. Liu}.  @cite{Computer Solution of Large Sparse
+Positive Definite Systems}, Prentice Hall Series in Computational
+Mathematics, ISBN 0-13-165274-5, 1981.
+
+@seealso{colperm, colamd, symamd}
+@end deftypefn */)
+{
+  if (args.length () != 1)
+    print_usage ();
+
+  octave_value arg = args(0);
+
+  // the parameter of the matrix is converted into a sparse matrix
+  //(if necessary)
+  octave_idx_type *cidx;
+  octave_idx_type *ridx;
+  SparseMatrix Ar;
+  SparseComplexMatrix Ac;
+
+  if (arg.isreal ())
+    {
+      Ar = arg.sparse_matrix_value ();
+      // Note cidx/ridx are const, so use xridx and xcidx...
+      cidx = Ar.xcidx ();
+      ridx = Ar.xridx ();
+    }
+  else
+    {
+      Ac = arg.sparse_complex_matrix_value ();
+      cidx = Ac.xcidx ();
+      ridx = Ac.xridx ();
+    }
+
+  octave_idx_type nr = arg.rows ();
+  octave_idx_type nc = arg.columns ();
+
+  if (nr != nc)
+    err_square_matrix_required ("symrcm", "S");
+
+  if (nr == 0 && nc == 0)
+    return ovl (NDArray (dim_vector (1, 0)));
+
+  // sizes of the heaps
+  octave_idx_type s = 0;
+
+  // head- and tail-indices for the queue
+  octave_idx_type qt = 0;
+  octave_idx_type qh = 0;
+  CMK_Node v, w;
+  // dimension of the matrix
+  octave_idx_type N = nr;
+
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, cidx2, N + 1);
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, ridx2, cidx[N]);
+  transpose (N, ridx, cidx, ridx2, cidx2);
+
+  // the permutation vector
+  NDArray P (dim_vector (1, N));
+
+  // compute the node degrees
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, D, N);
+  octave_idx_type max_deg = calc_degrees (N, ridx, cidx, D);
+
+  // if none of the nodes has a degree > 0 (a matrix of zeros)
+  // the return value corresponds to the identity permutation
+  if (max_deg == 0)
+    {
+      for (octave_idx_type i = 0; i < N; i++)
+        P(i) = i;
+
+      return ovl (P);
+    }
+
+  // a heap for the a node's neighbors.  The number of neighbors is
+  // limited by the maximum degree max_deg:
+  OCTAVE_LOCAL_BUFFER (CMK_Node, S, max_deg);
+
+  // a queue for the BFS.  The array is always one element larger than
+  // the number of entries that are stored.
+  OCTAVE_LOCAL_BUFFER (CMK_Node, Q, N+1);
+
+  // a counter (for building the permutation)
+  octave_idx_type c = -1;
+
+  // upper bound for the bandwidth (=quality of solution)
+  // initialize the bandwidth of the graph with 0.  B contains the
+  // the maximum of the theoretical lower limits of the subgraphs
+  // bandwidths.
+  octave_idx_type B = 0;
+
+  // mark all nodes as unvisited; with the exception of the nodes
+  // that have degree==0 and build a CC of the graph.
+
+  boolNDArray btmp (dim_vector (1, N), false);
+  bool *visit = btmp.fortran_vec ();
+
+  do
+    {
+      // locate an unvisited starting node of the graph
+      octave_idx_type i;
+      for (i = 0; i < N; i++)
+        if (! visit[i])
+          break;
+
+      // locate a probably better starting node
+      v.id = find_starting_node (N, ridx, cidx, ridx2, cidx2, D, i);
+
+      // mark the node as visited and enqueue it (a starting node
+      // for the BFS).  Since the node will be a root of a spanning
+      // tree, its dist is 0.
+      v.deg = D[v.id];
+      v.dist = 0;
+      visit[v.id] = true;
+      Q_enq (Q, N, qt, v);
+
+      // lower bound for the bandwidth of a subgraph
+      // keep a "level" in the spanning tree (= min. distance to the
+      // root) for determining the bandwidth of the computed
+      // permutation P
+      octave_idx_type Bsub = 0;
+      // min. dist. to the root is 0
+      octave_idx_type level = 0;
+      // the root is the first/only node on level 0
+      octave_idx_type level_N = 1;
+
+      while (! Q_empty (Q, N, qh, qt))
+        {
+          v = Q_deq (Q, N, qh);
+          i = v.id;
+
+          c++;
+
+          // for computing the inverse permutation P where
+          // A(inv(P),inv(P)) or P'*A*P is banded
+          //         P(i) = c;
+
+          // for computing permutation P where
+          // A(P(i),P(j)) or P*A*P' is banded
+          P(c) = i;
+
+          // put all unvisited neighbors j of node i on the heap
+          s = 0;
+          octave_idx_type j1 = cidx[i];
+          octave_idx_type j2 = cidx2[i];
+
+          octave_quit ();
+
+          while (j1 < cidx[i+1] || j2 < cidx2[i+1])
+            {
+              octave_quit ();
+
+              if (j1 == cidx[i+1])
+                {
+                  octave_idx_type r2 = ridx2[j2++];
+                  if (! visit[r2])
+                    {
+                      // the distance of node j is dist(i)+1
+                      w.id = r2;
+                      w.deg = D[r2];
+                      w.dist = v.dist+1;
+                      H_insert (S, s, w);
+                      visit[r2] = true;
+                    }
+                }
+              else if (j2 == cidx2[i+1])
+                {
+                  octave_idx_type r1 = ridx[j1++];
+                  if (! visit[r1])
+                    {
+                      w.id = r1;
+                      w.deg = D[r1];
+                      w.dist = v.dist+1;
+                      H_insert (S, s, w);
+                      visit[r1] = true;
+                    }
+                }
+              else
+                {
+                  octave_idx_type r1 = ridx[j1];
+                  octave_idx_type r2 = ridx2[j2];
+                  if (r1 <= r2)
+                    {
+                      if (! visit[r1])
+                        {
+                          w.id = r1;
+                          w.deg = D[r1];
+                          w.dist = v.dist+1;
+                          H_insert (S, s, w);
+                          visit[r1] = true;
+                        }
+                      j1++;
+                      if (r1 == r2)
+                        j2++;
+                    }
+                  else
+                    {
+                      if (! visit[r2])
+                        {
+                          w.id = r2;
+                          w.deg = D[r2];
+                          w.dist = v.dist+1;
+                          H_insert (S, s, w);
+                          visit[r2] = true;
+                        }
+                      j2++;
+                    }
+                }
+            }
+
+          // add the neighbors to the queue (sorted by node degree)
+          while (! H_empty (S, s))
+            {
+              octave_quit ();
+
+              // locate a neighbor of i with minimal degree in O(log(N))
+              v = H_remove_min (S, s, 1);
+
+              // entered the BFS a new level?
+              if (v.dist > level)
+                {
+                  // adjustment of bandwidth:
+                  // "[...] the minimum bandwidth that
+                  // can be obtained [...] is the
+                  //  maximum number of nodes per level"
+                  if (Bsub < level_N)
+                    Bsub = level_N;
+
+                  level = v.dist;
+                  // v is the first node on the new level
+                  level_N = 1;
+                }
+              else
+                {
+                  // there is no new level but another node on
+                  // this level:
+                  level_N++;
+                }
+
+              // enqueue v in O(1)
+              Q_enq (Q, N, qt, v);
+            }
+
+          // synchronize the bandwidth with level_N once again:
+          if (Bsub < level_N)
+            Bsub = level_N;
+        }
+      // finish of BFS.  If there are still unvisited nodes in the graph
+      // then it is split into CCs.  The computed bandwidth is the maximum
+      // of all subgraphs.  Update:
+      if (Bsub > B)
+        B = Bsub;
+    }
+  // are there any nodes left?
+  while (c+1 < N);
+
+  // compute the reverse-ordering
+  s = N / 2 - 1;
+  for (octave_idx_type i = 0, j = N - 1; i <= s; i++, j--)
+    std::swap (P.elem (i), P.elem (j));
+
+  // increment all indices, since Octave is not C
+  return ovl (P+1);
+}
--- a/libinterp/corefcn/variables.cc	Sat Jan 25 15:26:07 2020 +0100
+++ b/libinterp/corefcn/variables.cc	Wed Jan 29 06:30:40 2020 -0500
@@ -492,11 +492,10 @@
 %! end_unwind_protect
 %! assert (exist (fullfile (pwd (), "%nonexistentfile%"), "file"), 0);
 
-%!testif HAVE_CHOLMOD
-%! assert (exist ("chol"), 3);
-%! assert (exist ("chol.oct"), 3);
-%! assert (exist ("chol", "file"), 3);
-%! assert (exist ("chol", "builtin"), 0);
+%!assert (exist ("fftw"), 3);
+%!assert (exist ("fftw.oct"), 3);
+%!assert (exist ("fftw", "file"), 3);
+%!assert (exist ("fftw", "builtin"), 0);
 
 %!assert (exist ("sin"), 5)
 %!assert (exist ("sin", "builtin"), 5)
--- a/libinterp/dldfcn/__eigs__.cc	Sat Jan 25 15:26:07 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,675 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2005-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include <limits>
-#include <string>
-
-#include "Matrix.h"
-#include "eigs-base.h"
-#include "unwind-prot.h"
-
-#include "defun-dld.h"
-#include "error.h"
-#include "errwarn.h"
-#include "interpreter-private.h"
-#include "oct-map.h"
-#include "ov.h"
-#include "ovl.h"
-#include "pager.h"
-#include "parse.h"
-#include "variables.h"
-
-#if defined (HAVE_ARPACK)
-
-// Global pointer for user defined function.
-static octave_value eigs_fcn;
-
-// Have we warned about imaginary values returned from user function?
-static bool warned_imaginary = false;
-
-// Is this a recursive call?
-static int call_depth = 0;
-
-ColumnVector
-eigs_func (const ColumnVector& x, int& eigs_error)
-{
-  ColumnVector retval;
-  octave_value_list args;
-  args(0) = x;
-
-  if (eigs_fcn.is_defined ())
-    {
-      octave_value_list tmp;
-
-      try
-        {
-          tmp = octave::feval (eigs_fcn, args, 1);
-        }
-      catch (octave::execution_exception& e)
-        {
-          err_user_supplied_eval (e, "eigs");
-        }
-
-      if (tmp.length () && tmp(0).is_defined ())
-        {
-          if (! warned_imaginary && tmp(0).iscomplex ())
-            {
-              warning ("eigs: ignoring imaginary part returned from user-supplied function");
-              warned_imaginary = true;
-            }
-
-          retval = tmp(0).xvector_value ("eigs: evaluation of user-supplied function failed");
-        }
-      else
-        {
-          eigs_error = 1;
-          err_user_supplied_eval ("eigs");
-        }
-    }
-
-  return retval;
-}
-
-ComplexColumnVector
-eigs_complex_func (const ComplexColumnVector& x, int& eigs_error)
-{
-  ComplexColumnVector retval;
-  octave_value_list args;
-  args(0) = x;
-
-  if (eigs_fcn.is_defined ())
-    {
-      octave_value_list tmp;
-
-      try
-        {
-          tmp = octave::feval (eigs_fcn, args, 1);
-        }
-      catch (octave::execution_exception& e)
-        {
-          err_user_supplied_eval (e, "eigs");
-        }
-
-      if (tmp.length () && tmp(0).is_defined ())
-        {
-          retval = tmp(0).xcomplex_vector_value ("eigs: evaluation of user-supplied function failed");
-        }
-      else
-        {
-          eigs_error = 1;
-          err_user_supplied_eval ("eigs");
-        }
-    }
-
-  return retval;
-}
-
-#endif
-
-DEFMETHOD_DLD (__eigs__, interp, args, nargout,
-               doc: /* -*- texinfo -*-
-@deftypefn  {} {@var{d} =} __eigs__ (@var{A})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k}, @var{sigma})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k}, @var{sigma}, @var{opts})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k}, @var{sigma})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k}, @var{sigma}, @var{opts})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k}, @var{sigma})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k}, @var{sigma})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k}, @var{sigma}, @var{opts})
-@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k}, @var{sigma}, @var{opts})
-@deftypefnx {} {[@var{V}, @var{d}] =} __eigs__ (@var{A}, @dots{})
-@deftypefnx {} {[@var{V}, @var{d}] =} __eigs__ (@var{af}, @var{n}, @dots{})
-@deftypefnx {} {[@var{V}, @var{d}, @var{flag}] =} __eigs__ (@var{A}, @dots{})
-@deftypefnx {} {[@var{V}, @var{d}, @var{flag}] =} __eigs__ (@var{af}, @var{n}, @dots{})
-Undocumented internal function.
-@end deftypefn */)
-{
-#if defined (HAVE_ARPACK)
-
-  int nargin = args.length ();
-
-  if (nargin == 0)
-    print_usage ();
-
-  octave_value_list retval;
-
-  std::string fcn_name;
-  octave_idx_type n = 0;
-  octave_idx_type k = 6;
-  Complex sigma = 0.0;
-  double sigmar, sigmai;
-  bool have_sigma = false;
-  std::string typ = "LM";
-  Matrix amm, bmm, bmt;
-  ComplexMatrix acm, bcm, bct;
-  SparseMatrix asmm, bsmm, bsmt;
-  SparseComplexMatrix ascm, bscm, bsct;
-  int b_arg = 0;
-  bool have_b = false;
-  bool have_a_fun = false;
-  bool a_is_complex = false;
-  bool b_is_complex = false;
-  bool symmetric = false;
-  bool sym_tested = false;
-  bool cholB = false;
-  bool a_is_sparse = false;
-  bool b_is_sparse = false;
-  ColumnVector permB;
-  int arg_offset = 0;
-  double tol = std::numeric_limits<double>::epsilon ();
-  int maxit = 300;
-  int disp = 0;
-  octave_idx_type p = -1;
-  ColumnVector resid;
-  ComplexColumnVector cresid;
-  octave_idx_type info = 1;
-
-  warned_imaginary = false;
-
-  octave::unwind_protect frame;
-
-  frame.protect_var (call_depth);
-  call_depth++;
-
-  if (call_depth > 1)
-    error ("eigs: invalid recursive call");
-
-  if (args(0).is_function_handle () || args(0).is_inline_function ()
-      || args(0).is_string ())
-    {
-      eigs_fcn = octave::get_function_handle (interp, args(0), "x");
-
-      if (eigs_fcn.is_undefined ())
-        error ("eigs: unknown function");
-
-      if (nargin < 2)
-        error ("eigs: incorrect number of arguments");
-
-      n = args(1).nint_value ();
-      arg_offset = 1;
-      have_a_fun = true;
-    }
-  else
-    {
-      if (args(0).iscomplex ())
-        {
-          if (args(0).issparse ())
-            {
-              ascm = (args(0).sparse_complex_matrix_value ());
-              a_is_sparse = true;
-            }
-          else
-            acm = (args(0).complex_matrix_value ());
-          a_is_complex = true;
-        }
-      else
-        {
-          if (args(0).issparse ())
-            {
-              asmm = (args(0).sparse_matrix_value ());
-              a_is_sparse = true;
-            }
-          else
-            {
-              amm = (args(0).matrix_value ());
-            }
-        }
-    }
-
-  // Note hold off reading B until later to avoid issues of double
-  // copies of the matrix if B is full/real while A is complex.
-  if (nargin > 1 + arg_offset
-      && ! (args(1 + arg_offset).is_real_scalar ()))
-    {
-      if (args(1+arg_offset).iscomplex ())
-        {
-          b_arg = 1+arg_offset;
-          if (args(b_arg).issparse ())
-            {
-              bscm = (args(b_arg).sparse_complex_matrix_value ());
-              b_is_sparse = true;
-            }
-          else
-            bcm = (args(b_arg).complex_matrix_value ());
-          have_b = true;
-          b_is_complex = true;
-          arg_offset++;
-        }
-      else
-        {
-          b_arg = 1+arg_offset;
-          if (args(b_arg).issparse ())
-            {
-              bsmm = (args(b_arg).sparse_matrix_value ());
-              b_is_sparse = true;
-            }
-          else
-            bmm = (args(b_arg).matrix_value ());
-          have_b = true;
-          arg_offset++;
-        }
-    }
-
-  if (nargin > (1+arg_offset))
-    k = args(1+arg_offset).nint_value ();
-
-  if (nargin > (2+arg_offset))
-    {
-      if (args(2+arg_offset).is_string ())
-        {
-          typ = args(2+arg_offset).string_value ();
-
-          // Use STL function to convert to upper case
-          transform (typ.begin (), typ.end (), typ.begin (), toupper);
-
-          sigma = 0.0;
-        }
-      else
-        {
-          sigma = args(2+arg_offset).xcomplex_value ("eigs: SIGMA must be a scalar or a string");
-
-          have_sigma = true;
-        }
-    }
-
-  sigmar = sigma.real ();
-  sigmai = sigma.imag ();
-
-  if (nargin > (3+arg_offset))
-    {
-      if (! args(3+arg_offset).isstruct ())
-        error ("eigs: OPTS argument must be a structure");
-
-      octave_scalar_map map = args(3+arg_offset).xscalar_map_value ("eigs: OPTS argument must be a scalar structure");
-
-      octave_value tmp;
-
-      // issym is ignored for complex matrix inputs
-      tmp = map.getfield ("issym");
-      if (tmp.is_defined ())
-        {
-          if (tmp.numel () != 1)
-            error ("eigs: OPTS.issym must be a scalar value");
-
-          symmetric = tmp.xbool_value ("eigs: OPTS.issym must be a logical value");
-          sym_tested = true;
-        }
-
-      // isreal is ignored if A is not a function
-      if (have_a_fun)
-        {
-          tmp = map.getfield ("isreal");
-          if (tmp.is_defined ())
-            {
-              if (tmp.numel () != 1)
-                error ("eigs: OPTS.isreal must be a scalar value");
-
-              a_is_complex = ! tmp.xbool_value ("eigs: OPTS.isreal must be a logical value");
-            }
-        }
-
-      tmp = map.getfield ("tol");
-      if (tmp.is_defined ())
-        tol = tmp.double_value ();
-
-      tmp = map.getfield ("maxit");
-      if (tmp.is_defined ())
-        maxit = tmp.nint_value ();
-
-      tmp = map.getfield ("p");
-      if (tmp.is_defined ())
-        p = tmp.nint_value ();
-
-      tmp = map.getfield ("v0");
-      if (tmp.is_defined ())
-        {
-          if (a_is_complex || b_is_complex)
-            cresid = ComplexColumnVector (tmp.complex_vector_value ());
-          else
-            resid = ColumnVector (tmp.vector_value ());
-        }
-
-      tmp = map.getfield ("disp");
-      if (tmp.is_defined ())
-        disp = tmp.nint_value ();
-
-      tmp = map.getfield ("cholB");
-      if (tmp.is_defined ())
-        {
-          if (tmp.numel () != 1)
-            error ("eigs: OPTS.cholB must be a scalar value");
-
-          cholB = tmp.xbool_value ("eigs: OPTS.cholB must be a logical value");
-        }
-
-      tmp = map.getfield ("permB");
-      if (tmp.is_defined ())
-        permB = ColumnVector (tmp.vector_value ()) - 1.0;
-    }
-
-  if (nargin > (4+arg_offset))
-    error ("eigs: incorrect number of arguments");
-
-  // Test undeclared (no issym) matrix inputs for symmetry
-  if (! sym_tested && ! have_a_fun)
-    {
-      if (a_is_complex)
-        {
-          if (a_is_sparse)
-            symmetric = ascm.ishermitian ();
-          else
-            symmetric = acm.ishermitian ();
-        }
-      else
-        {
-          if (a_is_sparse)
-            symmetric = asmm.issymmetric ();
-          else
-            symmetric = amm.issymmetric ();
-        }
-    }
-
-  if (have_b)
-    {
-      if (a_is_complex || b_is_complex)
-        {
-          if (b_is_sparse)
-            bscm = args(b_arg).sparse_complex_matrix_value ();
-          else
-            bcm = args(b_arg).complex_matrix_value ();
-        }
-      else
-        {
-          if (b_is_sparse)
-            bsmm = args(b_arg).sparse_matrix_value ();
-          else
-            bmm = args(b_arg).matrix_value ();
-        }
-    }
-
-  // Mode 1 for SM mode seems unstable for some reason.
-  // Use Mode 3 instead, with sigma = 0.
-  if (! have_sigma && typ == "SM")
-    have_sigma = true;
-
-  octave_idx_type nconv;
-  if (a_is_complex || b_is_complex)
-    {
-      ComplexMatrix eig_vec;
-      ComplexColumnVector eig_val;
-
-      if (have_a_fun)
-        {
-          if (b_is_sparse)
-            nconv = EigsComplexNonSymmetricFunc
-              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
-               eig_val, bscm, permB, cresid, octave_stdout, tol,
-               (nargout > 1), cholB, disp, maxit);
-          else
-            nconv = EigsComplexNonSymmetricFunc
-              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
-               eig_val, bcm, permB, cresid, octave_stdout, tol,
-               (nargout > 1), cholB, disp, maxit);
-        }
-      else if (have_sigma)
-        {
-          if (a_is_sparse)
-            nconv = EigsComplexNonSymmetricMatrixShift
-                    (ascm, sigma, k, p, info, eig_vec, eig_val, bscm, permB,
-                     cresid, octave_stdout, tol, (nargout > 1), cholB, disp,
-                     maxit);
-          else
-            nconv = EigsComplexNonSymmetricMatrixShift
-                    (acm, sigma, k, p, info, eig_vec, eig_val, bcm, permB,
-                     cresid, octave_stdout, tol, (nargout > 1), cholB, disp,
-                     maxit);
-        }
-      else
-        {
-          if (a_is_sparse)
-            nconv = EigsComplexNonSymmetricMatrix
-                    (ascm, typ, k, p, info, eig_vec, eig_val, bscm, permB,
-                     cresid, octave_stdout, tol, (nargout > 1), cholB, disp,
-                     maxit);
-          else
-            nconv = EigsComplexNonSymmetricMatrix
-                    (acm, typ, k, p, info, eig_vec, eig_val, bcm, permB,
-                     cresid, octave_stdout, tol, (nargout > 1), cholB, disp,
-                     maxit);
-        }
-
-      if (nargout < 2)
-        {
-          if (symmetric)
-            retval(0) = real (eig_val);
-          else
-            retval(0) = eig_val;
-        }
-      else
-        {
-          if (symmetric)
-            retval = ovl (eig_vec, DiagMatrix (real (eig_val)), double (info));
-          else
-            retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info));
-        }
-    }
-  else if (sigmai != 0.0)
-    {
-      // Promote real problem to a complex one.
-      ComplexMatrix eig_vec;
-      ComplexColumnVector eig_val;
-
-      if (have_a_fun)
-        {
-          if (b_is_sparse)
-            nconv = EigsComplexNonSymmetricFunc
-              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
-               eig_val, bscm, permB, cresid, octave_stdout, tol,
-               (nargout > 1), cholB, disp, maxit);
-          else
-            nconv = EigsComplexNonSymmetricFunc
-              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
-               eig_val, bcm, permB, cresid, octave_stdout, tol,
-               (nargout > 1), cholB, disp, maxit);
-        }
-      else
-        {
-          if (a_is_sparse)
-            nconv = EigsComplexNonSymmetricMatrixShift
-                    (SparseComplexMatrix (asmm), sigma, k, p, info, eig_vec,
-                     eig_val, SparseComplexMatrix (bsmm), permB, cresid,
-                     octave_stdout, tol, (nargout > 1), cholB, disp, maxit);
-          else
-            nconv = EigsComplexNonSymmetricMatrixShift
-                    (ComplexMatrix (amm), sigma, k, p, info, eig_vec,
-                     eig_val, ComplexMatrix (bmm), permB, cresid,
-                     octave_stdout, tol, (nargout > 1), cholB, disp, maxit);
-        }
-
-      if (nargout < 2)
-        {
-          if (symmetric)
-            retval(0) = real (eig_val);
-          else
-            retval(0) = eig_val;
-        }
-      else
-        {
-          if (symmetric)
-            retval = ovl (eig_vec, DiagMatrix (real (eig_val)), double (info));
-          else
-            retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info));
-        }
-    }
-  else
-    {
-      if (symmetric)
-        {
-          Matrix eig_vec;
-          ColumnVector eig_val;
-
-          if (have_a_fun)
-            {
-              if (b_is_sparse)
-                nconv = EigsRealSymmetricFunc
-                       (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
-                        eig_val, bsmm, permB, resid, octave_stdout, tol,
-                        (nargout > 1), cholB, disp, maxit);
-              else
-                nconv = EigsRealSymmetricFunc
-                       (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
-                        eig_val, bmm, permB, resid, octave_stdout, tol,
-                        (nargout > 1), cholB, disp, maxit);
-            }
-          else if (have_sigma)
-            {
-              if (a_is_sparse)
-                nconv = EigsRealSymmetricMatrixShift
-                        (asmm, sigmar, k, p, info, eig_vec, eig_val, bsmm,
-                         permB, resid, octave_stdout, tol, (nargout > 1),
-                         cholB, disp, maxit);
-              else
-                nconv = EigsRealSymmetricMatrixShift
-                        (amm, sigmar, k, p, info, eig_vec, eig_val, bmm,
-                         permB, resid, octave_stdout, tol, (nargout > 1),
-                         cholB, disp, maxit);
-            }
-          else
-            {
-              if (a_is_sparse)
-                nconv = EigsRealSymmetricMatrix
-                        (asmm, typ, k, p, info, eig_vec, eig_val, bsmm,
-                         permB, resid, octave_stdout, tol, (nargout > 1),
-                         cholB, disp, maxit);
-              else
-                nconv = EigsRealSymmetricMatrix
-                        (amm, typ, k, p, info, eig_vec, eig_val, bmm, permB,
-                         resid, octave_stdout, tol, (nargout > 1), cholB,
-                         disp, maxit);
-            }
-
-          if (nargout < 2)
-            retval(0) = eig_val;
-          else
-            retval = ovl (eig_vec, DiagMatrix (eig_val), double (info));
-        }
-      else
-        {
-          ComplexMatrix eig_vec;
-          ComplexColumnVector eig_val;
-
-          if (have_a_fun)
-            {
-              if (b_is_sparse)
-                nconv = EigsRealNonSymmetricFunc
-                        (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
-                         eig_val, bsmm, permB, resid, octave_stdout, tol,
-                         (nargout > 1), cholB, disp, maxit);
-              else
-                nconv = EigsRealNonSymmetricFunc
-                        (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
-                         eig_val, bmm, permB, resid, octave_stdout, tol,
-                         (nargout > 1), cholB, disp, maxit);
-            }
-          else if (have_sigma)
-            {
-              if (a_is_sparse)
-                nconv = EigsRealNonSymmetricMatrixShift
-                        (asmm, sigmar, k, p, info, eig_vec, eig_val, bsmm,
-                         permB, resid, octave_stdout, tol, (nargout > 1),
-                         cholB, disp, maxit);
-              else
-                nconv = EigsRealNonSymmetricMatrixShift
-                        (amm, sigmar, k, p, info, eig_vec, eig_val, bmm,
-                         permB, resid, octave_stdout, tol, (nargout > 1),
-                         cholB, disp, maxit);
-            }
-          else
-            {
-              if (a_is_sparse)
-                nconv = EigsRealNonSymmetricMatrix
-                        (asmm, typ, k, p, info, eig_vec, eig_val, bsmm,
-                         permB, resid, octave_stdout, tol, (nargout > 1),
-                         cholB, disp, maxit);
-              else
-                nconv = EigsRealNonSymmetricMatrix
-                        (amm, typ, k, p, info, eig_vec, eig_val, bmm, permB,
-                         resid, octave_stdout, tol, (nargout > 1), cholB,
-                         disp, maxit);
-            }
-
-          if (nargout < 2)
-            retval(0) = eig_val;
-          else
-            retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info));
-        }
-    }
-
-  if (nconv <= 0)
-    warning_with_id ("Octave:eigs:UnconvergedEigenvalues",
-                     "eigs: None of the %" OCTAVE_IDX_TYPE_FORMAT
-                     " requested eigenvalues converged", k);
-  else if (nconv < k)
-    warning_with_id ("Octave:eigs:UnconvergedEigenvalues",
-                     "eigs: Only %" OCTAVE_IDX_TYPE_FORMAT
-                     " of the %" OCTAVE_IDX_TYPE_FORMAT
-                     " requested eigenvalues converged",
-                     nconv, k);
-
-  if (! fcn_name.empty ())
-    {
-      octave::symbol_table& symtab = interp.get_symbol_table ();
-
-      symtab.clear_function (fcn_name);
-    }
-
-  return retval;
-
-#else
-
-  octave_unused_parameter (interp);
-  octave_unused_parameter (args);
-  octave_unused_parameter (nargout);
-
-  err_disabled_feature ("eigs", "ARPACK");
-
-#endif
-}
-
-/*
-## No test needed for internal helper function.
-%!assert (1)
-*/
--- a/libinterp/dldfcn/amd.cc	Sat Jan 25 15:26:07 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,205 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2008-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-// This is the octave interface to amd, which bore the copyright given
-// in the help of the functions.
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include <cstdlib>
-
-#include "CSparse.h"
-#include "Sparse.h"
-#include "dMatrix.h"
-#include "oct-locbuf.h"
-#include "oct-sparse.h"
-
-#include "defun-dld.h"
-#include "error.h"
-#include "errwarn.h"
-#include "oct-map.h"
-#include "ov.h"
-#include "ovl.h"
-#include "parse.h"
-
-DEFMETHOD_DLD (amd, interp, args, nargout,
-               doc: /* -*- texinfo -*-
-@deftypefn  {} {@var{p} =} amd (@var{S})
-@deftypefnx {} {@var{p} =} amd (@var{S}, @var{opts})
-
-Return the approximate minimum degree permutation of a matrix.
-
-This is a permutation such that the Cholesky@tie{}factorization of
-@code{@var{S} (@var{p}, @var{p})} tends to be sparser than the
-Cholesky@tie{}factorization of @var{S} itself.  @code{amd} is typically
-faster than @code{symamd} but serves a similar purpose.
-
-The optional parameter @var{opts} is a structure that controls the behavior
-of @code{amd}.  The fields of the structure are
-
-@table @asis
-@item @var{opts}.dense
-Determines what @code{amd} considers to be a dense row or column of the
-input matrix.  Rows or columns with more than @code{max (16, (dense *
-sqrt (@var{n})))} entries, where @var{n} is the order of the matrix @var{S},
-are ignored by @code{amd} during the calculation of the permutation.
-The value of dense must be a positive scalar and the default value is 10.0
-
-@item @var{opts}.aggressive
-If this value is a nonzero scalar, then @code{amd} performs aggressive
-absorption.  The default is not to perform aggressive absorption.
-@end table
-
-The author of the code itself is Timothy A. Davis
-(see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html}).
-@seealso{symamd, colamd}
-@end deftypefn */)
-{
-#if defined (HAVE_AMD)
-
-  int nargin = args.length ();
-
-  if (nargin < 1 || nargin > 2)
-    print_usage ();
-
-  octave_idx_type n_row, n_col;
-  const octave::suitesparse_integer *ridx, *cidx;
-  SparseMatrix sm;
-  SparseComplexMatrix scm;
-
-  if (args(0).issparse ())
-    {
-      if (args(0).iscomplex ())
-        {
-          scm = args(0).sparse_complex_matrix_value ();
-          n_row = scm.rows ();
-          n_col = scm.cols ();
-          ridx = octave::to_suitesparse_intptr (scm.xridx ());
-          cidx = octave::to_suitesparse_intptr (scm.xcidx ());
-        }
-      else
-        {
-          sm = args(0).sparse_matrix_value ();
-          n_row = sm.rows ();
-          n_col = sm.cols ();
-          ridx = octave::to_suitesparse_intptr (sm.xridx ());
-          cidx = octave::to_suitesparse_intptr (sm.xcidx ());
-        }
-    }
-  else
-    {
-      if (args(0).iscomplex ())
-        sm = SparseMatrix (real (args(0).complex_matrix_value ()));
-      else
-        sm = SparseMatrix (args(0).matrix_value ());
-
-      n_row = sm.rows ();
-      n_col = sm.cols ();
-      ridx = octave::to_suitesparse_intptr (sm.xridx ());
-      cidx = octave::to_suitesparse_intptr (sm.xcidx ());
-    }
-
-  if (n_row != n_col)
-    err_square_matrix_required ("amd", "S");
-
-  OCTAVE_LOCAL_BUFFER (double, Control, AMD_CONTROL);
-  AMD_NAME (_defaults) (Control);
-  if (nargin > 1)
-    {
-      octave_scalar_map arg1 = args(1).xscalar_map_value ("amd: OPTS argument must be a scalar structure");
-
-      octave_value tmp;
-
-      tmp = arg1.getfield ("dense");
-      if (tmp.is_defined ())
-        Control[AMD_DENSE] = tmp.double_value ();
-
-      tmp = arg1.getfield ("aggressive");
-      if (tmp.is_defined ())
-        Control[AMD_AGGRESSIVE] = tmp.double_value ();
-    }
-
-  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, P, n_col);
-  Matrix xinfo (AMD_INFO, 1);
-  double *Info = xinfo.fortran_vec ();
-
-  // Lock the function to not loose the SuiteSparse_config structure
-  interp.mlock ();
-
-  // FIXME: how can we manage the memory allocation of amd
-  //        in a cleaner manner?
-  SUITESPARSE_ASSIGN_FPTR (malloc_func, amd_malloc, malloc);
-  SUITESPARSE_ASSIGN_FPTR (free_func, amd_free, free);
-  SUITESPARSE_ASSIGN_FPTR (calloc_func, amd_calloc, calloc);
-  SUITESPARSE_ASSIGN_FPTR (realloc_func, amd_realloc, realloc);
-  SUITESPARSE_ASSIGN_FPTR (printf_func, amd_printf, printf);
-
-  octave_idx_type result = AMD_NAME (_order) (n_col, cidx, ridx, P, Control,
-                                              Info);
-
-  if (result == AMD_OUT_OF_MEMORY)
-    error ("amd: out of memory");
-  else if (result == AMD_INVALID)
-    error ("amd: matrix S is corrupted");
-
-  Matrix Pout (1, n_col);
-  for (octave_idx_type i = 0; i < n_col; i++)
-    Pout.xelem (i) = P[i] + 1;
-
-  if (nargout > 1)
-    return ovl (Pout, xinfo);
-  else
-    return ovl (Pout);
-
-#else
-
-  octave_unused_parameter (args);
-  octave_unused_parameter (nargout);
-
-  err_disabled_feature ("amd", "AMD");
-
-#endif
-}
-
-/*
-%!shared A, A2, opts
-%! A = ones (20, 30);
-%! A2 = ones (30, 30);
-
-%!testif HAVE_AMD
-%! assert(amd (A2), [1:30]);
-%! opts.dense = 25;
-%! assert(amd (A2, opts), [1:30]);
-%! opts.aggressive = 1;
-%! assert(amd (A2, opts), [1:30]);
-
-%!testif HAVE_AMD
-%! assert (amd ([]), zeros (1,0))
-
-%!error <S must be a square matrix|was unavailable or disabled> amd (A)
-%!error amd (A2, 2)
-*/
--- a/libinterp/dldfcn/ccolamd.cc	Sat Jan 25 15:26:07 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,583 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2005-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-// This is the octave interface to ccolamd, which bore the copyright given
-// in the help of the functions.
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include <cstdlib>
-
-#include "CSparse.h"
-#include "Sparse.h"
-#include "dNDArray.h"
-#include "oct-locbuf.h"
-#include "oct-sparse.h"
-
-#include "defun-dld.h"
-#include "error.h"
-#include "errwarn.h"
-#include "ov.h"
-#include "pager.h"
-
-DEFUN_DLD (ccolamd, args, nargout,
-           doc: /* -*- texinfo -*-
-@deftypefn  {} {@var{p} =} ccolamd (@var{S})
-@deftypefnx {} {@var{p} =} ccolamd (@var{S}, @var{knobs})
-@deftypefnx {} {@var{p} =} ccolamd (@var{S}, @var{knobs}, @var{cmember})
-@deftypefnx {} {[@var{p}, @var{stats}] =} ccolamd (@dots{})
-
-Constrained column approximate minimum degree permutation.
-
-@code{@var{p} = ccolamd (@var{S})} returns the column approximate minimum
-degree permutation vector for the sparse matrix @var{S}.  For a
-non-symmetric matrix @var{S}, @code{@var{S}(:, @var{p})} tends to have
-sparser LU@tie{}factors than @var{S}.
-@code{chol (@var{S}(:, @var{p})' * @var{S}(:, @var{p}))} also tends to be
-sparser than @code{chol (@var{S}' * @var{S})}.
-@code{@var{p} = ccolamd (@var{S}, 1)} optimizes the ordering for
-@code{lu (@var{S}(:, @var{p}))}.  The ordering is followed by a column
-elimination tree post-ordering.
-
-@var{knobs} is an optional 1-element to 5-element input vector, with a
-default value of @code{[0 10 10 1 0]} if not present or empty.  Entries not
-present are set to their defaults.
-
-@table @code
-@item @var{knobs}(1)
-if nonzero, the ordering is optimized for @code{lu (S(:, p))}.  It will be a
-poor ordering for @code{chol (@var{S}(:, @var{p})' * @var{S}(:, @var{p}))}.
-This is the most important knob for ccolamd.
-
-@item @var{knobs}(2)
-if @var{S} is m-by-n, rows with more than
-@code{max (16, @var{knobs}(2) * sqrt (n))} entries are ignored.
-
-@item @var{knobs}(3)
-columns with more than
-@code{max (16, @var{knobs}(3) * sqrt (min (@var{m}, @var{n})))} entries are
-ignored and ordered last in the output permutation
-(subject to the cmember constraints).
-
-@item @var{knobs}(4)
-if nonzero, aggressive absorption is performed.
-
-@item @var{knobs}(5)
-if nonzero, statistics and knobs are printed.
-
-@end table
-
-@var{cmember} is an optional vector of length @math{n}.  It defines the
-constraints on the column ordering.  If @code{@var{cmember}(j) = @var{c}},
-then column @var{j} is in constraint set @var{c} (@var{c} must be in the
-range 1 to n).  In the output permutation @var{p}, all columns in set 1
-appear first, followed by all columns in set 2, and so on.
-@code{@var{cmember} = ones (1,n)} if not present or empty.
-@code{ccolamd (@var{S}, [], 1 : n)} returns @code{1 : n}
-
-@code{@var{p} = ccolamd (@var{S})} is about the same as
-@code{@var{p} = colamd (@var{S})}.  @var{knobs} and its default values
-differ.  @code{colamd} always does aggressive absorption, and it finds an
-ordering suitable for both @code{lu (@var{S}(:, @var{p}))} and @code{chol
-(@var{S}(:, @var{p})' * @var{S}(:, @var{p}))}; it cannot optimize its
-ordering for @code{lu (@var{S}(:, @var{p}))} to the extent that
-@code{ccolamd (@var{S}, 1)} can.
-
-@var{stats} is an optional 20-element output vector that provides data
-about the ordering and the validity of the input matrix @var{S}.  Ordering
-statistics are in @code{@var{stats}(1 : 3)}.  @code{@var{stats}(1)} and
-@code{@var{stats}(2)} are the number of dense or empty rows and columns
-ignored by @sc{ccolamd} and @code{@var{stats}(3)} is the number of garbage
-collections performed on the internal data structure used by @sc{ccolamd}
-(roughly of size @code{2.2 * nnz (@var{S}) + 4 * @var{m} + 7 * @var{n}}
-integers).
-
-@code{@var{stats}(4 : 7)} provide information if CCOLAMD was able to
-continue.  The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if
-invalid.  @code{@var{stats}(5)} is the rightmost column index that is
-unsorted or contains duplicate entries, or zero if no such column exists.
-@code{@var{stats}(6)} is the last seen duplicate or out-of-order row
-index in the column index given by @code{@var{stats}(5)}, or zero if no
-such row index exists.  @code{@var{stats}(7)} is the number of duplicate
-or out-of-order row indices.  @code{@var{stats}(8 : 20)} is always zero in
-the current version of @sc{ccolamd} (reserved for future use).
-
-The authors of the code itself are @nospell{S. Larimore, T. Davis} and
-@nospell{S. Rajamanickam} in collaboration with @nospell{J. Bilbert and E. Ng}.
-Supported by the National Science Foundation
-@nospell{(DMS-9504974, DMS-9803599, CCR-0203270)}, and a grant from
-@nospell{Sandia} National Lab.
-See @url{http://faculty.cse.tamu.edu/davis/suitesparse.html} for ccolamd,
-csymamd, amd, colamd, symamd, and other related orderings.
-@seealso{colamd, csymamd}
-@end deftypefn */)
-{
-#if defined (HAVE_CCOLAMD)
-
-  int nargin = args.length ();
-
-  if (nargin < 1 || nargin > 3)
-    print_usage ();
-
-  octave_value_list retval (nargout == 2 ? 2 : 1);
-  int spumoni = 0;
-
-  // Get knobs
-  static_assert (CCOLAMD_KNOBS <= 40,
-                 "ccolamd: # of CCOLAMD_KNOBS exceeded.  Please report this to bugs.octave.org");
-  double knob_storage[CCOLAMD_KNOBS];
-  double *knobs = &knob_storage[0];
-  CCOLAMD_NAME (_set_defaults) (knobs);
-
-  // Check for user-passed knobs
-  if (nargin > 1)
-    {
-      NDArray User_knobs = args(1).array_value ();
-      int nel_User_knobs = User_knobs.numel ();
-
-      if (nel_User_knobs > 0)
-        knobs[CCOLAMD_LU] = (User_knobs(0) != 0);
-      if (nel_User_knobs > 1)
-        knobs[CCOLAMD_DENSE_ROW] = User_knobs(1);
-      if (nel_User_knobs > 2)
-        knobs[CCOLAMD_DENSE_COL] = User_knobs(2);
-      if (nel_User_knobs > 3)
-        knobs[CCOLAMD_AGGRESSIVE] = (User_knobs(3) != 0);
-      if (nel_User_knobs > 4)
-        spumoni = (User_knobs(4) != 0);
-
-      // print knob settings if spumoni is set
-      if (spumoni)
-        {
-          octave_stdout << "\nccolamd version " << CCOLAMD_MAIN_VERSION << '.'
-                        <<  CCOLAMD_SUB_VERSION << ", " << CCOLAMD_DATE
-                        << ":\nknobs(1): " << User_knobs(0) << ", order for ";
-          if (knobs[CCOLAMD_LU] != 0)
-            octave_stdout << "lu (A)\n";
-          else
-            octave_stdout << "chol (A'*A)\n";
-
-          if (knobs[CCOLAMD_DENSE_ROW] >= 0)
-            octave_stdout << "knobs(2): " << User_knobs(1)
-                          << ", rows with > max (16,"
-                          << knobs[CCOLAMD_DENSE_ROW]
-                          << "*sqrt (size(A,2)))"
-                          << " entries removed\n";
-          else
-            octave_stdout << "knobs(2): " << User_knobs(1)
-                          << ", no dense rows removed\n";
-
-          if (knobs[CCOLAMD_DENSE_COL] >= 0)
-            octave_stdout << "knobs(3): " << User_knobs(2)
-                          << ", cols with > max (16,"
-                          << knobs[CCOLAMD_DENSE_COL] << "*sqrt (size(A)))"
-                          << " entries removed\n";
-          else
-            octave_stdout << "knobs(3): " << User_knobs(2)
-                          << ", no dense columns removed\n";
-
-          if (knobs[CCOLAMD_AGGRESSIVE] != 0)
-            octave_stdout << "knobs(4): " << User_knobs(3)
-                          << ", aggressive absorption: yes";
-          else
-            octave_stdout << "knobs(4): " << User_knobs(3)
-                          << ", aggressive absorption: no";
-
-          octave_stdout << "knobs(5): " << User_knobs(4)
-                        << ", statistics and knobs printed\n";
-        }
-    }
-
-  octave_idx_type n_row, n_col, nnz;
-  octave_idx_type *ridx, *cidx;
-  SparseComplexMatrix scm;
-  SparseMatrix sm;
-
-  if (args(0).issparse ())
-    {
-      if (args(0).iscomplex ())
-        {
-          scm = args(0).sparse_complex_matrix_value ();
-          n_row = scm.rows ();
-          n_col = scm.cols ();
-          nnz = scm.nnz ();
-          ridx = scm.xridx ();
-          cidx = scm.xcidx ();
-        }
-      else
-        {
-          sm = args(0).sparse_matrix_value ();
-
-          n_row = sm.rows ();
-          n_col = sm.cols ();
-          nnz = sm.nnz ();
-          ridx = sm.xridx ();
-          cidx = sm.xcidx ();
-        }
-    }
-  else
-    {
-      if (args(0).iscomplex ())
-        sm = SparseMatrix (real (args(0).complex_matrix_value ()));
-      else
-        sm = SparseMatrix (args(0).matrix_value ());
-
-      n_row = sm.rows ();
-      n_col = sm.cols ();
-      nnz = sm.nnz ();
-      ridx = sm.xridx ();
-      cidx = sm.xcidx ();
-    }
-
-  // Allocate workspace for ccolamd
-  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, p, n_col+1);
-  for (octave_idx_type i = 0; i < n_col+1; i++)
-    p[i] = cidx[i];
-
-  octave_idx_type Alen = CCOLAMD_NAME (_recommended) (nnz, n_row, n_col);
-  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, A, Alen);
-  for (octave_idx_type i = 0; i < nnz; i++)
-    A[i] = ridx[i];
-
-  static_assert (CCOLAMD_STATS <= 40,
-                 "ccolamd: # of CCOLAMD_STATS exceeded.  Please report this to bugs.octave.org");
-  octave::suitesparse_integer stats_storage[CCOLAMD_STATS];
-  octave::suitesparse_integer *stats = &stats_storage[0];
-
-  if (nargin > 2)
-    {
-      NDArray in_cmember = args(2).array_value ();
-      octave_idx_type cslen = in_cmember.numel ();
-      OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, cmember, cslen);
-      for (octave_idx_type i = 0; i < cslen; i++)
-        // convert cmember from 1-based to 0-based
-        cmember[i] = static_cast<octave::suitesparse_integer>(in_cmember(i) - 1);
-
-      if (cslen != n_col)
-        error ("ccolamd: CMEMBER must be of length equal to #cols of A");
-
-      // Order the columns (destroys A)
-      if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats,cmember))
-        {
-          CCOLAMD_NAME (_report) (stats);
-
-          error ("ccolamd: internal error!");
-        }
-    }
-  else
-    {
-      // Order the columns (destroys A)
-      if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats, nullptr))
-        {
-          CCOLAMD_NAME (_report) (stats);
-
-          error ("ccolamd: internal error!");
-        }
-    }
-
-  // return the permutation vector
-  NDArray out_perm (dim_vector (1, n_col));
-  for (octave_idx_type i = 0; i < n_col; i++)
-    out_perm(i) = p[i] + 1;
-
-  retval(0) = out_perm;
-
-  // print stats if spumoni > 0
-  if (spumoni > 0)
-    CCOLAMD_NAME (_report) (stats);
-
-  // Return the stats vector
-  if (nargout == 2)
-    {
-      NDArray out_stats (dim_vector (1, CCOLAMD_STATS));
-      for (octave_idx_type i = 0 ; i < CCOLAMD_STATS ; i++)
-        out_stats(i) = stats[i];
-      retval(1) = out_stats;
-
-      // fix stats (5) and (6), for 1-based information on
-      // jumbled matrix.  note that this correction doesn't
-      // occur if symamd returns FALSE
-      out_stats(CCOLAMD_INFO1)++;
-      out_stats(CCOLAMD_INFO2)++;
-    }
-
-  return retval;
-
-#else
-
-  octave_unused_parameter (args);
-  octave_unused_parameter (nargout);
-
-  err_disabled_feature ("ccolamd", "CCOLAMD");
-
-#endif
-}
-
-DEFUN_DLD (csymamd, args, nargout,
-           doc: /* -*- texinfo -*-
-@deftypefn  {} {@var{p} =} csymamd (@var{S})
-@deftypefnx {} {@var{p} =} csymamd (@var{S}, @var{knobs})
-@deftypefnx {} {@var{p} =} csymamd (@var{S}, @var{knobs}, @var{cmember})
-@deftypefnx {} {[@var{p}, @var{stats}] =} csymamd (@dots{})
-
-For a symmetric positive definite matrix @var{S}, return the permutation
-vector @var{p} such that @code{@var{S}(@var{p},@var{p})} tends to have a
-sparser Cholesky@tie{}factor than @var{S}.
-
-Sometimes @code{csymamd} works well for symmetric indefinite matrices too.
-The matrix @var{S} is assumed to be symmetric; only the strictly lower
-triangular part is referenced.  @var{S} must be square.  The ordering is
-followed by an elimination tree post-ordering.
-
-@var{knobs} is an optional 1-element to 3-element input vector, with a
-default value of @code{[10 1 0]}.  Entries not present are set to their
-defaults.
-
-@table @code
-@item @var{knobs}(1)
-If @var{S} is n-by-n, then rows and columns with more than
-@code{max(16,@var{knobs}(1)*sqrt(n))} entries are ignored, and ordered
-last in the output permutation (subject to the cmember constraints).
-
-@item @var{knobs}(2)
-If nonzero, aggressive absorption is performed.
-
-@item @var{knobs}(3)
-If nonzero, statistics and knobs are printed.
-
-@end table
-
-@var{cmember} is an optional vector of length n.  It defines the constraints
-on the ordering.  If @code{@var{cmember}(j) = @var{S}}, then row/column j is
-in constraint set @var{c} (@var{c} must be in the range 1 to n).  In the
-output permutation @var{p}, rows/columns in set 1 appear first, followed
-by all rows/columns in set 2, and so on.  @code{@var{cmember} = ones (1,n)}
-if not present or empty.  @code{csymamd (@var{S},[],1:n)} returns
-@code{1:n}.
-
-@code{@var{p} = csymamd (@var{S})} is about the same as
-@code{@var{p} = symamd (@var{S})}.  @var{knobs} and its default values
-differ.
-
-@code{@var{stats}(4:7)} provide information if CCOLAMD was able to
-continue.  The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if
-invalid.  @code{@var{stats}(5)} is the rightmost column index that is
-unsorted or contains duplicate entries, or zero if no such column exists.
-@code{@var{stats}(6)} is the last seen duplicate or out-of-order row
-index in the column index given by @code{@var{stats}(5)}, or zero if no
-such row index exists.  @code{@var{stats}(7)} is the number of duplicate
-or out-of-order row indices.  @code{@var{stats}(8:20)} is always zero in
-the current version of @sc{ccolamd} (reserved for future use).
-
-The authors of the code itself are @nospell{S. Larimore, T. Davis} and
-@nospell{S. Rajamanickam} in collaboration with @nospell{J. Bilbert and E. Ng}.
-Supported by the National Science Foundation
-@nospell{(DMS-9504974, DMS-9803599, CCR-0203270)}, and a grant from
-@nospell{Sandia} National Lab.
-See @url{http://faculty.cse.tamu.edu/davis/suitesparse.html} for ccolamd,
-colamd, csymamd, amd, colamd, symamd, and other related orderings.
-@seealso{symamd, ccolamd}
-@end deftypefn */)
-{
-#if defined (HAVE_CCOLAMD)
-
-  int nargin = args.length ();
-
-  if (nargin < 1 || nargin > 3)
-    print_usage ();
-
-  octave_value_list retval (nargout == 2 ? 2 : 1);
-  int spumoni = 0;
-
-  // Get knobs
-  static_assert (CCOLAMD_KNOBS <= 40,
-                 "csymamd: # of CCOLAMD_KNOBS exceeded.  Please report this to bugs.octave.org");
-  double knob_storage[CCOLAMD_KNOBS];
-  double *knobs = &knob_storage[0];
-  CCOLAMD_NAME (_set_defaults) (knobs);
-
-  // Check for user-passed knobs
-  if (nargin > 1)
-    {
-      NDArray User_knobs = args(1).array_value ();
-      int nel_User_knobs = User_knobs.numel ();
-
-      if (nel_User_knobs > 0)
-        knobs[CCOLAMD_DENSE_ROW] = User_knobs(0);
-      if (nel_User_knobs > 1)
-        knobs[CCOLAMD_AGGRESSIVE] = User_knobs(1);
-      if (nel_User_knobs > 2)
-        spumoni = static_cast<int> (User_knobs(2));
-
-      // print knob settings if spumoni is set
-      if (spumoni)
-        {
-          octave_stdout << "\ncsymamd version " << CCOLAMD_MAIN_VERSION
-                        << '.' << CCOLAMD_SUB_VERSION
-                        << ", " << CCOLAMD_DATE << "\n";
-
-          if (knobs[CCOLAMD_DENSE_ROW] >= 0)
-            octave_stdout << "knobs(1): " << User_knobs(0)
-                          << ", rows/cols with > max (16,"
-                          << knobs[CCOLAMD_DENSE_ROW]
-                          << "*sqrt (size(A,2)))"
-                          << " entries removed\n";
-          else
-            octave_stdout << "knobs(1): " << User_knobs(0)
-                          << ", no dense rows/cols removed\n";
-
-          if (knobs[CCOLAMD_AGGRESSIVE] != 0)
-            octave_stdout << "knobs(2): " << User_knobs(1)
-                          << ", aggressive absorption: yes";
-          else
-            octave_stdout << "knobs(2): " << User_knobs(1)
-                          << ", aggressive absorption: no";
-
-          octave_stdout << "knobs(3): " << User_knobs(2)
-                        << ", statistics and knobs printed\n";
-        }
-    }
-
-  octave_idx_type n_row, n_col;
-  octave_idx_type *ridx, *cidx;
-  SparseMatrix sm;
-  SparseComplexMatrix scm;
-
-  if (args(0).issparse ())
-    {
-      if (args(0).iscomplex ())
-        {
-          scm = args(0).sparse_complex_matrix_value ();
-          n_row = scm.rows ();
-          n_col = scm.cols ();
-          ridx = scm.xridx ();
-          cidx = scm.xcidx ();
-        }
-      else
-        {
-          sm = args(0).sparse_matrix_value ();
-          n_row = sm.rows ();
-          n_col = sm.cols ();
-          ridx = sm.xridx ();
-          cidx = sm.xcidx ();
-        }
-    }
-  else
-    {
-      if (args(0).iscomplex ())
-        sm = SparseMatrix (real (args(0).complex_matrix_value ()));
-      else
-        sm = SparseMatrix (args(0).matrix_value ());
-
-      n_row = sm.rows ();
-      n_col = sm.cols ();
-      ridx = sm.xridx ();
-      cidx = sm.xcidx ();
-    }
-
-  if (n_row != n_col)
-    err_square_matrix_required ("csymamd", "S");
-
-  // Allocate workspace for symamd
-  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, perm, n_col+1);
-  static_assert (CCOLAMD_STATS <= 40,
-                 "csymamd: # of CCOLAMD_STATS exceeded.  Please report this to bugs.octave.org");
-  octave::suitesparse_integer stats_storage[CCOLAMD_STATS];
-  octave::suitesparse_integer *stats = &stats_storage[0];
-
-  if (nargin > 2)
-    {
-      NDArray in_cmember = args(2).array_value ();
-      octave_idx_type cslen = in_cmember.numel ();
-      OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, cmember, cslen);
-      for (octave_idx_type i = 0; i < cslen; i++)
-        // convert cmember from 1-based to 0-based
-        cmember[i] = static_cast<octave_idx_type> (in_cmember(i) - 1);
-
-      if (cslen != n_col)
-        error ("csymamd: CMEMBER must be of length equal to #cols of A");
-
-      if (! CSYMAMD_NAME () (n_col,
-                             octave::to_suitesparse_intptr (ridx),
-                             octave::to_suitesparse_intptr (cidx),
-                             perm, knobs, stats, &calloc, &free, cmember, -1))
-        {
-          CSYMAMD_NAME (_report)(stats);
-
-          error ("csymamd: internal error!");
-        }
-    }
-  else
-    {
-      if (! CSYMAMD_NAME () (n_col,
-                             octave::to_suitesparse_intptr (ridx),
-                             octave::to_suitesparse_intptr (cidx),
-                             perm, knobs, stats, &calloc, &free, nullptr, -1))
-        {
-          CSYMAMD_NAME (_report)(stats);
-
-          error ("csymamd: internal error!");
-        }
-    }
-
-  // return the permutation vector
-  NDArray out_perm (dim_vector (1, n_col));
-  for (octave_idx_type i = 0; i < n_col; i++)
-    out_perm(i) = perm[i] + 1;
-
-  retval(0) = out_perm;
-
-  // print stats if spumoni > 0
-  if (spumoni > 0)
-    CSYMAMD_NAME (_report)(stats);
-
-  // Return the stats vector
-  if (nargout == 2)
-    {
-      NDArray out_stats (dim_vector (1, CCOLAMD_STATS));
-      for (octave_idx_type i = 0 ; i < CCOLAMD_STATS ; i++)
-        out_stats(i) = stats[i];
-      retval(1) = out_stats;
-
-      // fix stats (5) and (6), for 1-based information on
-      // jumbled matrix.  note that this correction doesn't
-      // occur if symamd returns FALSE
-      out_stats(CCOLAMD_INFO1)++;
-      out_stats(CCOLAMD_INFO2)++;
-    }
-
-  return retval;
-
-#else
-
-  octave_unused_parameter (args);
-  octave_unused_parameter (nargout);
-
-  err_disabled_feature ("csymamd", "CCOLAMD");
-
-#endif
-}
--- a/libinterp/dldfcn/chol.cc	Sat Jan 25 15:26:07 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1331 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 1996-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include <string>
-
-#include "Matrix.h"
-#include "chol.h"
-#include "oct-string.h"
-#include "sparse-chol.h"
-#include "sparse-util.h"
-
-#include "defun-dld.h"
-#include "error.h"
-#include "errwarn.h"
-#include "ov.h"
-#include "ovl.h"
-
-template <typename CHOLT>
-static octave_value
-get_chol (const CHOLT& fact)
-{
-  return octave_value (fact.chol_matrix());
-}
-
-template <typename CHOLT>
-static octave_value
-get_chol_r (const CHOLT& fact)
-{
-  return octave_value (fact.chol_matrix (),
-                       MatrixType (MatrixType::Upper));
-}
-
-template <typename CHOLT>
-static octave_value
-get_chol_l (const CHOLT& fact)
-{
-  return octave_value (fact.chol_matrix ().transpose (),
-                       MatrixType (MatrixType::Lower));
-}
-
-DEFUN_DLD (chol, args, nargout,
-           doc: /* -*- texinfo -*-
-@deftypefn  {} {@var{R} =} chol (@var{A})
-@deftypefnx {} {[@var{R}, @var{p}] =} chol (@var{A})
-@deftypefnx {} {[@var{R}, @var{p}, @var{Q}] =} chol (@var{A})
-@deftypefnx {} {[@var{R}, @var{p}, @var{Q}] =} chol (@var{A}, "vector")
-@deftypefnx {} {[@var{L}, @dots{}] =} chol (@dots{}, "lower")
-@deftypefnx {} {[@var{R}, @dots{}] =} chol (@dots{}, "upper")
-@cindex Cholesky factorization
-Compute the upper Cholesky@tie{}factor, @var{R}, of the real symmetric
-or complex Hermitian positive definite matrix @var{A}.
-
-The upper Cholesky@tie{}factor @var{R} is computed by using the upper
-triangular part of matrix @var{A} and is defined by
-@tex
-$ R^T R = A $.
-@end tex
-@ifnottex
-
-@example
-@var{R}' * @var{R} = @var{A}.
-@end example
-
-@end ifnottex
-
-Calling @code{chol} using the optional @qcode{"upper"} flag has the
-same behavior.  In contrast, using the optional @qcode{"lower"} flag,
-@code{chol} returns the lower triangular factorization, computed by using
-the lower triangular part of matrix @var{A}, such that
-@tex
-$ L L^T = A $.
-@end tex
-@ifnottex
-
-@example
-@var{L} * @var{L}' = @var{A}.
-@end example
-
-@end ifnottex
-
-Called with one output argument @code{chol} fails if matrix @var{A} is
-not positive definite.  Note that if matrix @var{A} is not real symmetric
-or complex Hermitian then the lower triangular part is considered to be
-the (complex conjugate) transpose of the upper triangular part, or vice
-versa, given the @qcode{"lower"} flag.
-
-Called with two or more output arguments @var{p} flags whether the matrix
-@var{A} was positive definite and @code{chol} does not fail.  A zero value
-of @var{p} indicates that matrix @var{A} is positive definite and @var{R}
-gives the factorization.  Otherwise, @var{p} will have a positive value.
-
-If called with three output arguments matrix @var{A} must be sparse and
-a sparsity preserving row/column permutation is applied to matrix @var{A}
-prior to the factorization.  That is @var{R} is the factorization of
-@code{@var{A}(@var{Q},@var{Q})} such that
-@tex
-$ R^T R = Q^T A Q$.
-@end tex
-@ifnottex
-
-@example
-@var{R}' * @var{R} = @var{Q}' * @var{A} * @var{Q}.
-@end example
-
-@end ifnottex
-
-The sparsity preserving permutation is generally returned as a matrix.
-However, given the optional flag @qcode{"vector"}, @var{Q} will be
-returned as a vector such that
-@tex
-$ R^T R = A (Q, Q)$.
-@end tex
-@ifnottex
-
-@example
-@var{R}' * @var{R} = @var{A}(@var{Q}, @var{Q}).
-@end example
-
-@end ifnottex
-
-In general the lower triangular factorization is significantly faster for
-sparse matrices.
-@seealso{hess, lu, qr, qz, schur, svd, ichol, cholinv, chol2inv, cholupdate, cholinsert, choldelete, cholshift}
-@end deftypefn */)
-{
-  int nargin = args.length ();
-
-  if (nargin < 1 || nargin > 3 || nargout > 3)
-    print_usage ();
-  if (nargout > 2 && ! args(0).issparse ())
-    error ("chol: using three output arguments, matrix A must be sparse");
-
-  bool LLt = false;
-  bool vecout = false;
-
-  int n = 1;
-  while (n < nargin)
-    {
-      std::string tmp = args(n++).xstring_value ("chol: optional arguments must be strings");
-
-      if (octave::string::strcmpi (tmp, "vector"))
-        vecout = true;
-      else if (octave::string::strcmpi (tmp, "lower"))
-        LLt = true;
-      else if (octave::string::strcmpi (tmp, "upper"))
-        LLt = false;
-      else
-        error (R"(chol: optional argument must be one of "vector", "lower", or "upper")");
-    }
-
-  octave_value_list retval;
-  octave_value arg = args(0);
-
-  if (arg.isempty ())
-    return ovl (Matrix ());
-
-  if (arg.issparse ())
-    {
-      octave_idx_type info;
-      bool natural = (nargout != 3);
-      bool force = nargout > 1;
-
-      if (arg.isreal ())
-        {
-          SparseMatrix m = arg.sparse_matrix_value ();
-
-          octave::math::sparse_chol<SparseMatrix> fact (m, info, natural, force);
-
-          if (nargout == 3)
-            {
-              if (vecout)
-                retval(2) = fact.perm ();
-              else
-                retval(2) = fact.Q ();
-            }
-
-          if (nargout >= 2 || info == 0)
-            {
-              retval(1) = info;
-              if (LLt)
-                retval(0) = fact.L ();
-              else
-                retval(0) = fact.R ();
-            }
-          else
-            error ("chol: input matrix must be positive definite");
-        }
-      else if (arg.iscomplex ())
-        {
-          SparseComplexMatrix m = arg.sparse_complex_matrix_value ();
-
-          octave::math::sparse_chol<SparseComplexMatrix> fact (m, info, natural, force);
-
-          if (nargout == 3)
-            {
-              if (vecout)
-                retval(2) = fact.perm ();
-              else
-                retval(2) = fact.Q ();
-            }
-
-          if (nargout >= 2 || info == 0)
-            {
-              retval(1) = info;
-              if (LLt)
-                retval(0) = fact.L ();
-              else
-                retval(0) = fact.R ();
-            }
-          else
-            error ("chol: input matrix must be positive definite");
-        }
-      else
-        err_wrong_type_arg ("chol", arg);
-    }
-  else if (arg.is_single_type ())
-    {
-      if (vecout)
-        error (R"(chol: A must be sparse for the "vector" option)");
-      if (arg.isreal ())
-        {
-          FloatMatrix m = arg.float_matrix_value ();
-
-          octave_idx_type info;
-
-          octave::math::chol<FloatMatrix> fact (m, info, LLt != true);
-
-          if (nargout == 2 || info == 0)
-            retval = ovl (get_chol (fact), info);
-          else
-            error ("chol: input matrix must be positive definite");
-        }
-      else if (arg.iscomplex ())
-        {
-          FloatComplexMatrix m = arg.float_complex_matrix_value ();
-
-          octave_idx_type info;
-
-          octave::math::chol<FloatComplexMatrix> fact (m, info, LLt != true);
-
-          if (nargout == 2 || info == 0)
-            retval = ovl (get_chol (fact), info);
-          else
-            error ("chol: input matrix must be positive definite");
-        }
-      else
-        err_wrong_type_arg ("chol", arg);
-    }
-  else
-    {
-      if (vecout)
-        error (R"(chol: A must be sparse for the "vector" option)");
-      if (arg.isreal ())
-        {
-          Matrix m = arg.matrix_value ();
-
-          octave_idx_type info;
-
-          octave::math::chol<Matrix> fact (m, info, LLt != true);
-
-          if (nargout == 2 || info == 0)
-            retval = ovl (get_chol (fact), info);
-          else
-            error ("chol: input matrix must be positive definite");
-        }
-      else if (arg.iscomplex ())
-        {
-          ComplexMatrix m = arg.complex_matrix_value ();
-
-          octave_idx_type info;
-
-          octave::math::chol<ComplexMatrix> fact (m, info, LLt != true);
-
-          if (nargout == 2 || info == 0)
-            retval = ovl (get_chol (fact), info);
-          else
-            error ("chol: input matrix must be positive definite");
-        }
-      else
-        err_wrong_type_arg ("chol", arg);
-    }
-
-  return retval;
-}
-
-/*
-%!assert (chol ([2, 1; 1, 1]), [sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)], sqrt (eps))
-%!assert (chol (single ([2, 1; 1, 1])), single ([sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)]), sqrt (eps ("single")))
-
-%!assert (chol ([2, 1; 1, 1], "upper"), [sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)],
-%!        sqrt (eps))
-%!assert (chol ([2, 1; 1, 1], "lower"), [sqrt(2), 0; 1/sqrt(2), 1/sqrt(2)],
-%!        sqrt (eps))
-
-%!assert (chol ([2, 1; 1, 1], "lower"), chol ([2, 1; 1, 1], "LoweR"))
-%!assert (chol ([2, 1; 1, 1], "upper"), chol ([2, 1; 1, 1], "Upper"))
-
-## Check the "vector" option which only affects the 3rd argument and
-## is only valid for sparse input.
-%!testif HAVE_CHOLMOD
-%! a = sparse ([2 1; 1 1]);
-%! r = sparse ([sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)]);
-%! [rd, pd, qd] = chol (a);
-%! [rv, pv, qv] = chol (a, "vector");
-%! assert (r, rd, eps)
-%! assert (r, rv, eps)
-%! assert (pd, 0)
-%! assert (pd, pv)
-%! assert (qd, sparse (eye (2)))
-%! assert (qv, [1 2])
-%!
-%! [rv, pv, qv] = chol (a, "Vector"); # check case sensitivity
-%! assert (r, rv, eps)
-%! assert (pd, pv)
-%! assert (qv, [1 2])
-
-%!testif HAVE_CHOLMOD <*42587>
-%! A = sparse ([1 0 8;0 1 8;8 8 1]);
-%! [Q, p] = chol (A);
-%! assert (p != 0);
-
-%!error chol ()
-%!error <matrix must be positive definite> chol ([1, 2; 3, 4])
-%!error <requires square matrix> chol ([1, 2; 3, 4; 5, 6])
-%!error <optional arguments must be strings> chol (1, 2)
-%!error <optional argument must be one of "vector", "lower"> chol (1, "foobar")
-%!error <matrix A must be sparse> [L,p,Q] = chol ([1, 2; 3, 4])
-%!error <A must be sparse> [L, p] = chol ([1, 2; 3, 4], "vector")
-*/
-
-DEFUN_DLD (cholinv, args, ,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {} cholinv (@var{A})
-Compute the inverse of the symmetric positive definite matrix @var{A} using
-the Cholesky@tie{}factorization.
-@seealso{chol, chol2inv, inv}
-@end deftypefn */)
-{
-  if (args.length () != 1)
-    print_usage ();
-
-  octave_value retval;
-  octave_value arg = args(0);
-
-  octave_idx_type nr = arg.rows ();
-  octave_idx_type nc = arg.columns ();
-
-  if (nr == 0 || nc == 0)
-    retval = Matrix ();
-  else
-    {
-      if (arg.issparse ())
-        {
-          octave_idx_type info;
-
-          if (arg.isreal ())
-            {
-              SparseMatrix m = arg.sparse_matrix_value ();
-
-              octave::math::sparse_chol<SparseMatrix> chol (m, info);
-
-              if (info == 0)
-                retval = chol.inverse ();
-              else
-                error ("cholinv: A must be positive definite");
-            }
-          else if (arg.iscomplex ())
-            {
-              SparseComplexMatrix m = arg.sparse_complex_matrix_value ();
-
-              octave::math::sparse_chol<SparseComplexMatrix> chol (m, info);
-
-              if (info == 0)
-                retval = chol.inverse ();
-              else
-                error ("cholinv: A must be positive definite");
-            }
-          else
-            err_wrong_type_arg ("cholinv", arg);
-        }
-      else if (arg.is_single_type ())
-        {
-          if (arg.isreal ())
-            {
-              FloatMatrix m = arg.float_matrix_value ();
-
-              octave_idx_type info;
-              octave::math::chol<FloatMatrix> chol (m, info);
-              if (info == 0)
-                retval = chol.inverse ();
-              else
-                error ("cholinv: A must be positive definite");
-            }
-          else if (arg.iscomplex ())
-            {
-              FloatComplexMatrix m = arg.float_complex_matrix_value ();
-
-              octave_idx_type info;
-              octave::math::chol<FloatComplexMatrix> chol (m, info);
-              if (info == 0)
-                retval = chol.inverse ();
-              else
-                error ("cholinv: A must be positive definite");
-            }
-          else
-            err_wrong_type_arg ("chol", arg);
-        }
-      else
-        {
-          if (arg.isreal ())
-            {
-              Matrix m = arg.matrix_value ();
-
-              octave_idx_type info;
-              octave::math::chol<Matrix> chol (m, info);
-              if (info == 0)
-                retval = chol.inverse ();
-              else
-                error ("cholinv: A must be positive definite");
-            }
-          else if (arg.iscomplex ())
-            {
-              ComplexMatrix m = arg.complex_matrix_value ();
-
-              octave_idx_type info;
-              octave::math::chol<ComplexMatrix> chol (m, info);
-              if (info == 0)
-                retval = chol.inverse ();
-              else
-                error ("cholinv: A must be positive definite");
-            }
-          else
-            err_wrong_type_arg ("chol", arg);
-        }
-    }
-
-  return retval;
-}
-
-/*
-%!shared A, Ainv
-%! A = [2,0.2;0.2,1];
-%! Ainv = inv (A);
-%!test
-%! Ainv1 = cholinv (A);
-%! assert (norm (Ainv-Ainv1), 0, 1e-10);
-%!testif HAVE_CHOLMOD
-%! Ainv2 = inv (sparse (A));
-%! assert (norm (Ainv-Ainv2), 0, 1e-10);
-%!testif HAVE_CHOLMOD
-%! Ainv3 = cholinv (sparse (A));
-%! assert (norm (Ainv-Ainv3), 0, 1e-10);
-*/
-
-DEFUN_DLD (chol2inv, args, ,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {} chol2inv (@var{U})
-Invert a symmetric, positive definite square matrix from its Cholesky
-decomposition, @var{U}.
-
-Note that @var{U} should be an upper-triangular matrix with positive
-diagonal elements.  @code{chol2inv (@var{U})} provides
-@code{inv (@var{U}'*@var{U})} but it is much faster than using @code{inv}.
-@seealso{chol, cholinv, inv}
-@end deftypefn */)
-{
-  if (args.length () != 1)
-    print_usage ();
-
-  octave_value retval;
-
-  octave_value arg = args(0);
-
-  octave_idx_type nr = arg.rows ();
-  octave_idx_type nc = arg.columns ();
-
-  if (nr == 0 || nc == 0)
-    retval = Matrix ();
-  else
-    {
-      if (arg.issparse ())
-        {
-          if (arg.isreal ())
-            {
-              SparseMatrix r = arg.sparse_matrix_value ();
-
-              retval = octave::math::chol2inv (r);
-            }
-          else if (arg.iscomplex ())
-            {
-              SparseComplexMatrix r = arg.sparse_complex_matrix_value ();
-
-              retval = octave::math::chol2inv (r);
-            }
-          else
-            err_wrong_type_arg ("chol2inv", arg);
-        }
-      else if (arg.is_single_type ())
-        {
-          if (arg.isreal ())
-            {
-              FloatMatrix r = arg.float_matrix_value ();
-
-              retval = octave::math::chol2inv (r);
-            }
-          else if (arg.iscomplex ())
-            {
-              FloatComplexMatrix r = arg.float_complex_matrix_value ();
-
-              retval = octave::math::chol2inv (r);
-            }
-          else
-            err_wrong_type_arg ("chol2inv", arg);
-
-        }
-      else
-        {
-          if (arg.isreal ())
-            {
-              Matrix r = arg.matrix_value ();
-
-              retval = octave::math::chol2inv (r);
-            }
-          else if (arg.iscomplex ())
-            {
-              ComplexMatrix r = arg.complex_matrix_value ();
-
-              retval = octave::math::chol2inv (r);
-            }
-          else
-            err_wrong_type_arg ("chol2inv", arg);
-        }
-    }
-
-  return retval;
-}
-
-/*
-
-## Test for bug #36437
-%!function sparse_chol2inv (T, tol)
-%!  iT = inv (T);
-%!  ciT = chol2inv (chol (T));
-%!  assert (ciT, iT, tol);
-%!  assert (chol2inv (chol ( full (T))), ciT, tol*2);
-%!endfunction
-
-%!testif HAVE_CHOLMOD
-%! A = gallery ("poisson", 3);
-%! sparse_chol2inv (A, eps);
-
-%!testif HAVE_CHOLMOD
-%! n = 10;
-%! B = spdiags (ones (n, 1) * [1 2 1], [-1 0 1], n, n);
-%! sparse_chol2inv (B, eps*100);
-
-%!testif HAVE_CHOLMOD
-%! C = gallery("tridiag", 5);
-%! sparse_chol2inv (C, eps*10);
-
-%!testif HAVE_CHOLMOD
-%! D = gallery("wathen", 1, 1);
-%! sparse_chol2inv (D, eps*10^4);
-
-*/
-
-DEFUN_DLD (cholupdate, args, nargout,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {[@var{R1}, @var{info}] =} cholupdate (@var{R}, @var{u}, @var{op})
-Update or downdate a Cholesky@tie{}factorization.
-
-Given an upper triangular matrix @var{R} and a column vector @var{u},
-attempt to determine another upper triangular matrix @var{R1} such that
-
-@itemize @bullet
-@item
-@var{R1}'*@var{R1} = @var{R}'*@var{R} + @var{u}*@var{u}'
-if @var{op} is @qcode{"+"}
-
-@item
-@var{R1}'*@var{R1} = @var{R}'*@var{R} - @var{u}*@var{u}'
-if @var{op} is @qcode{"-"}
-@end itemize
-
-If @var{op} is @qcode{"-"}, @var{info} is set to
-
-@itemize
-@item 0 if the downdate was successful,
-
-@item 1 if @var{R}'*@var{R} - @var{u}*@var{u}' is not positive definite,
-
-@item 2 if @var{R} is singular.
-@end itemize
-
-If @var{info} is not present, an error message is printed in cases 1 and 2.
-@seealso{chol, cholinsert, choldelete, cholshift}
-@end deftypefn */)
-{
-  int nargin = args.length ();
-
-  if (nargin < 2 || nargin > 3)
-    print_usage ();
-
-  octave_value argr = args(0);
-  octave_value argu = args(1);
-
-  if (! argr.isnumeric () || ! argu.isnumeric ()
-      || (nargin > 2 && ! args(2).is_string ()))
-    print_usage ();
-
-  octave_value_list retval (nargout == 2 ? 2 : 1);
-
-  octave_idx_type n = argr.rows ();
-
-  std::string op = (nargin < 3) ? "+" : args(2).string_value ();
-
-  bool down = (op == "-");
-
-  if (! down && op != "+")
-    error (R"(cholupdate: OP must be "+" or "-")");
-
-  if (argr.columns () != n || argu.rows () != n || argu.columns () != 1)
-    error ("cholupdate: dimension mismatch between R and U");
-
-  int err = 0;
-  if (argr.is_single_type () || argu.is_single_type ())
-    {
-      if (argr.isreal () && argu.isreal ())
-        {
-          // real case
-          FloatMatrix R = argr.float_matrix_value ();
-          FloatColumnVector u = argu.float_column_vector_value ();
-
-          octave::math::chol<FloatMatrix> fact;
-          fact.set (R);
-
-          if (down)
-            err = fact.downdate (u);
-          else
-            fact.update (u);
-
-          retval = ovl (get_chol_r (fact));
-        }
-      else
-        {
-          // complex case
-          FloatComplexMatrix R = argr.float_complex_matrix_value ();
-          FloatComplexColumnVector u
-            = argu.float_complex_column_vector_value ();
-
-          octave::math::chol<FloatComplexMatrix> fact;
-          fact.set (R);
-
-          if (down)
-            err = fact.downdate (u);
-          else
-            fact.update (u);
-
-          retval = ovl (get_chol_r (fact));
-        }
-    }
-  else
-    {
-      if (argr.isreal () && argu.isreal ())
-        {
-          // real case
-          Matrix R = argr.matrix_value ();
-          ColumnVector u = argu.column_vector_value ();
-
-          octave::math::chol<Matrix> fact;
-          fact.set (R);
-
-          if (down)
-            err = fact.downdate (u);
-          else
-            fact.update (u);
-
-          retval = ovl (get_chol_r (fact));
-        }
-      else
-        {
-          // complex case
-          ComplexMatrix R = argr.complex_matrix_value ();
-          ComplexColumnVector u = argu.complex_column_vector_value ();
-
-          octave::math::chol<ComplexMatrix> fact;
-          fact.set (R);
-
-          if (down)
-            err = fact.downdate (u);
-          else
-            fact.update (u);
-
-          retval = ovl (get_chol_r (fact));
-        }
-    }
-
-  if (nargout > 1)
-    retval(1) = err;
-  else if (err == 1)
-    error ("cholupdate: downdate violates positiveness");
-  else if (err == 2)
-    error ("cholupdate: singular matrix");
-
-  return retval;
-}
-
-/*
-%!shared A, u, Ac, uc
-%! A = [  0.436997  -0.131721   0.124120  -0.061673 ;
-%!       -0.131721   0.738529   0.019851  -0.140295 ;
-%!        0.124120   0.019851   0.354879  -0.059472 ;
-%!       -0.061673  -0.140295  -0.059472   0.600939 ];
-%!
-%! u = [  0.98950 ;
-%!        0.39844 ;
-%!        0.63484 ;
-%!        0.13351 ];
-%! Ac = [  0.5585528 + 0.0000000i  -0.1662088 - 0.0315341i   0.0107873 + 0.0236411i  -0.0276775 - 0.0186073i ;
-%!        -0.1662088 + 0.0315341i   0.6760061 + 0.0000000i   0.0011452 - 0.0475528i   0.0145967 + 0.0247641i ;
-%!         0.0107873 - 0.0236411i   0.0011452 + 0.0475528i   0.6263149 - 0.0000000i  -0.1585837 - 0.0719763i ;
-%!        -0.0276775 + 0.0186073i   0.0145967 - 0.0247641i  -0.1585837 + 0.0719763i   0.6034234 - 0.0000000i ];
-%!
-%! uc = [ 0.54267 + 0.91519i ;
-%!        0.99647 + 0.43141i ;
-%!        0.83760 + 0.68977i ;
-%!        0.39160 + 0.90378i ];
-
-%!test
-%! R = chol (A);
-%! R1 = cholupdate (R, u);
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1'*R1 - R'*R - u*u', Inf) < 1e1*eps);
-%!
-%! R1 = cholupdate (R1, u, "-");
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1 - R, Inf) < 1e1*eps);
-
-%!test
-%! R = chol (Ac);
-%! R1 = cholupdate (R, uc);
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1'*R1 - R'*R - uc*uc', Inf) < 1e1*eps);
-%!
-%! R1 = cholupdate (R1, uc, "-");
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1 - R, Inf) < 1e1*eps);
-
-%!test
-%! R = chol (single (A));
-%! R1 = cholupdate (R, single (u));
-%! assert (norm (triu (R1)-R1, Inf), single (0));
-%! assert (norm (R1'*R1 - R'*R - single (u*u'), Inf) < 1e1*eps ("single"));
-%!
-%! R1 = cholupdate (R1, single (u), "-");
-%! assert (norm (triu (R1)-R1, Inf), single (0));
-%! assert (norm (R1 - R, Inf) < 2e1*eps ("single"));
-
-%!test
-%! R = chol (single (Ac));
-%! R1 = cholupdate (R, single (uc));
-%! assert (norm (triu (R1)-R1, Inf), single (0));
-%! assert (norm (R1'*R1 - R'*R - single (uc*uc'), Inf) < 1e1*eps ("single"));
-%!
-%! R1 = cholupdate (R1, single (uc), "-");
-%! assert (norm (triu (R1)-R1, Inf), single (0));
-%! assert (norm (R1 - R, Inf) < 2e1*eps ("single"));
-*/
-
-DEFUN_DLD (cholinsert, args, nargout,
-           doc: /* -*- texinfo -*-
-@deftypefn  {} {@var{R1} =} cholinsert (@var{R}, @var{j}, @var{u})
-@deftypefnx {} {[@var{R1}, @var{info}] =} cholinsert (@var{R}, @var{j}, @var{u})
-Update a Cholesky factorization given a row or column to insert in the
-original factored matrix.
-
-Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian
-positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper
-triangular, return the Cholesky@tie{}factorization of
-@var{A1}, where @w{A1(p,p) = A}, @w{A1(:,j) = A1(j,:)' = u} and
-@w{p = [1:j-1,j+1:n+1]}.  @w{u(j)} should be positive.
-
-On return, @var{info} is set to
-
-@itemize
-@item 0 if the insertion was successful,
-
-@item 1 if @var{A1} is not positive definite,
-
-@item 2 if @var{R} is singular.
-@end itemize
-
-If @var{info} is not present, an error message is printed in cases 1 and 2.
-@seealso{chol, cholupdate, choldelete, cholshift}
-@end deftypefn */)
-{
-  if (args.length () != 3)
-    print_usage ();
-
-  octave_value argr = args(0);
-  octave_value argj = args(1);
-  octave_value argu = args(2);
-
-  if (! argr.isnumeric () || ! argu.isnumeric ()
-      || ! argj.is_real_scalar ())
-    print_usage ();
-
-  octave_idx_type n = argr.rows ();
-  octave_idx_type j = argj.scalar_value ();
-
-  if (argr.columns () != n || argu.rows () != n+1 || argu.columns () != 1)
-    error ("cholinsert: dimension mismatch between R and U");
-
-  if (j < 1 || j > n+1)
-    error ("cholinsert: index J out of range");
-
-  octave_value_list retval (nargout == 2 ? 2 : 1);
-
-  int err = 0;
-  if (argr.is_single_type () || argu.is_single_type ())
-    {
-      if (argr.isreal () && argu.isreal ())
-        {
-          // real case
-          FloatMatrix R = argr.float_matrix_value ();
-          FloatColumnVector u = argu.float_column_vector_value ();
-
-          octave::math::chol<FloatMatrix> fact;
-          fact.set (R);
-          err = fact.insert_sym (u, j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-      else
-        {
-          // complex case
-          FloatComplexMatrix R = argr.float_complex_matrix_value ();
-          FloatComplexColumnVector u
-            = argu.float_complex_column_vector_value ();
-
-          octave::math::chol<FloatComplexMatrix> fact;
-          fact.set (R);
-          err = fact.insert_sym (u, j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-    }
-  else
-    {
-      if (argr.isreal () && argu.isreal ())
-        {
-          // real case
-          Matrix R = argr.matrix_value ();
-          ColumnVector u = argu.column_vector_value ();
-
-          octave::math::chol<Matrix> fact;
-          fact.set (R);
-          err = fact.insert_sym (u, j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-      else
-        {
-          // complex case
-          ComplexMatrix R = argr.complex_matrix_value ();
-          ComplexColumnVector u = argu.complex_column_vector_value ();
-
-          octave::math::chol<ComplexMatrix> fact;
-          fact.set (R);
-          err = fact.insert_sym (u, j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-    }
-
-  if (nargout > 1)
-    retval(1) = err;
-  else if (err == 1)
-    error ("cholinsert: insertion violates positiveness");
-  else if (err == 2)
-    error ("cholinsert: singular matrix");
-  else if (err == 3)
-    error ("cholinsert: diagonal element must be real");
-
-  return retval;
-}
-
-/*
-%!test
-%! u2 = [  0.35080 ;
-%!         0.63930 ;
-%!         3.31057 ;
-%!        -0.13825 ;
-%!         0.45266 ];
-%!
-%! R = chol (A);
-%!
-%! j = 3;  p = [1:j-1, j+1:5];
-%! R1 = cholinsert (R, j, u2);
-%! A1 = R1'*R1;
-%!
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (A1(p,p) - A, Inf) < 1e1*eps);
-
-%!test
-%! u2 = [  0.35080  + 0.04298i;
-%!         0.63930  + 0.23778i;
-%!         3.31057  + 0.00000i;
-%!        -0.13825  + 0.19879i;
-%!         0.45266  + 0.50020i];
-%!
-%! R = chol (Ac);
-%!
-%! j = 3;  p = [1:j-1, j+1:5];
-%! R1 = cholinsert (R, j, u2);
-%! A1 = R1'*R1;
-%!
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (A1(p,p) - Ac, Inf) < 1e1*eps);
-
-%!test
-%! u2 = single ([  0.35080 ;
-%!                 0.63930 ;
-%!                 3.31057 ;
-%!                -0.13825 ;
-%!                 0.45266 ]);
-%!
-%! R = chol (single (A));
-%!
-%! j = 3;  p = [1:j-1, j+1:5];
-%! R1 = cholinsert (R, j, u2);
-%! A1 = R1'*R1;
-%!
-%! assert (norm (triu (R1)-R1, Inf), single (0));
-%! assert (norm (A1(p,p) - A, Inf) < 1e1*eps ("single"));
-
-%!test
-%! u2 = single ([  0.35080  + 0.04298i;
-%!                 0.63930  + 0.23778i;
-%!                 3.31057  + 0.00000i;
-%!                -0.13825  + 0.19879i;
-%!                 0.45266  + 0.50020i]);
-%!
-%! R = chol (single (Ac));
-%!
-%! j = 3;  p = [1:j-1, j+1:5];
-%! R1 = cholinsert (R, j, u2);
-%! A1 = R1'*R1;
-%!
-%! assert (norm (triu (R1)-R1, Inf), single (0));
-%! assert (norm (A1(p,p) - single (Ac), Inf) < 2e1*eps ("single"));
-
-%!test
-%! cu = chol (triu (A), "upper");
-%! cl = chol (tril (A), "lower");
-%! assert (cu, cl', eps);
-
-%!test
-%! cca   = chol (Ac);
-%!
-%! ccal  = chol (Ac, "lower");
-%! ccal2 = chol (tril (Ac), "lower");
-%!
-%! ccau  = chol (Ac, "upper");
-%! ccau2 = chol (triu (Ac), "upper");
-%!
-%! assert (cca'*cca,     Ac, eps);
-%! assert (ccau'*ccau,   Ac, eps);
-%! assert (ccau2'*ccau2, Ac, eps);
-%!
-%! assert (cca, ccal',  eps);
-%! assert (cca, ccau,   eps);
-%! assert (cca, ccal2', eps);
-%! assert (cca, ccau2,  eps);
-
-%!test
-%! cca   = chol (single (Ac));
-%!
-%! ccal  = chol (single (Ac), "lower");
-%! ccal2 = chol (tril (single (Ac)), "lower");
-%!
-%! ccau  = chol (single (Ac), "upper");
-%! ccau2 = chol (triu (single (Ac)), "upper");
-%!
-%! assert (cca'*cca,     single (Ac), eps ("single"));
-%! assert (ccau'*ccau,   single (Ac), eps ("single"));
-%! assert (ccau2'*ccau2, single (Ac), eps ("single"));
-%!
-%! assert (cca, ccal',  eps ("single"));
-%! assert (cca, ccau,   eps ("single"));
-%! assert (cca, ccal2', eps ("single"));
-%! assert (cca, ccau2,  eps ("single"));
-
-%!test
-%! a = [12,  2,  3,  4;
-%!       2, 14,  5,  3;
-%!       3,  5, 16,  6;
-%!       4,  3,  6, 16];
-%!
-%! b = [0,  1,  2,  3;
-%!     -1,  0,  1,  2;
-%!     -2, -1,  0,  1;
-%!     -3, -2, -1,  0];
-%!
-%! ca = a + i*b;
-%!
-%! cca   = chol (ca);
-%!
-%! ccal  = chol (ca, "lower");
-%! ccal2 = chol (tril (ca), "lower");
-%!
-%! ccau  = chol (ca, "upper");
-%! ccau2 = chol (triu (ca), "upper");
-%!
-%! assert (cca'*cca,     ca, 16*eps);
-%! assert (ccau'*ccau,   ca, 16*eps);
-%! assert (ccau2'*ccau2, ca, 16*eps);
-%!
-%! assert (cca, ccal',  16*eps);
-%! assert (cca, ccau,   16*eps);
-%! assert (cca, ccal2', 16*eps);
-%! assert (cca, ccau2,  16*eps);
-*/
-
-DEFUN_DLD (choldelete, args, ,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {@var{R1} =} choldelete (@var{R}, @var{j})
-Update a Cholesky factorization given a row or column to delete from the
-original factored matrix.
-
-Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian
-positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper
-triangular, return the Cholesky@tie{}factorization of @w{A(p,p)}, where
-@w{p = [1:j-1,j+1:n+1]}.
-@seealso{chol, cholupdate, cholinsert, cholshift}
-@end deftypefn */)
-{
-  if (args.length () != 2)
-    print_usage ();
-
-  octave_value argr = args(0);
-  octave_value argj = args(1);
-
-  if (! argr.isnumeric () || ! argj.is_real_scalar ())
-    print_usage ();
-
-  octave_idx_type n = argr.rows ();
-  octave_idx_type j = argj.scalar_value ();
-
-  if (argr.columns () != n)
-    err_square_matrix_required ("choldelete", "R");
-
-  if (j < 0 && j > n)
-    error ("choldelete: index J out of range");
-
-  octave_value_list retval;
-
-  if (argr.is_single_type ())
-    {
-      if (argr.isreal ())
-        {
-          // real case
-          FloatMatrix R = argr.float_matrix_value ();
-
-          octave::math::chol<FloatMatrix> fact;
-          fact.set (R);
-          fact.delete_sym (j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-      else
-        {
-          // complex case
-          FloatComplexMatrix R = argr.float_complex_matrix_value ();
-
-          octave::math::chol<FloatComplexMatrix> fact;
-          fact.set (R);
-          fact.delete_sym (j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-    }
-  else
-    {
-      if (argr.isreal ())
-        {
-          // real case
-          Matrix R = argr.matrix_value ();
-
-          octave::math::chol<Matrix> fact;
-          fact.set (R);
-          fact.delete_sym (j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-      else
-        {
-          // complex case
-          ComplexMatrix R = argr.complex_matrix_value ();
-
-          octave::math::chol<ComplexMatrix> fact;
-          fact.set (R);
-          fact.delete_sym (j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-    }
-
-  return retval;
-}
-
-/*
-%!test
-%! R = chol (A);
-%!
-%! j = 3;  p = [1:j-1,j+1:4];
-%! R1 = choldelete (R, j);
-%!
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps);
-
-%!test
-%! R = chol (Ac);
-%!
-%! j = 3;  p = [1:j-1,j+1:4];
-%! R1 = choldelete (R, j);
-%!
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps);
-
-%!test
-%! R = chol (single (A));
-%!
-%! j = 3;  p = [1:j-1,j+1:4];
-%! R1 = choldelete (R, j);
-%!
-%! assert (norm (triu (R1)-R1, Inf), single (0));
-%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single"));
-
-%!test
-%! R = chol (single (Ac));
-%!
-%! j = 3;  p = [1:j-1,j+1:4];
-%! R1 = choldelete (R,j);
-%!
-%! assert (norm (triu (R1)-R1, Inf), single (0));
-%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single"));
-*/
-
-DEFUN_DLD (cholshift, args, ,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {@var{R1} =} cholshift (@var{R}, @var{i}, @var{j})
-Update a Cholesky factorization given a range of columns to shift in the
-original factored matrix.
-
-Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian
-positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper
-triangular, return the Cholesky@tie{}factorization of
-@w{@var{A}(p,p)}, where @w{p} is the permutation @*
-@code{p = [1:i-1, shift(i:j, 1), j+1:n]} if @w{@var{i} < @var{j}} @*
- or @*
-@code{p = [1:j-1, shift(j:i,-1), i+1:n]} if @w{@var{j} < @var{i}}.  @*
-
-@seealso{chol, cholupdate, cholinsert, choldelete}
-@end deftypefn */)
-{
-  if (args.length () != 3)
-    print_usage ();
-
-  octave_value argr = args(0);
-  octave_value argi = args(1);
-  octave_value argj = args(2);
-
-  if (! argr.isnumeric () || ! argi.is_real_scalar ()
-      || ! argj.is_real_scalar ())
-    print_usage ();
-
-  octave_idx_type n = argr.rows ();
-  octave_idx_type i = argi.scalar_value ();
-  octave_idx_type j = argj.scalar_value ();
-
-  if (argr.columns () != n)
-    err_square_matrix_required ("cholshift", "R");
-
-  if (j < 0 || j > n+1 || i < 0 || i > n+1)
-    error ("cholshift: index I or J is out of range");
-
-  octave_value_list retval;
-
-  if (argr.is_single_type () && argi.is_single_type ()
-      && argj.is_single_type ())
-    {
-      if (argr.isreal ())
-        {
-          // real case
-          FloatMatrix R = argr.float_matrix_value ();
-
-          octave::math::chol<FloatMatrix> fact;
-          fact.set (R);
-          fact.shift_sym (i-1, j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-      else
-        {
-          // complex case
-          FloatComplexMatrix R = argr.float_complex_matrix_value ();
-
-          octave::math::chol<FloatComplexMatrix> fact;
-          fact.set (R);
-          fact.shift_sym (i-1, j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-    }
-  else
-    {
-      if (argr.isreal ())
-        {
-          // real case
-          Matrix R = argr.matrix_value ();
-
-          octave::math::chol<Matrix> fact;
-          fact.set (R);
-          fact.shift_sym (i-1, j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-      else
-        {
-          // complex case
-          ComplexMatrix R = argr.complex_matrix_value ();
-
-          octave::math::chol<ComplexMatrix> fact;
-          fact.set (R);
-          fact.shift_sym (i-1, j-1);
-
-          retval = ovl (get_chol_r (fact));
-        }
-    }
-
-  return retval;
-}
-
-/*
-%!test
-%! R = chol (A);
-%!
-%! i = 1;  j = 3;  p = [1:i-1, shift(i:j,-1), j+1:4];
-%! R1 = cholshift (R, i, j);
-%!
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps);
-%!
-%! j = 1;  i = 3;  p = [1:j-1, shift(j:i,+1), i+1:4];
-%! R1 = cholshift (R, i, j);
-%!
-%! assert (norm (triu (R1) - R1, Inf), 0);
-%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps);
-
-%!test
-%! R = chol (Ac);
-%!
-%! i = 1;  j = 3;  p = [1:i-1, shift(i:j,-1), j+1:4];
-%! R1 = cholshift (R, i, j);
-%!
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps);
-%!
-%! j = 1;  i = 3;  p = [1:j-1, shift(j:i,+1), i+1:4];
-%! R1 = cholshift (R, i, j);
-%!
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps);
-
-%!test
-%! R = chol (single (A));
-%!
-%! i = 1;  j = 3;  p = [1:i-1, shift(i:j,-1), j+1:4];
-%! R1 = cholshift (R, i, j);
-%!
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single"));
-%!
-%! j = 1;  i = 3;  p = [1:j-1, shift(j:i,+1), i+1:4];
-%! R1 = cholshift (R, i, j);
-%!
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single"));
-
-%!test
-%! R = chol (single (Ac));
-%!
-%! i = 1;  j = 3;  p = [1:i-1, shift(i:j,-1), j+1:4];
-%! R1 = cholshift (R, i, j);
-%!
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single"));
-%!
-%! j = 1; i = 3; p = [1:j-1, shift(j:i,+1), i+1:4];
-%! R1 = cholshift (R, i, j);
-%!
-%! assert (norm (triu (R1)-R1, Inf), 0);
-%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single"));
-*/
--- a/libinterp/dldfcn/colamd.cc	Sat Jan 25 15:26:07 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,774 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 1998-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-// This is the octave interface to colamd, which bore the copyright given
-// in the help of the functions.
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include <cstdlib>
-
-#include <string>
-
-#include "CSparse.h"
-#include "dNDArray.h"
-#include "dSparse.h"
-#include "oct-locbuf.h"
-#include "oct-sparse.h"
-
-#include "defun-dld.h"
-#include "error.h"
-#include "errwarn.h"
-#include "ovl.h"
-#include "pager.h"
-
-// The symmetric column elimination tree code take from the Davis LDL code.
-// Copyright given elsewhere in this file.
-static void
-symetree (const octave_idx_type *ridx, const octave_idx_type *cidx,
-          octave_idx_type *Parent, octave_idx_type *P, octave_idx_type n)
-{
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, Flag, n);
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, Pinv, (P ? n : 0));
-  if (P)
-    // If P is present then compute Pinv, the inverse of P
-    for (octave_idx_type k = 0 ; k < n ; k++)
-      Pinv[P[k]] = k;
-
-  for (octave_idx_type k = 0 ; k < n ; k++)
-    {
-      // L(k,:) pattern: all nodes reachable in etree from nz in A(0:k-1,k)
-      Parent[k] = n ;                // parent of k is not yet known
-      Flag[k] = k ;                  // mark node k as visited
-      octave_idx_type kk = (P ? P[k] : k); // kth original, or permuted, column
-      octave_idx_type p2 = cidx[kk+1];
-      for (octave_idx_type p = cidx[kk] ; p < p2 ; p++)
-        {
-          // A (i,k) is nonzero (original or permuted A)
-          octave_idx_type i = (P ? Pinv[ridx[p]] : ridx[p]);
-          if (i < k)
-            {
-              // follow path from i to root of etree, stop at flagged node
-              for ( ; Flag[i] != k ; i = Parent[i])
-                {
-                  // find parent of i if not yet determined
-                  if (Parent[i] == n)
-                    Parent[i] = k;
-                  Flag[i] = k ;        // mark i as visited
-                }
-            }
-        }
-    }
-}
-
-// The elimination tree post-ordering code below is taken from SuperLU
-static inline octave_idx_type
-make_set (octave_idx_type i, octave_idx_type *pp)
-{
-  pp[i] = i;
-  return i;
-}
-
-static inline octave_idx_type
-link (octave_idx_type s, octave_idx_type t, octave_idx_type *pp)
-{
-  pp[s] = t;
-  return t;
-}
-
-static inline octave_idx_type
-find (octave_idx_type i, octave_idx_type *pp)
-{
-  octave_idx_type p = pp[i];
-  octave_idx_type gp = pp[p];
-
-  while (gp != p)
-    {
-      pp[i] = gp;
-      i = gp;
-      p = pp[i];
-      gp = pp[p];
-    }
-
-  return p;
-}
-
-static octave_idx_type
-etdfs (octave_idx_type v, octave_idx_type *first_kid,
-       octave_idx_type *next_kid, octave_idx_type *post,
-       octave_idx_type postnum)
-{
-  for (octave_idx_type w = first_kid[v]; w != -1; w = next_kid[w])
-    postnum = etdfs (w, first_kid, next_kid, post, postnum);
-
-  post[postnum++] = v;
-
-  return postnum;
-}
-
-static void
-tree_postorder (octave_idx_type n, octave_idx_type *parent,
-                octave_idx_type *post)
-{
-  // Allocate storage for working arrays and results
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, first_kid, n+1);
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, next_kid, n+1);
-
-  // Set up structure describing children
-  for (octave_idx_type v = 0; v <= n; first_kid[v++] = -1)
-    ; // do nothing
-
-  for (octave_idx_type v = n-1; v >= 0; v--)
-    {
-      octave_idx_type dad = parent[v];
-      next_kid[v] = first_kid[dad];
-      first_kid[dad] = v;
-    }
-
-  // Depth-first search from dummy root vertex #n
-  etdfs (n, first_kid, next_kid, post, 0);
-}
-
-static void
-coletree (const octave_idx_type *ridx, const octave_idx_type *colbeg,
-          octave_idx_type *colend, octave_idx_type *parent,
-          octave_idx_type nr, octave_idx_type nc)
-{
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, root, nc);
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, pp, nc);
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, firstcol, nr);
-
-  // Compute firstcol[row] = first nonzero column in row
-  for (octave_idx_type row = 0; row < nr; firstcol[row++] = nc)
-    ; // do nothing
-
-  for (octave_idx_type col = 0; col < nc; col++)
-    for (octave_idx_type p = colbeg[col]; p < colend[col]; p++)
-      {
-        octave_idx_type row = ridx[p];
-        if (firstcol[row] > col)
-          firstcol[row] = col;
-      }
-
-  // Compute etree by Liu's algorithm for symmetric matrices,
-  // except use (firstcol[r],c) in place of an edge (r,c) of A.
-  // Thus each row clique in A'*A is replaced by a star
-  // centered at its first vertex, which has the same fill.
-  for (octave_idx_type col = 0; col < nc; col++)
-    {
-      octave_idx_type cset = make_set (col, pp);
-      root[cset] = col;
-      parent[col] = nc;
-      for (octave_idx_type p = colbeg[col]; p < colend[col]; p++)
-        {
-          octave_idx_type row = firstcol[ridx[p]];
-          if (row >= col)
-            continue;
-          octave_idx_type rset = find (row, pp);
-          octave_idx_type rroot = root[rset];
-          if (rroot != col)
-            {
-              parent[rroot] = col;
-              cset = link (cset, rset, pp);
-              root[cset] = col;
-            }
-        }
-    }
-}
-
-DEFUN_DLD (colamd, args, nargout,
-           doc: /* -*- texinfo -*-
-@deftypefn  {} {@var{p} =} colamd (@var{S})
-@deftypefnx {} {@var{p} =} colamd (@var{S}, @var{knobs})
-@deftypefnx {} {[@var{p}, @var{stats}] =} colamd (@var{S})
-@deftypefnx {} {[@var{p}, @var{stats}] =} colamd (@var{S}, @var{knobs})
-
-Compute the column approximate minimum degree permutation.
-
-@code{@var{p} = colamd (@var{S})} returns the column approximate minimum
-degree permutation vector for the sparse matrix @var{S}.  For a
-non-symmetric matrix @var{S}, @code{@var{S}(:,@var{p})} tends to have
-sparser LU@tie{}factors than @var{S}.  The Cholesky@tie{}factorization of
-@code{@var{S}(:,@var{p})' * @var{S}(:,@var{p})} also tends to be sparser
-than that of @code{@var{S}' * @var{S}}.
-
-@var{knobs} is an optional one- to three-element input vector.  If @var{S}
-is m-by-n, then rows with more than @code{max(16,@var{knobs}(1)*sqrt(n))}
-entries are ignored.  Columns with more than
-@code{max (16,@var{knobs}(2)*sqrt(min(m,n)))} entries are removed prior to
-ordering, and ordered last in the output permutation @var{p}.  Only
-completely dense rows or columns are removed if @code{@var{knobs}(1)} and
-@code{@var{knobs}(2)} are < 0, respectively.  If @code{@var{knobs}(3)} is
-nonzero, @var{stats} and @var{knobs} are printed.  The default is
-@code{@var{knobs} = [10 10 0]}.  Note that @var{knobs} differs from earlier
-versions of colamd.
-
-@var{stats} is an optional 20-element output vector that provides data
-about the ordering and the validity of the input matrix @var{S}.  Ordering
-statistics are in @code{@var{stats}(1:3)}.  @code{@var{stats}(1)} and
-@code{@var{stats}(2)} are the number of dense or empty rows and columns
-ignored by @sc{colamd} and @code{@var{stats}(3)} is the number of garbage
-collections performed on the internal data structure used by @sc{colamd}
-(roughly of size @code{2.2 * nnz(@var{S}) + 4 * @var{m} + 7 * @var{n}}
-integers).
-
-Octave built-in functions are intended to generate valid sparse matrices,
-with no duplicate entries, with ascending row indices of the nonzeros
-in each column, with a non-negative number of entries in each column (!)
-and so on.  If a matrix is invalid, then @sc{colamd} may or may not be able
-to continue.  If there are duplicate entries (a row index appears two or
-more times in the same column) or if the row indices in a column are out
-of order, then @sc{colamd} can correct these errors by ignoring the
-duplicate entries and sorting each column of its internal copy of the
-matrix @var{S} (the input matrix @var{S} is not repaired, however).  If a
-matrix is invalid in other ways then @sc{colamd} cannot continue, an error
-message is printed, and no output arguments (@var{p} or @var{stats}) are
-returned.
-@sc{colamd} is thus a simple way to check a sparse matrix to see if it's
-valid.
-
-@code{@var{stats}(4:7)} provide information if @sc{colamd} was able to
-continue.  The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if
-invalid.  @code{@var{stats}(5)} is the rightmost column index that is
-unsorted or contains duplicate entries, or zero if no such column exists.
-@code{@var{stats}(6)} is the last seen duplicate or out-of-order row
-index in the column index given by @code{@var{stats}(5)}, or zero if no
-such row index exists.  @code{@var{stats}(7)} is the number of duplicate
-or out-of-order row indices.  @code{@var{stats}(8:20)} is always zero in
-the current version of @sc{colamd} (reserved for future use).
-
-The ordering is followed by a column elimination tree post-ordering.
-
-The authors of the code itself are @nospell{Stefan I. Larimore} and
-@nospell{Timothy A. Davis}.  The algorithm was developed in collaboration with
-@nospell{John Gilbert}, Xerox PARC, and @nospell{Esmond Ng}, Oak Ridge National
-Laboratory.  (see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html})
-@seealso{colperm, symamd, ccolamd}
-@end deftypefn */)
-{
-#if defined (HAVE_COLAMD)
-
-  int nargin = args.length ();
-
-  if (nargin < 1 || nargin > 2)
-    print_usage ();
-
-  octave_value_list retval (nargout == 2 ? 2 : 1);
-  int spumoni = 0;
-
-  // Get knobs
-  static_assert (COLAMD_KNOBS <= 40,
-                 "colamd: # of COLAMD_KNOBS exceeded.  Please report this to bugs.octave.org");
-  double knob_storage[COLAMD_KNOBS];
-  double *knobs = &knob_storage[0];
-  COLAMD_NAME (_set_defaults) (knobs);
-
-  // Check for user-passed knobs
-  if (nargin == 2)
-    {
-      NDArray User_knobs = args(1).array_value ();
-      int nel_User_knobs = User_knobs.numel ();
-
-      if (nel_User_knobs > 0)
-        knobs[COLAMD_DENSE_ROW] = User_knobs(0);
-      if (nel_User_knobs > 1)
-        knobs[COLAMD_DENSE_COL] = User_knobs(1);
-      if (nel_User_knobs > 2)
-        spumoni = static_cast<int> (User_knobs(2));
-
-      // print knob settings if spumoni is set
-      if (spumoni)
-        {
-
-          octave_stdout << "\ncolamd version " << COLAMD_MAIN_VERSION
-                        << '.' <<  COLAMD_SUB_VERSION
-                        << ", " << COLAMD_DATE << ":\n";
-
-          if (knobs[COLAMD_DENSE_ROW] >= 0)
-            octave_stdout << "knobs(1): " << User_knobs (0)
-                          << ", rows with > max (16,"
-                          << knobs[COLAMD_DENSE_ROW] << "*sqrt (size(A,2)))"
-                          << " entries removed\n";
-          else
-            octave_stdout << "knobs(1): " << User_knobs (0)
-                          << ", only completely dense rows removed\n";
-
-          if (knobs[COLAMD_DENSE_COL] >= 0)
-            octave_stdout << "knobs(2): " << User_knobs (1)
-                          << ", cols with > max (16,"
-                          << knobs[COLAMD_DENSE_COL] << "*sqrt (size(A)))"
-                          << " entries removed\n";
-          else
-            octave_stdout << "knobs(2): " << User_knobs (1)
-                          << ", only completely dense columns removed\n";
-
-          octave_stdout << "knobs(3): " << User_knobs (2)
-                        << ", statistics and knobs printed\n";
-
-        }
-    }
-
-  octave_idx_type n_row, n_col, nnz;
-  octave_idx_type *ridx, *cidx;
-  SparseComplexMatrix scm;
-  SparseMatrix sm;
-
-  if (args(0).issparse ())
-    {
-      if (args(0).iscomplex ())
-        {
-          scm = args(0).sparse_complex_matrix_value ();
-          n_row = scm.rows ();
-          n_col = scm.cols ();
-          nnz = scm.nnz ();
-          ridx = scm.xridx ();
-          cidx = scm.xcidx ();
-        }
-      else
-        {
-          sm = args(0).sparse_matrix_value ();
-
-          n_row = sm.rows ();
-          n_col = sm.cols ();
-          nnz = sm.nnz ();
-          ridx = sm.xridx ();
-          cidx = sm.xcidx ();
-        }
-    }
-  else
-    {
-      if (args(0).iscomplex ())
-        sm = SparseMatrix (real (args(0).complex_matrix_value ()));
-      else
-        sm = SparseMatrix (args(0).matrix_value ());
-
-      n_row = sm.rows ();
-      n_col = sm.cols ();
-      nnz = sm.nnz ();
-      ridx = sm.xridx ();
-      cidx = sm.xcidx ();
-    }
-
-  // Allocate workspace for colamd
-  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, p, n_col+1);
-  for (octave_idx_type i = 0; i < n_col+1; i++)
-    p[i] = cidx[i];
-
-  octave_idx_type Alen = COLAMD_NAME (_recommended) (nnz, n_row, n_col);
-  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, A, Alen);
-  for (octave_idx_type i = 0; i < nnz; i++)
-    A[i] = ridx[i];
-
-  // Order the columns (destroys A)
-  static_assert (COLAMD_STATS <= 40,
-                 "colamd: # of COLAMD_STATS exceeded.  Please report this to bugs.octave.org");
-  octave::suitesparse_integer stats_storage[COLAMD_STATS];
-  octave::suitesparse_integer *stats = &stats_storage[0];
-  if (! COLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats))
-    {
-      COLAMD_NAME (_report)(stats);
-
-      error ("colamd: internal error!");
-    }
-
-  // column elimination tree post-ordering (reuse variables)
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, colbeg, n_col + 1);
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, colend, n_col + 1);
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1);
-
-  for (octave_idx_type i = 0; i < n_col; i++)
-    {
-      colbeg[i] = cidx[p[i]];
-      colend[i] = cidx[p[i]+1];
-    }
-
-  coletree (ridx, colbeg, colend, etree, n_row, n_col);
-
-  // Calculate the tree post-ordering
-  tree_postorder (n_col, etree, colbeg);
-
-  // return the permutation vector
-  NDArray out_perm (dim_vector (1, n_col));
-  for (octave_idx_type i = 0; i < n_col; i++)
-    out_perm(i) = p[colbeg[i]] + 1;
-
-  retval(0) = out_perm;
-
-  // print stats if spumoni > 0
-  if (spumoni > 0)
-    COLAMD_NAME (_report)(stats);
-
-  // Return the stats vector
-  if (nargout == 2)
-    {
-      NDArray out_stats (dim_vector (1, COLAMD_STATS));
-      for (octave_idx_type i = 0 ; i < COLAMD_STATS ; i++)
-        out_stats(i) = stats[i];
-      retval(1) = out_stats;
-
-      // fix stats (5) and (6), for 1-based information on
-      // jumbled matrix.  note that this correction doesn't
-      // occur if symamd returns FALSE
-      out_stats(COLAMD_INFO1)++;
-      out_stats(COLAMD_INFO2)++;
-    }
-
-  return retval;
-
-#else
-
-  octave_unused_parameter (args);
-  octave_unused_parameter (nargout);
-
-  err_disabled_feature ("colamd", "COLAMD");
-
-#endif
-}
-
-DEFUN_DLD (symamd, args, nargout,
-           doc: /* -*- texinfo -*-
-@deftypefn  {} {@var{p} =} symamd (@var{S})
-@deftypefnx {} {@var{p} =} symamd (@var{S}, @var{knobs})
-@deftypefnx {} {[@var{p}, @var{stats}] =} symamd (@var{S})
-@deftypefnx {} {[@var{p}, @var{stats}] =} symamd (@var{S}, @var{knobs})
-
-For a symmetric positive definite matrix @var{S}, returns the permutation
-vector p such that @code{@var{S}(@var{p}, @var{p})} tends to have a
-sparser Cholesky@tie{}factor than @var{S}.
-
-Sometimes @code{symamd} works well for symmetric indefinite matrices too.
-The matrix @var{S} is assumed to be symmetric; only the strictly lower
-triangular part is referenced.  @var{S} must be square.
-
-@var{knobs} is an optional one- to two-element input vector.  If @var{S} is
-n-by-n, then rows and columns with more than
-@code{max (16,@var{knobs}(1)*sqrt(n))} entries are removed prior to
-ordering, and ordered last in the output permutation @var{p}.  No
-rows/columns are removed if @code{@var{knobs}(1) < 0}.  If
-@code{@var{knobs}(2)} is nonzero, @var{stats} and @var{knobs} are
-printed.  The default is @code{@var{knobs} = [10 0]}.  Note that
-@var{knobs} differs from earlier versions of @code{symamd}.
-
-@var{stats} is an optional 20-element output vector that provides data
-about the ordering and the validity of the input matrix @var{S}.  Ordering
-statistics are in @code{@var{stats}(1:3)}.
-@code{@var{stats}(1) = @var{stats}(2)} is the number of dense or empty rows
-and columns ignored by SYMAMD and @code{@var{stats}(3)} is the number of
-garbage collections performed on the internal data structure used by SYMAMD
-(roughly of size @code{8.4 * nnz (tril (@var{S}, -1)) + 9 * @var{n}}
-integers).
-
-Octave built-in functions are intended to generate valid sparse matrices,
-with no duplicate entries, with ascending row indices of the nonzeros
-in each column, with a non-negative number of entries in each column (!)
-and so on.  If a matrix is invalid, then SYMAMD may or may not be able
-to continue.  If there are duplicate entries (a row index appears two or
-more times in the same column) or if the row indices in a column are out
-of order, then SYMAMD can correct these errors by ignoring the duplicate
-entries and sorting each column of its internal copy of the matrix S (the
-input matrix S is not repaired, however).  If a matrix is invalid in
-other ways then SYMAMD cannot continue, an error message is printed, and
-no output arguments (@var{p} or @var{stats}) are returned.  SYMAMD is
-thus a simple way to check a sparse matrix to see if it's valid.
-
-@code{@var{stats}(4:7)} provide information if SYMAMD was able to
-continue.  The matrix is OK if @code{@var{stats} (4)} is zero, or 1
-if invalid.  @code{@var{stats}(5)} is the rightmost column index that
-is unsorted or contains duplicate entries, or zero if no such column
-exists.  @code{@var{stats}(6)} is the last seen duplicate or out-of-order
-row index in the column index given by @code{@var{stats}(5)}, or zero
-if no such row index exists.  @code{@var{stats}(7)} is the number of
-duplicate or out-of-order row indices.  @code{@var{stats}(8:20)} is
-always zero in the current version of SYMAMD (reserved for future use).
-
-The ordering is followed by a column elimination tree post-ordering.
-
-The authors of the code itself are @nospell{Stefan I. Larimore} and
-@nospell{Timothy A. Davis}.  The algorithm was developed in collaboration with
-@nospell{John Gilbert}, Xerox PARC, and @nospell{Esmond Ng}, Oak Ridge National
-Laboratory.  (see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html})
-@seealso{colperm, colamd}
-@end deftypefn */)
-{
-#if defined (HAVE_COLAMD)
-
-  int nargin = args.length ();
-
-  if (nargin < 1 || nargin > 2)
-    print_usage ();
-
-  octave_value_list retval (nargin == 2 ? 2 : 1);
-  int spumoni = 0;
-
-  // Get knobs
-  static_assert (COLAMD_KNOBS <= 40,
-                 "symamd: # of COLAMD_KNOBS exceeded.  Please report this to bugs.octave.org");
-  double knob_storage[COLAMD_KNOBS];
-  double *knobs = &knob_storage[0];
-  COLAMD_NAME (_set_defaults) (knobs);
-
-  // Check for user-passed knobs
-  if (nargin == 2)
-    {
-      NDArray User_knobs = args(1).array_value ();
-      int nel_User_knobs = User_knobs.numel ();
-
-      if (nel_User_knobs > 0)
-        knobs[COLAMD_DENSE_ROW] = User_knobs(COLAMD_DENSE_ROW);
-      if (nel_User_knobs > 1)
-        spumoni = static_cast<int> (User_knobs (1));
-    }
-
-  // print knob settings if spumoni is set
-  if (spumoni > 0)
-    octave_stdout << "symamd: dense row/col fraction: "
-                  << knobs[COLAMD_DENSE_ROW] << std::endl;
-
-  octave_idx_type n_row, n_col;
-  octave_idx_type *ridx, *cidx;
-  SparseMatrix sm;
-  SparseComplexMatrix scm;
-
-  if (args(0).issparse ())
-    {
-      if (args(0).iscomplex ())
-        {
-          scm = args(0).sparse_complex_matrix_value ();
-          n_row = scm.rows ();
-          n_col = scm.cols ();
-          ridx = scm.xridx ();
-          cidx = scm.xcidx ();
-        }
-      else
-        {
-          sm = args(0).sparse_matrix_value ();
-          n_row = sm.rows ();
-          n_col = sm.cols ();
-          ridx = sm.xridx ();
-          cidx = sm.xcidx ();
-        }
-    }
-  else
-    {
-      if (args(0).iscomplex ())
-        sm = SparseMatrix (real (args(0).complex_matrix_value ()));
-      else
-        sm = SparseMatrix (args(0).matrix_value ());
-
-      n_row = sm.rows ();
-      n_col = sm.cols ();
-      ridx = sm.xridx ();
-      cidx = sm.xcidx ();
-    }
-
-  if (n_row != n_col)
-    err_square_matrix_required ("symamd", "S");
-
-  // Allocate workspace for symamd
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, perm, n_col+1);
-  static_assert (COLAMD_STATS <= 40,
-                 "symamd: # of COLAMD_STATS exceeded.  Please report this to bugs.octave.org");
-  octave::suitesparse_integer stats_storage[COLAMD_STATS];
-  octave::suitesparse_integer *stats = &stats_storage[0];
-  if (! SYMAMD_NAME () (n_col, octave::to_suitesparse_intptr (ridx),
-                        octave::to_suitesparse_intptr (cidx),
-                        octave::to_suitesparse_intptr (perm),
-                        knobs, stats, &calloc, &free))
-    {
-      SYMAMD_NAME (_report)(stats);
-
-      error ("symamd: internal error!");
-    }
-
-  // column elimination tree post-ordering
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1);
-  symetree (ridx, cidx, etree, perm, n_col);
-
-  // Calculate the tree post-ordering
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, post, n_col + 1);
-  tree_postorder (n_col, etree, post);
-
-  // return the permutation vector
-  NDArray out_perm (dim_vector (1, n_col));
-  for (octave_idx_type i = 0; i < n_col; i++)
-    out_perm(i) = perm[post[i]] + 1;
-
-  retval(0) = out_perm;
-
-  // print stats if spumoni > 0
-  if (spumoni > 0)
-    SYMAMD_NAME (_report)(stats);
-
-  // Return the stats vector
-  if (nargout == 2)
-    {
-      NDArray out_stats (dim_vector (1, COLAMD_STATS));
-      for (octave_idx_type i = 0 ; i < COLAMD_STATS ; i++)
-        out_stats(i) = stats[i];
-      retval(1) = out_stats;
-
-      // fix stats (5) and (6), for 1-based information on
-      // jumbled matrix.  note that this correction doesn't
-      // occur if symamd returns FALSE
-      out_stats(COLAMD_INFO1)++;
-      out_stats(COLAMD_INFO2)++;
-    }
-
-  return retval;
-
-#else
-
-  octave_unused_parameter (args);
-  octave_unused_parameter (nargout);
-
-  err_disabled_feature ("symamd", "COLAMD");
-
-#endif
-}
-
-DEFUN_DLD (etree, args, nargout,
-           doc: /* -*- texinfo -*-
-@deftypefn  {} {@var{p} =} etree (@var{S})
-@deftypefnx {} {@var{p} =} etree (@var{S}, @var{typ})
-@deftypefnx {} {[@var{p}, @var{q}] =} etree (@var{S}, @var{typ})
-
-Return the elimination tree for the matrix @var{S}.
-
-By default @var{S} is assumed to be symmetric and the symmetric elimination
-tree is returned.  The argument @var{typ} controls whether a symmetric or
-column elimination tree is returned.  Valid values of @var{typ} are
-@qcode{"sym"} or @qcode{"col"}, for symmetric or column elimination tree
-respectively.
-
-Called with a second argument, @code{etree} also returns the postorder
-permutations on the tree.
-@end deftypefn */)
-{
-  int nargin = args.length ();
-
-  if (nargin < 1 || nargin > 2)
-    print_usage ();
-
-  octave_value_list retval (nargout == 2 ? 2 : 1);
-
-  octave_idx_type n_row = 0;
-  octave_idx_type n_col = 0;
-  octave_idx_type *ridx = nullptr;
-  octave_idx_type *cidx = nullptr;
-
-  if (! args(0).issparse ())
-    error ("etree: S must be a sparse matrix");
-
-  if (args(0).iscomplex ())
-    {
-      SparseComplexMatrix scm = args(0).sparse_complex_matrix_value ();
-
-      n_row = scm.rows ();
-      n_col = scm.cols ();
-      ridx = scm.xridx ();
-      cidx = scm.xcidx ();
-    }
-  else
-    {
-      SparseMatrix sm = args(0).sparse_matrix_value ();
-
-      n_row = sm.rows ();
-      n_col = sm.cols ();
-      ridx = sm.xridx ();
-      cidx = sm.xcidx ();
-    }
-
-  bool is_sym = true;
-
-  if (nargin == 2)
-    {
-      std::string str = args(1).xstring_value ("etree: TYP must be a string");
-      if (str.find ('C') == 0 || str.find ('c') == 0)
-        is_sym = false;
-    }
-
-  // column elimination tree post-ordering (reuse variables)
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1);
-
-  if (is_sym)
-    {
-      if (n_row != n_col)
-        error ("etree: S is marked as symmetric, but is not square");
-
-      symetree (ridx, cidx, etree, nullptr, n_col);
-    }
-  else
-    {
-      OCTAVE_LOCAL_BUFFER (octave_idx_type, colbeg, n_col);
-      OCTAVE_LOCAL_BUFFER (octave_idx_type, colend, n_col);
-
-      for (octave_idx_type i = 0; i < n_col; i++)
-        {
-          colbeg[i] = cidx[i];
-          colend[i] = cidx[i+1];
-        }
-
-      coletree (ridx, colbeg, colend, etree, n_row, n_col);
-    }
-
-  NDArray tree (dim_vector (1, n_col));
-  for (octave_idx_type i = 0; i < n_col; i++)
-    // We flag a root with n_col while Matlab does it with zero
-    // Convert for matlab compatible output
-    if (etree[i] == n_col)
-      tree(i) = 0;
-    else
-      tree(i) = etree[i] + 1;
-
-  retval(0) = tree;
-
-  if (nargout == 2)
-    {
-      // Calculate the tree post-ordering
-      OCTAVE_LOCAL_BUFFER (octave_idx_type, post, n_col + 1);
-      tree_postorder (n_col, etree, post);
-
-      NDArray postorder (dim_vector (1, n_col));
-      for (octave_idx_type i = 0; i < n_col; i++)
-        postorder(i) = post[i] + 1;
-
-      retval(1) = postorder;
-    }
-
-  return retval;
-}
-
-/*
-%!assert (etree (speye (2)), [0, 0]);
-%!assert (etree (gallery ("poisson", 16)), [2:256, 0]);
-
-%!error etree ()
-%!error etree (1, 2, 3)
-%!error <S must be a sparse matrix> etree ([1, 2; 3, 4])
-%!error <TYP must be a string> etree (speye (2), 3)
-%!error <is not square> etree (sprand (2, 4, .25))
-*/
--- a/libinterp/dldfcn/dmperm.cc	Sat Jan 25 15:26:07 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,215 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 1998-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include "CSparse.h"
-#include "dRowVector.h"
-#include "dSparse.h"
-#include "oct-sparse.h"
-
-#include "defun-dld.h"
-#include "errwarn.h"
-#include "ov.h"
-#include "ovl.h"
-#include "utils.h"
-
-#if defined (OCTAVE_ENABLE_64)
-#  define CXSPARSE_NAME(name) cs_dl ## name
-#else
-#  define CXSPARSE_NAME(name) cs_di ## name
-#endif
-
-#if defined (HAVE_CXSPARSE)
-
-static RowVector
-put_int (octave::suitesparse_integer *p, octave_idx_type n)
-{
-  RowVector ret (n);
-  for (octave_idx_type i = 0; i < n; i++)
-    ret.xelem (i) = p[i] + 1;
-  return ret;
-}
-
-static octave_value_list
-dmperm_internal (bool rank, const octave_value arg, int nargout)
-{
-  octave_value_list retval;
-  octave_idx_type nr = arg.rows ();
-  octave_idx_type nc = arg.columns ();
-  SparseMatrix m;
-  SparseComplexMatrix cm;
-  CXSPARSE_NAME () csm;
-  csm.m = nr;
-  csm.n = nc;
-  csm.x = nullptr;
-  csm.nz = -1;
-
-  if (arg.isreal ())
-    {
-      m = arg.sparse_matrix_value ();
-      csm.nzmax = m.nnz ();
-      csm.p = octave::to_suitesparse_intptr (m.xcidx ());
-      csm.i = octave::to_suitesparse_intptr (m.xridx ());
-    }
-  else
-    {
-      cm = arg.sparse_complex_matrix_value ();
-      csm.nzmax = cm.nnz ();
-      csm.p = octave::to_suitesparse_intptr (cm.xcidx ());
-      csm.i = octave::to_suitesparse_intptr (cm.xridx ());
-    }
-
-  if (nargout <= 1 || rank)
-    {
-      octave::suitesparse_integer *jmatch = CXSPARSE_NAME (_maxtrans) (&csm, 0);
-      if (rank)
-        {
-          octave_idx_type r = 0;
-          for (octave_idx_type i = 0; i < nc; i++)
-            if (jmatch[nr+i] >= 0)
-              r++;
-          retval(0) = static_cast<double> (r);
-        }
-      else
-        retval(0) = put_int (jmatch + nr, nc);
-      CXSPARSE_NAME (_free) (jmatch);
-    }
-  else
-    {
-      CXSPARSE_NAME (d) *dm = CXSPARSE_NAME(_dmperm) (&csm, 0);
-
-      //retval(5) = put_int (dm->rr, 5);
-      //retval(4) = put_int (dm->cc, 5);
-      retval = ovl (put_int (dm->p, nr), put_int (dm->q, nc),
-                    put_int (dm->r, dm->nb+1), put_int (dm->s, dm->nb+1));
-
-      CXSPARSE_NAME (_dfree) (dm);
-    }
-
-  return retval;
-}
-
-#endif
-
-DEFUN_DLD (dmperm, args, nargout,
-           doc: /* -*- texinfo -*-
-@deftypefn  {} {@var{p} =} dmperm (@var{S})
-@deftypefnx {} {[@var{p}, @var{q}, @var{r}, @var{S}] =} dmperm (@var{S})
-
-@cindex @nospell{Dulmage-Mendelsohn} decomposition
-Perform a @nospell{Dulmage-Mendelsohn} permutation of the sparse matrix
-@var{S}.
-
-With a single output argument @code{dmperm} performs the row permutations
-@var{p} such that @code{@var{S}(@var{p},:)} has no zero elements on the
-diagonal.
-
-Called with two or more output arguments, returns the row and column
-permutations, such that @code{@var{S}(@var{p}, @var{q})} is in block
-triangular form.  The values of @var{r} and @var{S} define the boundaries
-of the blocks.  If @var{S} is square then @code{@var{r} == @var{S}}.
-
-The method used is described in: @nospell{A. Pothen & C.-J. Fan.}
-@cite{Computing the Block Triangular Form of a Sparse Matrix}.
-@nospell{ACM} Trans.@: Math.@: Software, 16(4):303--324, 1990.
-@seealso{colamd, ccolamd}
-@end deftypefn */)
-{
-#if defined (HAVE_CXSPARSE)
-
-  if (args.length () != 1)
-    print_usage ();
-
-  return dmperm_internal (false, args(0), nargout);
-
-#else
-
-  octave_unused_parameter (args);
-  octave_unused_parameter (nargout);
-
-  err_disabled_feature ("dmperm", "CXSparse");
-
-#endif
-}
-
-/*
-%!testif HAVE_CXSPARSE
-%! n = 20;
-%! a = speye (n,n);
-%! a = a(randperm (n),:);
-%! assert (a(dmperm (a),:), speye (n));
-
-%!testif HAVE_CXSPARSE
-%! n = 20;
-%! d = 0.2;
-%! a = tril (sprandn (n,n,d), -1) + speye (n,n);
-%! a = a(randperm (n), randperm (n));
-%! [p,q,r,s] = dmperm (a);
-%! assert (tril (a(p,q), -1), sparse (n, n));
-*/
-
-DEFUN_DLD (sprank, args, nargout,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {@var{p} =} sprank (@var{S})
-@cindex structural rank
-
-Calculate the structural rank of the sparse matrix @var{S}.
-
-Note that only the structure of the matrix is used in this calculation based
-on a @nospell{Dulmage-Mendelsohn} permutation to block triangular form.  As
-such the numerical rank of the matrix @var{S} is bounded by
-@code{sprank (@var{S}) >= rank (@var{S})}.  Ignoring floating point errors
-@code{sprank (@var{S}) == rank (@var{S})}.
-@seealso{dmperm}
-@end deftypefn */)
-{
-#if defined (HAVE_CXSPARSE)
-
-  if (args.length () != 1)
-    print_usage ();
-
-  return dmperm_internal (true, args(0), nargout);
-
-#else
-
-  octave_unused_parameter (args);
-  octave_unused_parameter (nargout);
-
-  err_disabled_feature ("sprank", "CXSparse");
-
-#endif
-}
-
-/*
-%!testif HAVE_CXSPARSE
-%! assert (sprank (speye (20)), 20);
-%!testif HAVE_CXSPARSE
-%! assert (sprank ([1,0,2,0;2,0,4,0]), 2);
-
-%!error sprank (1,2)
-*/
--- a/libinterp/dldfcn/module-files	Sat Jan 25 15:26:07 2020 +0100
+++ b/libinterp/dldfcn/module-files	Wed Jan 29 06:30:40 2020 -0500
@@ -1,22 +1,13 @@
 # FILE|CPPFLAGS|LDFLAGS|LIBRARIES
 __delaunayn__.cc|$(QHULL_CPPFLAGS)|$(QHULL_LDFLAGS)|$(QHULL_LIBS)
-__eigs__.cc|$(ARPACK_CPPFLAGS) $(SPARSE_XCPPFLAGS)|$(ARPACK_LDFLAGS) $(SPARSE_XLDFLAGS)|$(ARPACK_LIBS) $(SPARSE_XLIBS) $(LAPACK_LIBS) $(BLAS_LIBS)
 __fltk_uigetfile__.cc|$(FLTK_CPPFLAGS) $(FT2_CPPFLAGS)|$(FLTK_LDFLAGS) $(FT2_LDFLAGS)|$(FLTK_LIBS) $(FT2_LIBS)
 __glpk__.cc|$(GLPK_CPPFLAGS)|$(GLPK_LDFLAGS)|$(GLPK_LIBS)
 __init_fltk__.cc|$(FLTK_CPPFLAGS) $(FT2_CPPFLAGS) $(FONTCONFIG_CPPFLAGS)|$(FLTK_LDFLAGS) $(FT2_LDFLAGS)|$(FLTK_LIBS) $(FT2_LIBS) $(OPENGL_LIBS)
 __init_gnuplot__.cc|$(FT2_CPPFLAGS) $(FONTCONFIG_CPPFLAGS)||
 __ode15__.cc|$(SUNDIALS_XCPPFLAGS)|$(SUNDIALS_XLDFLAGS)|$(SUNDIALS_XLIBS)
 __voronoi__.cc|$(QHULL_CPPFLAGS)|$(QHULL_LDFLAGS)|$(QHULL_LIBS)
-amd.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS)
 audiodevinfo.cc|$(PORTAUDIO_CPPFLAGS)|$(PORTAUDIO_LDFLAGS)|$(PORTAUDIO_LIBS)
 audioread.cc|$(SNDFILE_CPPFLAGS)|$(SNDFILE_LDFLAGS)|$(SNDFILE_LIBS)
-ccolamd.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS)
-chol.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS)
-colamd.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS)
 convhulln.cc|$(QHULL_CPPFLAGS)|$(QHULL_LDFLAGS)|$(QHULL_LIBS)
-dmperm.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS)
 fftw.cc|$(FFTW_XCPPFLAGS)|$(FFTW_XLDFLAGS)|$(FFTW_XLIBS)
 gzip.cc|$(Z_CPPFLAGS) $(BZ2_CPPFLAGS)|$(Z_LDFLAGS) $(BZ2_LDFLAGS)|$(Z_LIBS) $(BZ2_LIBS)
-qr.cc|$(QRUPDATE_CPPFLAGS) $(SPARSE_XCPPFLAGS)|$(QRUPDATE_LDFLAGS) $(SPARSE_XLDFLAGS)|$(QRUPDATE_LIBS) $(SPARSE_XLIBS)
-symbfact.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS)
-symrcm.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS)
--- a/libinterp/dldfcn/qr.cc	Sat Jan 25 15:26:07 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1751 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 1996-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include <string>
-
-#include "MArray.h"
-#include "Matrix.h"
-#include "qr.h"
-#include "qrp.h"
-#include "sparse-qr.h"
-
-#include "defun-dld.h"
-#include "error.h"
-#include "errwarn.h"
-#include "ov.h"
-#include "ovl.h"
-
-/*
-## Restore all rand* "state" values
-%!function restore_rand_states (state)
-%!  rand ("state", state.rand);
-%!  randn ("state", state.randn);
-%!endfunction
-
-%!shared old_state, restore_state
-%! ## Save and restore the states of both random number generators that are
-%! ## tested by the unit tests in this file.
-%! old_state.rand = rand ("state");
-%! old_state.randn = randn ("state");
-%! restore_state = onCleanup (@() restore_rand_states (old_state));
-*/
-
-template <typename MT>
-static octave_value
-get_qr_r (const octave::math::qr<MT>& fact)
-{
-  MT R = fact.R ();
-  if (R.issquare () && fact.regular ())
-    return octave_value (R, MatrixType (MatrixType::Upper));
-  else
-    return R;
-}
-
-template <typename T>
-static typename octave::math::qr<T>::type
-qr_type (int nargout, bool economy)
-{
-  if (nargout == 0 || nargout == 1)
-    return octave::math::qr<T>::raw;
-  else if (economy)
-    return octave::math::qr<T>::economy;
-  else
-    return octave::math::qr<T>::std;
-}
-
-// [Q, R] = qr (X):       form Q unitary and R upper triangular such
-//                        that Q * R = X
-//
-// [Q, R] = qr (X, 0):    form the economy decomposition such that if X is
-//                        m by n then only the first n columns of Q are
-//                        computed.
-//
-// [Q, R, P] = qr (X):    form QRP factorization of X where
-//                        P is a permutation matrix such that
-//                        A * P = Q * R
-//
-// [Q, R, P] = qr (X, 0): form the economy decomposition with
-//                        permutation vector P such that Q * R = X(:, P)
-//
-// qr (X) alone returns the output of the LAPACK routine dgeqrf, such
-// that R = triu (qr (X))
-
-DEFUN_DLD (qr, args, nargout,
-           doc: /* -*- texinfo -*-
-@deftypefn  {} {[@var{Q}, @var{R}] =} qr (@var{A})
-@deftypefnx {} {[@var{Q}, @var{R}, @var{P}] =} qr (@var{A})  # non-sparse A
-@deftypefnx {} {@var{X} =} qr (@var{A})  # non-sparse A
-@deftypefnx {} {@var{R} =} qr (@var{A})  # sparse A
-@deftypefnx {} {[@var{C}, @var{R}] =} qr (@var{A}, @var{B})
-@deftypefnx {} {[@dots{}] =} qr (@dots{}, 0)
-@deftypefnx {} {[@dots{}] =} qr (@dots{}, "vector")
-@deftypefnx {} {[@dots{}] =} qr (@dots{}, "matrix")
-@cindex QR factorization
-Compute the QR@tie{}factorization of @var{A}, using standard @sc{lapack}
-subroutines.
-
-The QR@tie{}factorization is
-@tex
-$QR = A$ where $Q$ is an orthogonal matrix and $R$ is upper triangular.
-@end tex
-@ifnottex
-
-@example
-@var{Q} * @var{R} = @var{A}
-@end example
-
-@noindent
-where @var{Q} is an orthogonal matrix and @var{R} is upper triangular.
-@end ifnottex
-
-For example, given the matrix @code{@var{A} = [1, 2; 3, 4]},
-
-@example
-[@var{Q}, @var{R}] = qr (@var{A})
-@end example
-
-@noindent
-returns
-
-@example
-@group
-@var{Q} =
-
-  -0.31623  -0.94868
-  -0.94868   0.31623
-
-@var{R} =
-
-  -3.16228  -4.42719
-   0.00000  -0.63246
-@end group
-@end example
-
-@noindent
-which multiplied together return the original matrix
-
-@example
-@group
-@var{Q} * @var{R}
-  @result{}
-     1.0000   2.0000
-     3.0000   4.0000
-@end group
-@end example
-
-If just a single return value is requested then it is either @var{R}, if
-@var{A} is sparse, or @var{X}, such that @code{@var{R} = triu (@var{X})} if
-@var{A} is full.  (Note: unlike most commands, the single return value is not
-the first return value when multiple values are requested.)
-
-If the matrix @var{A} is full, and a third output @var{P} is requested, then
-@code{qr} calculates the permuted QR@tie{}factorization
-@tex
-$QR = AP$ where $Q$ is an orthogonal matrix, $R$ is upper triangular, and $P$
-is a permutation matrix.
-@end tex
-@ifnottex
-
-@example
-@var{Q} * @var{R} = @var{A} * @var{P}
-@end example
-
-@noindent
-where @var{Q} is an orthogonal matrix, @var{R} is upper triangular, and
-@var{P} is a permutation matrix.
-@end ifnottex
-
-The permuted QR@tie{}factorization has the additional property that the
-diagonal entries of @var{R} are ordered by decreasing magnitude.  In other
-words, @code{abs (diag (@var{R}))} will be ordered from largest to smallest.
-
-For example, given the matrix @code{@var{A} = [1, 2; 3, 4]},
-
-@example
-[@var{Q}, @var{R}, @var{P}] = qr (@var{A})
-@end example
-
-@noindent
-returns
-
-@example
-@group
-@var{Q} =
-
-  -0.44721  -0.89443
-  -0.89443   0.44721
-
-@var{R} =
-
-  -4.47214  -3.13050
-   0.00000   0.44721
-
-@var{P} =
-
-   0  1
-   1  0
-@end group
-@end example
-
-If the input matrix @var{A} is sparse then the sparse QR@tie{}factorization
-is computed using @sc{CSparse}.  Because the matrix @var{Q} is, in general, a
-full matrix, it is recommended to request only one return value @var{R}.  In
-that case, the computation avoids the construction of @var{Q} and returns
-@var{R} such that @code{@var{R} = chol (@var{A}' * @var{A})}.
-
-If an additional matrix @var{B} is supplied and two return values are
-requested, then @code{qr} returns @var{C}, where
-@code{@var{C} = @var{Q}' * @var{B}}.  This allows the least squares
-approximation of @code{@var{A} \ @var{B}} to be calculated as
-
-@example
-@group
-[@var{C}, @var{R}] = qr (@var{A}, @var{B})
-@var{x} = @var{R} \ @var{C}
-@end group
-@end example
-
-If the final argument is the string @qcode{"vector"} then @var{P} is a
-permutation vector (of the columns of @var{A}) instead of a permutation matrix.
-In this case, the defining relationship is
-
-@example
-@var{Q} * @var{R} = @var{A}(:, @var{P})
-@end example
-
-The default, however, is to return a permutation matrix and this may be
-explicitly specified by using a final argument of @qcode{"matrix"}.
-
-If the final argument is the scalar 0 an @qcode{"economy"} factorization is
-returned.  When the original matrix @var{A} has size MxN and M > N then the
-@qcode{"economy"} factorization will calculate just N rows in @var{R} and N
-columns in @var{Q} and omit the zeros in @var{R}.  If M @leq{} N there is no
-difference between the economy and standard factorizations.  When calculating
-an @qcode{"economy"} factorization the output @var{P} is always a vector
-rather than a matrix.
-
-Background: The QR factorization has applications in the solution of least
-squares problems
-@tex
-$$
-\min_x \left\Vert A x - b \right\Vert_2
-$$
-@end tex
-@ifnottex
-
-@example
-min norm (A*x - b)
-@end example
-
-@end ifnottex
-for overdetermined systems of equations (i.e.,
-@tex
-$A$
-@end tex
-@ifnottex
-@var{A}
-@end ifnottex
-is a tall, thin matrix).
-
-The permuted QR@tie{}factorization
-@code{[@var{Q}, @var{R}, @var{P}] = qr (@var{A})} allows the construction of an
-orthogonal basis of @code{span (A)}.
-
-@seealso{chol, hess, lu, qz, schur, svd, qrupdate, qrinsert, qrdelete, qrshift}
-@end deftypefn */)
-{
-  int nargin = args.length ();
-
-  if (nargin < 1 || nargin > 3)
-    print_usage ();
-
-  octave_value_list retval;
-
-  octave_value arg = args(0);
-
-  bool economy = false;
-  bool is_cmplx = false;
-  bool have_b = 0;
-  bool vector_p = 0;
-
-  if (arg.iscomplex ())
-    is_cmplx = true;
-  if (nargin > 1)
-    {
-      have_b = true;
-      if (args(nargin-1).is_scalar_type ())
-        {
-          int val = args(nargin-1).int_value ();
-          if (val == 0)
-            {
-              economy = true;
-              have_b = (nargin > 2);
-            }
-          else if (nargin == 3)   // argument 3 should be 0 or a string
-            print_usage ();
-        }
-      else if (args(nargin-1).is_string ())
-        {
-          std::string str = args(nargin-1).string_value ();
-          if (str == "vector")
-            vector_p = true;
-          else if (str != "matrix")
-            error ("qr: type for P must be 'matrix' or 'vector', not %s",
-                   str.c_str ());
-          have_b = (nargin > 2);
-        }
-      else if (! args(nargin-1).is_matrix_type ())
-        err_wrong_type_arg ("qr", args(nargin-1));
-      else if (nargin == 3)   // should be caught by is_scalar_type or is_string
-        print_usage ();
-
-      if (have_b && args(1).iscomplex ())
-        is_cmplx = true;
-    }
-
-  if (arg.issparse ())
-    {
-      if (nargout > 2)
-        error ("qr: Permutation output is not supported for sparse input");
-
-      if (is_cmplx)
-        {
-          octave::math::sparse_qr<SparseComplexMatrix> q (arg.sparse_complex_matrix_value ());
-
-          if (have_b)
-            {
-              retval = ovl (q.C (args(1).complex_matrix_value ()),
-                            q.R (economy));
-              if (arg.rows () < arg.columns ())
-                warning ("qr: non minimum norm solution for under-determined "
-                         "problem %" OCTAVE_IDX_TYPE_FORMAT
-                         "x%" OCTAVE_IDX_TYPE_FORMAT,
-                         arg.rows (), arg.columns ());
-            }
-          else if (nargout > 1)
-            retval = ovl (q.Q (), q.R (economy));
-          else
-            retval = ovl (q.R (economy));
-        }
-      else
-        {
-          octave::math::sparse_qr<SparseMatrix> q (arg.sparse_matrix_value ());
-
-          if (have_b)
-            {
-              retval = ovl (q.C (args(1).matrix_value ()), q.R (economy));
-              if (arg.rows () < arg.columns ())
-                warning ("qr: non minimum norm solution for under-determined "
-                         "problem %" OCTAVE_IDX_TYPE_FORMAT
-                         "x%" OCTAVE_IDX_TYPE_FORMAT,
-                         arg.rows (), arg.columns ());
-            }
-          else if (nargout > 1)
-            retval = ovl (q.Q (), q.R (economy));
-          else
-            retval = ovl (q.R (economy));
-        }
-    }
-  else
-    {
-      if (arg.is_single_type ())
-        {
-          if (arg.isreal ())
-            {
-              octave::math::qr<FloatMatrix>::type type
-                = qr_type<FloatMatrix> (nargout, economy);
-
-              FloatMatrix m = arg.float_matrix_value ();
-
-              switch (nargout)
-                {
-                case 0:
-                case 1:
-                  {
-                    octave::math::qr<FloatMatrix> fact (m, type);
-                    retval = ovl (fact.R ());
-                  }
-                  break;
-
-                case 2:
-                  {
-                    octave::math::qr<FloatMatrix> fact (m, type);
-                    retval = ovl (fact.Q (), get_qr_r (fact));
-                    if (have_b)
-                      {
-                        if (is_cmplx)
-                          retval(0) = fact.Q ().transpose ()
-                                      * args(1).float_complex_matrix_value ();
-                        else
-                          retval(0) = fact.Q ().transpose ()
-                                      * args(1).float_matrix_value ();
-                      }
-                  }
-                  break;
-
-                default:
-                  {
-                    octave::math::qrp<FloatMatrix> fact (m, type);
-
-                    if (economy || vector_p)
-                      retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
-                    else
-                      retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
-                  }
-                  break;
-                }
-            }
-          else if (arg.iscomplex ())
-            {
-              octave::math::qr<FloatComplexMatrix>::type type
-                = qr_type<FloatComplexMatrix> (nargout, economy);
-
-              FloatComplexMatrix m = arg.float_complex_matrix_value ();
-
-              switch (nargout)
-                {
-                case 0:
-                case 1:
-                  {
-                    octave::math::qr<FloatComplexMatrix> fact (m, type);
-                    retval = ovl (fact.R ());
-                  }
-                  break;
-
-                case 2:
-                  {
-                    octave::math::qr<FloatComplexMatrix> fact (m, type);
-                    retval = ovl (fact.Q (), get_qr_r (fact));
-                    if (have_b)
-                      retval (0) = conj (fact.Q ().transpose ())
-                                   * args(1).float_complex_matrix_value ();
-                  }
-                  break;
-
-                default:
-                  {
-                    octave::math::qrp<FloatComplexMatrix> fact (m, type);
-                    if (economy || vector_p)
-                      retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
-                    else
-                      retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
-                  }
-                  break;
-                }
-            }
-        }
-      else
-        {
-          if (arg.isreal ())
-            {
-              octave::math::qr<Matrix>::type type
-                = qr_type<Matrix> (nargout, economy);
-
-              Matrix m = arg.matrix_value ();
-
-              switch (nargout)
-                {
-                case 0:
-                case 1:
-                  {
-                    octave::math::qr<Matrix> fact (m, type);
-                    retval = ovl (fact.R ());
-                  }
-                  break;
-
-                case 2:
-                  {
-                    octave::math::qr<Matrix> fact (m, type);
-                    retval = ovl (fact.Q (), get_qr_r (fact));
-                    if (have_b)
-                      {
-                        if (is_cmplx)
-                          retval(0) = fact.Q ().transpose ()
-                                      * args(1).complex_matrix_value ();
-                        else
-                          retval(0) = fact.Q ().transpose ()
-                                      * args(1).matrix_value ();
-                      }
-                  }
-                  break;
-
-                default:
-                  {
-                    octave::math::qrp<Matrix> fact (m, type);
-                    if (economy || vector_p)
-                      retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
-                    else
-                      retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
-                  }
-                  break;
-                }
-            }
-          else if (arg.iscomplex ())
-            {
-              octave::math::qr<ComplexMatrix>::type type
-                = qr_type<ComplexMatrix> (nargout, economy);
-
-              ComplexMatrix m = arg.complex_matrix_value ();
-
-              switch (nargout)
-                {
-                case 0:
-                case 1:
-                  {
-                    octave::math::qr<ComplexMatrix> fact (m, type);
-                    retval = ovl (fact.R ());
-                  }
-                  break;
-
-                case 2:
-                  {
-                    octave::math::qr<ComplexMatrix> fact (m, type);
-                    retval = ovl (fact.Q (), get_qr_r (fact));
-                    if (have_b)
-                      retval (0) = conj (fact.Q ().transpose ())
-                                   * args(1).complex_matrix_value ();
-                  }
-                  break;
-
-                default:
-                  {
-                    octave::math::qrp<ComplexMatrix> fact (m, type);
-                    if (economy || vector_p)
-                      retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ());
-                    else
-                      retval = ovl (fact.Q (), get_qr_r (fact), fact.P ());
-                  }
-                  break;
-                }
-            }
-          else
-            err_wrong_type_arg ("qr", arg);
-        }
-    }
-
-  return retval;
-}
-
-/*
-%!test
-%! a = [0, 2, 1; 2, 1, 2];
-%!
-%! [q, r] = qr (a);
-%! [qe, re] = qr (a, 0);
-%!
-%! assert (q * r, a, sqrt (eps));
-%! assert (qe * re, a, sqrt (eps));
-
-%!test
-%! a = [0, 2, 1; 2, 1, 2];
-%!
-%! [q, r] = qr (a);
-%! [qe, re] = qr (a, 0);
-%!
-%! assert (q * r, a, sqrt (eps));
-%! assert (qe * re, a, sqrt (eps));
-
-%!test
-%! a = [0, 2, 1; 2, 1, 2];
-%!
-%! [q, r, p] = qr (a);  # FIXME: not giving right dimensions.
-%! [qe, re, pe] = qr (a, 0);
-%!
-%! assert (q * r, a * p, sqrt (eps));
-%! assert (qe * re, a(:, pe), sqrt (eps));
-
-%!test
-%! a = [0, 2; 2, 1; 1, 2];
-%!
-%! [q, r] = qr (a);
-%! [qe, re] = qr (a, 0);
-%!
-%! assert (q * r, a, sqrt (eps));
-%! assert (qe * re, a, sqrt (eps));
-
-%!test
-%! a = [0, 2; 2, 1; 1, 2];
-%!
-%! [q, r, p] = qr (a);
-%! [qe, re, pe] = qr (a, 0);
-%!
-%! assert (q * r, a * p, sqrt (eps));
-%! assert (qe * re, a(:, pe), sqrt (eps));
-
-%!test
-%! a = [0, 2, 1; 2, 1, 2; 3, 1, 2];
-%! b = [1, 3, 2; 1, 1, 0; 3, 0, 2];
-%!
-%! [q, r] = qr (a);
-%! [c, re] = qr (a, b);
-%!
-%! assert (r, re, sqrt (eps));
-%! assert (q'*b, c, sqrt (eps));
-
-%!test
-%! a = [0, 2, i; 2, 1, 2; 3, 1, 2];
-%! b = [1, 3, 2; 1, i, 0; 3, 0, 2];
-%!
-%! [q, r] = qr (a);
-%! [c, re] = qr (a, b);
-%!
-%! assert (r, re, sqrt (eps));
-%! assert (q'*b, c, sqrt (eps));
-
-%!test
-%! a = [0, 2, i; 2, 1, 2; 3, 1, 2];
-%! b = [1, 3, 2; 1, 1, 0; 3, 0, 2];
-%!
-%! [q, r] = qr (a);
-%! [c, re] = qr (a, b);
-%!
-%! assert (r, re, sqrt (eps));
-%! assert (q'*b, c, sqrt (eps));
-
-%!test
-%! a = [0, 2, 1; 2, 1, 2; 3, 1, 2];
-%! b = [1, 3, 2; 1, i, 0; 3, 0, 2];
-%!
-%! [q, r] = qr (a);
-%! [c, re] = qr (a, b);
-%!
-%! assert (r, re, sqrt (eps));
-%! assert (q'*b, c, sqrt (eps));
-
-%!test
-%! assert (qr (zeros (0, 0)), zeros (0, 0))
-%! assert (qr (zeros (1, 0)), zeros (1, 0))
-%! assert (qr (zeros (0, 1)), zeros (0, 1))
-
-%!error qr ()
-%!error qr ([1, 2; 3, 4], 0, 2)
-
-%!function retval = __testqr (q, r, a, p)
-%!  tol = 100*eps (class (q));
-%!  retval = 0;
-%!  if (nargin == 3)
-%!    n1 = norm (q*r - a);
-%!    n2 = norm (q'*q - eye (columns (q)));
-%!    retval = (n1 < tol && n2 < tol);
-%!  else
-%!    n1 = norm (q'*q - eye (columns (q)));
-%!    retval = (n1 < tol);
-%!    if (isvector (p))
-%!      n2 = norm (q*r - a(:,p));
-%!      retval = (retval && n2 < tol);
-%!    else
-%!      n2 = norm (q*r - a*p);
-%!      retval = (retval && n2 < tol);
-%!    endif
-%!  endif
-%!endfunction
-
-%!test
-%! t = ones (24, 1);
-%! j = 1;
-%!
-%! if (false)  # eliminate big matrix tests
-%!   a = rand (5000, 20);
-%!   [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
-%!   [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
-%!   [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
-%!   [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
-%!
-%!   a = a+1i*eps;
-%!   [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
-%!   [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
-%!   [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
-%!   [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
-%! endif
-%!
-%! a = [ ones(1,15); sqrt(eps)*eye(15) ];
-%! [q,r]   = qr (a);   t(j++) = __testqr (q, r, a);
-%! [q,r]   = qr (a');  t(j++) = __testqr (q, r, a');
-%! [q,r,p] = qr (a);   t(j++) = __testqr (q, r, a, p);
-%! [q,r,p] = qr (a');  t(j++) = __testqr (q, r, a', p);
-%!
-%! a = a+1i*eps;
-%! [q,r]   = qr (a);   t(j++) = __testqr (q, r, a);
-%! [q,r]   = qr (a');  t(j++) = __testqr (q, r, a');
-%! [q,r,p] = qr (a);   t(j++) = __testqr (q, r, a, p);
-%! [q,r,p] = qr (a');  t(j++) = __testqr (q, r, a', p);
-%!
-%! a = [ ones(1,15); sqrt(eps)*eye(15) ];
-%! [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
-%! [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
-%! [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
-%! [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
-%!
-%! a = a+1i*eps;
-%! [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
-%! [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
-%! [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
-%! [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
-%!
-%! a = [ 611   196  -192   407    -8   -52   -49    29
-%!       196   899   113  -192   -71   -43    -8   -44
-%!      -192   113   899   196    61    49     8    52
-%!       407  -192   196   611     8    44    59   -23
-%!        -8   -71    61     8   411  -599   208   208
-%!       -52   -43    49    44  -599   411   208   208
-%!       -49    -8     8    59   208   208    99  -911
-%!        29   -44    52   -23   208   208  -911    99 ];
-%! [q,r] = qr (a);
-%!
-%! assert (all (t) && norm (q*r - a) < 5000*eps);
-
-%!test
-%! a = single ([0, 2, 1; 2, 1, 2]);
-%!
-%! [q, r] = qr (a);
-%! [qe, re] = qr (a, 0);
-%!
-%! assert (q * r, a, sqrt (eps ("single")));
-%! assert (qe * re, a, sqrt (eps ("single")));
-
-%!test
-%! a = single ([0, 2, 1; 2, 1, 2]);
-%!
-%! [q, r, p] = qr (a);  # FIXME: not giving right dimensions.
-%! [qe, re, pe] = qr (a, 0);
-%!
-%! assert (q * r, a * p, sqrt (eps ("single")));
-%! assert (qe * re, a(:, pe), sqrt (eps ("single")));
-
-%!test
-%! a = single ([0, 2; 2, 1; 1, 2]);
-%!
-%! [q, r] = qr (a);
-%! [qe, re] = qr (a, 0);
-%!
-%! assert (q * r, a, sqrt (eps ("single")));
-%! assert (qe * re, a, sqrt (eps ("single")));
-
-%!test
-%! a = single ([0, 2; 2, 1; 1, 2]);
-%!
-%! [q, r, p] = qr (a);
-%! [qe, re, pe] = qr (a, 0);
-%!
-%! assert (q * r, a * p, sqrt (eps ("single")));
-%! assert (qe * re, a(:, pe), sqrt (eps ("single")));
-
-%!test
-%! a = single([0, 2, 1; 2, 1, 2; 3, 1, 2]);
-%! b = single([1, 3, 2; 1, 1, 0; 3, 0, 2]);
-%!
-%! [q, r] = qr (a);
-%! [c, re] = qr (a, b);
-%!
-%! assert (r, re, sqrt (eps ("single")));
-%! assert (q'*b, c, sqrt (eps ("single")));
-
-%!test
-%! a = single([0, 2, i; 2, 1, 2; 3, 1, 2]);
-%! b = single([1, 3, 2; 1, i, 0; 3, 0, 2]);
-%!
-%! [q, r] = qr (a);
-%! [c, re] = qr (a, b);
-%!
-%! assert (r, re, sqrt (eps ("single")));
-%! assert (q'*b, c, sqrt (eps ("single")));
-
-%!test
-%! a = single([0, 2, i; 2, 1, 2; 3, 1, 2]);
-%! b = single([1, 3, 2; 1, 1, 0; 3, 0, 2]);
-%!
-%! [q, r] = qr (a);
-%! [c, re] = qr (a, b);
-%!
-%! assert (r, re, sqrt (eps));
-%! assert (q'*b, c, sqrt (eps));
-
-%!test
-%! a = single([0, 2, 1; 2, 1, 2; 3, 1, 2]);
-%! b = single([1, 3, 2; 1, i, 0; 3, 0, 2]);
-%!
-%! [q, r] = qr (a);
-%! [c, re] = qr (a, b);
-%!
-%! assert (r, re, sqrt (eps ("single")));
-%! assert (q'*b, c, sqrt (eps ("single")));
-
-%!error qr ()
-%!error qr ([1, 2; 3, 4], 0, 2)
-
-%!test
-%! t = ones (24, 1);
-%! j = 1;
-%!
-%! if (false)  # eliminate big matrix tests
-%!   a = rand (5000,20);
-%!   [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
-%!   [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
-%!   [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
-%!   [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
-%!
-%!   a = a+1i*eps ("single");
-%!   [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
-%!   [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
-%!   [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
-%!   [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
-%! endif
-%!
-%! a = [ ones(1,15); sqrt(eps("single"))*eye(15) ];
-%! [q,r]   = qr (a);   t(j++) = __testqr (q, r, a);
-%! [q,r]   = qr (a');  t(j++) = __testqr (q, r, a');
-%! [q,r,p] = qr (a);   t(j++) = __testqr (q, r, a, p);
-%! [q,r,p] = qr (a');  t(j++) = __testqr (q, r, a', p);
-%!
-%! a = a+1i*eps ("single");
-%! [q,r]   = qr (a);   t(j++) = __testqr (q, r, a);
-%! [q,r]   = qr (a');  t(j++) = __testqr (q, r, a');
-%! [q,r,p] = qr (a);   t(j++) = __testqr (q, r, a, p);
-%! [q,r,p] = qr (a');  t(j++) = __testqr (q, r, a', p);
-%!
-%! a = [ ones(1,15); sqrt(eps("single"))*eye(15) ];
-%! [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
-%! [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
-%! [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
-%! [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a', p);
-%!
-%! a = a+1i*eps ("single");
-%! [q,r]   = qr (a, 0);  t(j++) = __testqr (q, r, a);
-%! [q,r]   = qr (a',0);  t(j++) = __testqr (q, r, a');
-%! [q,r,p] = qr (a, 0);  t(j++) = __testqr (q, r, a, p);
-%! [q,r,p] = qr (a',0);  t(j++) = __testqr (q, r, a',p);
-%!
-%! a = [ 611   196  -192   407    -8   -52   -49    29
-%!       196   899   113  -192   -71   -43    -8   -44
-%!      -192   113   899   196    61    49     8    52
-%!       407  -192   196   611     8    44    59   -23
-%!        -8   -71    61     8   411  -599   208   208
-%!       -52   -43    49    44  -599   411   208   208
-%!       -49    -8     8    59   208   208    99  -911
-%!        29   -44    52   -23   208   208  -911    99 ];
-%! [q,r] = qr (a);
-%!
-%! assert (all (t) && norm (q*r-a) < 5000*eps ("single"));
-
-## The deactivated tests below can't be tested till rectangular back-subs is
-## implemented for sparse matrices.
-
-%!testif HAVE_CXSPARSE
-%! n = 20;  d = 0.2;
-%! ## initialize generators to make behavior reproducible
-%! rand ("state", 42);
-%! randn ("state", 42);
-%! a = sprandn (n,n,d) + speye (n,n);
-%! r = qr (a);
-%! assert (r'*r, a'*a, 1e-10);
-
-%!testif HAVE_COLAMD
-%! n = 20;  d = 0.2;
-%! ## initialize generators to make behavior reproducible
-%! rand ("state", 42);
-%! randn ("state", 42);
-%! a = sprandn (n,n,d) + speye (n,n);
-%! q = symamd (a);
-%! a = a(q,q);
-%! r = qr (a);
-%! assert (r'*r, a'*a, 1e-10);
-
-%!testif HAVE_CXSPARSE
-%! n = 20;  d = 0.2;
-%! ## initialize generators to make behavior reproducible
-%! rand ("state", 42);
-%! randn ("state", 42);
-%! a = sprandn (n,n,d) + speye (n,n);
-%! [c,r] = qr (a, ones (n,1));
-%! assert (r\c, full (a)\ones (n,1), 10e-10);
-
-%!testif HAVE_CXSPARSE
-%! n = 20;  d = 0.2;
-%! ## initialize generators to make behavior reproducible
-%! rand ("state", 42);
-%! randn ("state", 42);
-%! a = sprandn (n,n,d) + speye (n,n);
-%! b = randn (n,2);
-%! [c,r] = qr (a, b);
-%! assert (r\c, full (a)\b, 10e-10);
-
-## Test under-determined systems!!
-%!#testif HAVE_CXSPARSE
-%! n = 20;  d = 0.2;
-%! ## initialize generators to make behavior reproducible
-%! rand ("state", 42);
-%! randn ("state", 42);
-%! a = sprandn (n,n+1,d) + speye (n,n+1);
-%! b = randn (n,2);
-%! [c,r] = qr (a, b);
-%! assert (r\c, full (a)\b, 10e-10);
-
-%!testif HAVE_CXSPARSE
-%! n = 20;  d = 0.2;
-%! ## initialize generators to make behavior reproducible
-%! rand ("state", 42);
-%! randn ("state", 42);
-%! a = 1i*sprandn (n,n,d) + speye (n,n);
-%! r = qr (a);
-%! assert (r'*r,a'*a,1e-10);
-
-%!testif HAVE_COLAMD
-%! n = 20;  d = 0.2;
-%! ## initialize generators to make behavior reproducible
-%! rand ("state", 42);
-%! randn ("state", 42);
-%! a = 1i*sprandn (n,n,d) + speye (n,n);
-%! q = symamd (a);
-%! a = a(q,q);
-%! r = qr (a);
-%! assert (r'*r, a'*a, 1e-10);
-
-%!testif HAVE_CXSPARSE
-%! n = 20;  d = 0.2;
-%! ## initialize generators to make behavior reproducible
-%! rand ("state", 42);
-%! randn ("state", 42);
-%! a = 1i*sprandn (n,n,d) + speye (n,n);
-%! [c,r] = qr (a, ones (n,1));
-%! assert (r\c, full (a)\ones (n,1), 10e-10);
-
-%!testif HAVE_CXSPARSE
-%! n = 20;  d = 0.2;
-%! ## initialize generators to make behavior reproducible
-%! rand ("state", 42);
-%! randn ("state", 42);
-%! a = 1i*sprandn (n,n,d) + speye (n,n);
-%! b = randn (n,2);
-%! [c,r] = qr (a, b);
-%! assert (r\c, full (a)\b, 10e-10);
-
-## Test under-determined systems!!
-%!#testif HAVE_CXSPARSE
-%! n = 20;  d = 0.2;
-%! ## initialize generators to make behavior reproducible
-%! rand ("state", 42);
-%! randn ("state", 42);
-%! a = 1i*sprandn (n,n+1,d) + speye (n,n+1);
-%! b = randn (n,2);
-%! [c,r] = qr (a, b);
-%! assert (r\c, full (a)\b, 10e-10);
-
-*/
-
-static
-bool check_qr_dims (const octave_value& q, const octave_value& r,
-                    bool allow_ecf = false)
-{
-  octave_idx_type m = q.rows ();
-  octave_idx_type k = r.rows ();
-  octave_idx_type n = r.columns ();
-  return ((q.ndims () == 2 && r.ndims () == 2 && k == q.columns ())
-          && (m == k || (allow_ecf && k == n && k < m)));
-}
-
-static
-bool check_index (const octave_value& i, bool vector_allowed = false)
-{
-  return ((i.isreal () || i.isinteger ())
-          && (i.is_scalar_type () || vector_allowed));
-}
-
-DEFUN_DLD (qrupdate, args, ,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {[@var{Q1}, @var{R1}] =} qrupdate (@var{Q}, @var{R}, @var{u}, @var{v})
-Update a QR factorization given update vectors or matrices.
-
-Given a QR@tie{}factorization of a real or complex matrix
-@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and
-@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of
-@w{@var{A} + @var{u}*@var{v}'}, where @var{u} and @var{v} are column vectors
-(rank-1 update) or matrices with equal number of columns
-(rank-k update).  Notice that the latter case is done as a sequence of
-rank-1 updates; thus, for k large enough, it will be both faster and more
-accurate to recompute the factorization from scratch.
-
-The QR@tie{}factorization supplied may be either full (Q is square) or
-economized (R is square).
-
-@seealso{qr, qrinsert, qrdelete, qrshift}
-@end deftypefn */)
-{
-  octave_value_list retval;
-
-  if (args.length () != 4)
-    print_usage ();
-
-  octave_value argq = args(0);
-  octave_value argr = args(1);
-  octave_value argu = args(2);
-  octave_value argv = args(3);
-
-  if (! argq.isnumeric () || ! argr.isnumeric ()
-      || ! argu.isnumeric () || ! argv.isnumeric ())
-    print_usage ();
-
-  if (! check_qr_dims (argq, argr, true))
-    error ("qrupdate: Q and R dimensions don't match");
-
-  if (argq.isreal () && argr.isreal () && argu.isreal ()
-      && argv.isreal ())
-    {
-      // all real case
-      if (argq.is_single_type () || argr.is_single_type ()
-          || argu.is_single_type () || argv.is_single_type ())
-        {
-          FloatMatrix Q = argq.float_matrix_value ();
-          FloatMatrix R = argr.float_matrix_value ();
-          FloatMatrix u = argu.float_matrix_value ();
-          FloatMatrix v = argv.float_matrix_value ();
-
-          octave::math::qr<FloatMatrix> fact (Q, R);
-          fact.update (u, v);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-      else
-        {
-          Matrix Q = argq.matrix_value ();
-          Matrix R = argr.matrix_value ();
-          Matrix u = argu.matrix_value ();
-          Matrix v = argv.matrix_value ();
-
-          octave::math::qr<Matrix> fact (Q, R);
-          fact.update (u, v);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-    }
-  else
-    {
-      // complex case
-      if (argq.is_single_type () || argr.is_single_type ()
-          || argu.is_single_type () || argv.is_single_type ())
-        {
-          FloatComplexMatrix Q = argq.float_complex_matrix_value ();
-          FloatComplexMatrix R = argr.float_complex_matrix_value ();
-          FloatComplexMatrix u = argu.float_complex_matrix_value ();
-          FloatComplexMatrix v = argv.float_complex_matrix_value ();
-
-          octave::math::qr<FloatComplexMatrix> fact (Q, R);
-          fact.update (u, v);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-      else
-        {
-          ComplexMatrix Q = argq.complex_matrix_value ();
-          ComplexMatrix R = argr.complex_matrix_value ();
-          ComplexMatrix u = argu.complex_matrix_value ();
-          ComplexMatrix v = argv.complex_matrix_value ();
-
-          octave::math::qr<ComplexMatrix> fact (Q, R);
-          fact.update (u, v);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-    }
-
-  return retval;
-}
-
-/*
-%!shared A, u, v, Ac, uc, vc
-%! A = [0.091364  0.613038  0.999083;
-%!      0.594638  0.425302  0.603537;
-%!      0.383594  0.291238  0.085574;
-%!      0.265712  0.268003  0.238409;
-%!      0.669966  0.743851  0.445057 ];
-%!
-%! u = [0.85082;
-%!      0.76426;
-%!      0.42883;
-%!      0.53010;
-%!      0.80683 ];
-%!
-%! v = [0.98810;
-%!      0.24295;
-%!      0.43167 ];
-%!
-%! Ac = [0.620405 + 0.956953i  0.480013 + 0.048806i  0.402627 + 0.338171i;
-%!      0.589077 + 0.658457i  0.013205 + 0.279323i  0.229284 + 0.721929i;
-%!      0.092758 + 0.345687i  0.928679 + 0.241052i  0.764536 + 0.832406i;
-%!      0.912098 + 0.721024i  0.049018 + 0.269452i  0.730029 + 0.796517i;
-%!      0.112849 + 0.603871i  0.486352 + 0.142337i  0.355646 + 0.151496i ];
-%!
-%! uc = [0.20351 + 0.05401i;
-%!      0.13141 + 0.43708i;
-%!      0.29808 + 0.08789i;
-%!      0.69821 + 0.38844i;
-%!      0.74871 + 0.25821i ];
-%!
-%! vc = [0.85839 + 0.29468i;
-%!      0.20820 + 0.93090i;
-%!      0.86184 + 0.34689i ];
-%!
-
-%!test
-%! [Q,R] = qr (A);
-%! [Q,R] = qrupdate (Q, R, u, v);
-%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R)-R), Inf) == 0);
-%! assert (norm (vec (Q*R - A - u*v'), Inf) < norm (A)*1e1*eps);
-%!
-%!test
-%! [Q,R] = qr (Ac);
-%! [Q,R] = qrupdate (Q, R, uc, vc);
-%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R)-R), Inf) == 0);
-%! assert (norm (vec (Q*R - Ac - uc*vc'), Inf) < norm (Ac)*1e1*eps);
-
-%!test
-%! [Q,R] = qr (single (A));
-%! [Q,R] = qrupdate (Q, R, single (u), single (v));
-%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R)-R), Inf) == 0);
-%! assert (norm (vec (Q*R - single (A) - single (u)*single (v)'), Inf) < norm (single (A))*1e1*eps ("single"));
-%!
-%!test
-%! [Q,R] = qr (single (Ac));
-%! [Q,R] = qrupdate (Q, R, single (uc), single (vc));
-%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R)-R), Inf) == 0);
-%! assert (norm (vec (Q*R - single (Ac) - single (uc)*single (vc)'), Inf) < norm (single (Ac))*1e1*eps ("single"));
-*/
-
-DEFUN_DLD (qrinsert, args, ,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {[@var{Q1}, @var{R1}] =} qrinsert (@var{Q}, @var{R}, @var{j}, @var{x}, @var{orient})
-Update a QR factorization given a row or column to insert in the original
-factored matrix.
-
-
-Given a QR@tie{}factorization of a real or complex matrix
-@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and
-@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of
-@w{[A(:,1:j-1) x A(:,j:n)]}, where @var{u} is a column vector to be inserted
-into @var{A} (if @var{orient} is @qcode{"col"}), or the
-QR@tie{}factorization of @w{[A(1:j-1,:);x;A(:,j:n)]}, where @var{x} is a row
-vector to be inserted into @var{A} (if @var{orient} is @qcode{"row"}).
-
-The default value of @var{orient} is @qcode{"col"}.  If @var{orient} is
-@qcode{"col"}, @var{u} may be a matrix and @var{j} an index vector
-resulting in the QR@tie{}factorization of a matrix @var{B} such that
-@w{B(:,@var{j})} gives @var{u} and @w{B(:,@var{j}) = []} gives @var{A}.
-Notice that the latter case is done as a sequence of k insertions;
-thus, for k large enough, it will be both faster and more accurate to
-recompute the factorization from scratch.
-
-If @var{orient} is @qcode{"col"}, the QR@tie{}factorization supplied may
-be either full (Q is square) or economized (R is square).
-
-If @var{orient} is @qcode{"row"}, full factorization is needed.
-@seealso{qr, qrupdate, qrdelete, qrshift}
-@end deftypefn */)
-{
-  int nargin = args.length ();
-
-  if (nargin < 4 || nargin > 5)
-    print_usage ();
-
-  octave_value argq = args(0);
-  octave_value argr = args(1);
-  octave_value argj = args(2);
-  octave_value argx = args(3);
-
-  if (! argq.isnumeric () || ! argr.isnumeric ()
-      || ! argx.isnumeric ()
-      || (nargin > 4 && ! args(4).is_string ()))
-    print_usage ();
-
-  std::string orient = (nargin < 5) ? "col" : args(4).string_value ();
-  bool col = (orient == "col");
-
-  if (! col && orient != "row")
-    error (R"(qrinsert: ORIENT must be "col" or "row")");
-
-  if (! check_qr_dims (argq, argr, col) || (! col && argx.rows () != 1))
-    error ("qrinsert: dimension mismatch");
-
-  if (! check_index (argj, col))
-    error ("qrinsert: invalid index J");
-
-  octave_value_list retval;
-
-  MArray<octave_idx_type> j = argj.octave_idx_type_vector_value ();
-
-  octave_idx_type one = 1;
-
-  if (argq.isreal () && argr.isreal () && argx.isreal ())
-    {
-      // real case
-      if (argq.is_single_type () || argr.is_single_type ()
-          || argx.is_single_type ())
-        {
-          FloatMatrix Q = argq.float_matrix_value ();
-          FloatMatrix R = argr.float_matrix_value ();
-          FloatMatrix x = argx.float_matrix_value ();
-
-          octave::math::qr<FloatMatrix> fact (Q, R);
-
-          if (col)
-            fact.insert_col (x, j-one);
-          else
-            fact.insert_row (x.row (0), j(0)-one);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-      else
-        {
-          Matrix Q = argq.matrix_value ();
-          Matrix R = argr.matrix_value ();
-          Matrix x = argx.matrix_value ();
-
-          octave::math::qr<Matrix> fact (Q, R);
-
-          if (col)
-            fact.insert_col (x, j-one);
-          else
-            fact.insert_row (x.row (0), j(0)-one);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-    }
-  else
-    {
-      // complex case
-      if (argq.is_single_type () || argr.is_single_type ()
-          || argx.is_single_type ())
-        {
-          FloatComplexMatrix Q = argq.float_complex_matrix_value ();
-          FloatComplexMatrix R = argr.float_complex_matrix_value ();
-          FloatComplexMatrix x = argx.float_complex_matrix_value ();
-
-          octave::math::qr<FloatComplexMatrix> fact (Q, R);
-
-          if (col)
-            fact.insert_col (x, j-one);
-          else
-            fact.insert_row (x.row (0), j(0)-one);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-      else
-        {
-          ComplexMatrix Q = argq.complex_matrix_value ();
-          ComplexMatrix R = argr.complex_matrix_value ();
-          ComplexMatrix x = argx.complex_matrix_value ();
-
-          octave::math::qr<ComplexMatrix> fact (Q, R);
-
-          if (col)
-            fact.insert_col (x, j-one);
-          else
-            fact.insert_row (x.row (0), j(0)-one);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-    }
-
-  return retval;
-}
-
-/*
-%!test
-%! [Q,R] = qr (A);
-%! [Q,R] = qrinsert (Q, R, 3, u);
-%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [A(:,1:2) u A(:,3)]), Inf) < norm (A)*1e1*eps);
-%!test
-%! [Q,R] = qr (Ac);
-%! [Q,R] = qrinsert (Q, R, 3, uc);
-%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [Ac(:,1:2) uc Ac(:,3)]), Inf) < norm (Ac)*1e1*eps);
-%!test
-%! x = [0.85082  0.76426  0.42883 ];
-%!
-%! [Q,R] = qr (A);
-%! [Q,R] = qrinsert (Q, R, 3, x, "row");
-%! assert (norm (vec (Q'*Q - eye (6)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [A(1:2,:);x;A(3:5,:)]), Inf) < norm (A)*1e1*eps);
-%!test
-%! x = [0.20351 + 0.05401i  0.13141 + 0.43708i  0.29808 + 0.08789i ];
-%!
-%! [Q,R] = qr (Ac);
-%! [Q,R] = qrinsert (Q, R, 3, x, "row");
-%! assert (norm (vec (Q'*Q - eye (6)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [Ac(1:2,:);x;Ac(3:5,:)]), Inf) < norm (Ac)*1e1*eps);
-
-%!test
-%! [Q,R] = qr (single (A));
-%! [Q,R] = qrinsert (Q, R, 3, single (u));
-%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - single ([A(:,1:2) u A(:,3)])), Inf) < norm (single (A))*1e1*eps ("single"));
-%!test
-%! [Q,R] = qr (single (Ac));
-%! [Q,R] = qrinsert (Q, R, 3, single (uc));
-%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - single ([Ac(:,1:2) uc Ac(:,3)])), Inf) < norm (single (Ac))*1e1*eps ("single"));
-%!test
-%! x = single ([0.85082  0.76426  0.42883 ]);
-%!
-%! [Q,R] = qr (single (A));
-%! [Q,R] = qrinsert (Q, R, 3, x, "row");
-%! assert (norm (vec (Q'*Q - eye (6,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - single ([A(1:2,:);x;A(3:5,:)])), Inf) < norm (single (A))*1e1*eps ("single"));
-%!test
-%! x = single ([0.20351 + 0.05401i  0.13141 + 0.43708i  0.29808 + 0.08789i ]);
-%!
-%! [Q,R] = qr (single (Ac));
-%! [Q,R] = qrinsert (Q, R, 3, x, "row");
-%! assert (norm (vec (Q'*Q - eye (6,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - single ([Ac(1:2,:);x;Ac(3:5,:)])), Inf) < norm (single (Ac))*1e1*eps ("single"));
-*/
-
-DEFUN_DLD (qrdelete, args, ,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {[@var{Q1}, @var{R1}] =} qrdelete (@var{Q}, @var{R}, @var{j}, @var{orient})
-Update a QR factorization given a row or column to delete from the original
-factored matrix.
-
-Given a QR@tie{}factorization of a real or complex matrix
-@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and
-@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of
-@w{[A(:,1:j-1), U, A(:,j:n)]},
-where @var{u} is a column vector to be inserted into @var{A}
-(if @var{orient} is @qcode{"col"}),
-or the QR@tie{}factorization of @w{[A(1:j-1,:);X;A(:,j:n)]},
-where @var{x} is a row @var{orient} is @qcode{"row"}).
-The default value of @var{orient} is @qcode{"col"}.
-
-If @var{orient} is @qcode{"col"}, @var{j} may be an index vector
-resulting in the QR@tie{}factorization of a matrix @var{B} such that
-@w{A(:,@var{j}) = []} gives @var{B}.  Notice that the latter case is done as
-a sequence of k deletions; thus, for k large enough, it will be both faster
-and more accurate to recompute the factorization from scratch.
-
-If @var{orient} is @qcode{"col"}, the QR@tie{}factorization supplied may
-be either full (Q is square) or economized (R is square).
-
-If @var{orient} is @qcode{"row"}, full factorization is needed.
-@seealso{qr, qrupdate, qrinsert, qrshift}
-@end deftypefn */)
-{
-  int nargin = args.length ();
-
-  if (nargin < 3 || nargin > 4)
-    print_usage ();
-
-  octave_value argq = args(0);
-  octave_value argr = args(1);
-  octave_value argj = args(2);
-
-  if (! argq.isnumeric () || ! argr.isnumeric ()
-      || (nargin > 3 && ! args(3).is_string ()))
-    print_usage ();
-
-  std::string orient = (nargin < 4) ? "col" : args(3).string_value ();
-  bool col = orient == "col";
-
-  if (! col && orient != "row")
-    error (R"(qrdelete: ORIENT must be "col" or "row")");
-
-  if (! check_qr_dims (argq, argr, col))
-    error ("qrdelete: dimension mismatch");
-
-  MArray<octave_idx_type> j = argj.octave_idx_type_vector_value ();
-  if (! check_index (argj, col))
-    error ("qrdelete: invalid index J");
-
-  octave_value_list retval;
-
-  octave_idx_type one = 1;
-
-  if (argq.isreal () && argr.isreal ())
-    {
-      // real case
-      if (argq.is_single_type () || argr.is_single_type ())
-        {
-          FloatMatrix Q = argq.float_matrix_value ();
-          FloatMatrix R = argr.float_matrix_value ();
-
-          octave::math::qr<FloatMatrix> fact (Q, R);
-
-          if (col)
-            fact.delete_col (j-one);
-          else
-            fact.delete_row (j(0)-one);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-      else
-        {
-          Matrix Q = argq.matrix_value ();
-          Matrix R = argr.matrix_value ();
-
-          octave::math::qr<Matrix> fact (Q, R);
-
-          if (col)
-            fact.delete_col (j-one);
-          else
-            fact.delete_row (j(0)-one);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-    }
-  else
-    {
-      // complex case
-      if (argq.is_single_type () || argr.is_single_type ())
-        {
-          FloatComplexMatrix Q = argq.float_complex_matrix_value ();
-          FloatComplexMatrix R = argr.float_complex_matrix_value ();
-
-          octave::math::qr<FloatComplexMatrix> fact (Q, R);
-
-          if (col)
-            fact.delete_col (j-one);
-          else
-            fact.delete_row (j(0)-one);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-      else
-        {
-          ComplexMatrix Q = argq.complex_matrix_value ();
-          ComplexMatrix R = argr.complex_matrix_value ();
-
-          octave::math::qr<ComplexMatrix> fact (Q, R);
-
-          if (col)
-            fact.delete_col (j-one);
-          else
-            fact.delete_row (j(0)-one);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-    }
-
-  return retval;
-}
-
-/*
-%!test
-%! AA = [0.091364  0.613038  0.027504  0.999083;
-%!       0.594638  0.425302  0.562834  0.603537;
-%!       0.383594  0.291238  0.742073  0.085574;
-%!       0.265712  0.268003  0.783553  0.238409;
-%!       0.669966  0.743851  0.457255  0.445057 ];
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrdelete (Q, R, 3);
-%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 16*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps);
-%!
-%!test
-%! AA = [0.364554 + 0.993117i  0.669818 + 0.510234i  0.426568 + 0.041337i  0.847051 + 0.233291i;
-%!       0.049600 + 0.242783i  0.448946 + 0.484022i  0.141155 + 0.074420i  0.446746 + 0.392706i;
-%!       0.581922 + 0.657416i  0.581460 + 0.030016i  0.219909 + 0.447288i  0.201144 + 0.069132i;
-%!       0.694986 + 0.000571i  0.682327 + 0.841712i  0.807537 + 0.166086i  0.192767 + 0.358098i;
-%!       0.945002 + 0.066788i  0.350492 + 0.642638i  0.579629 + 0.048102i  0.600170 + 0.636938i ] * I;
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrdelete (Q, R, 3);
-%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 16*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps);
-%!
-%!test
-%! AA = [0.091364  0.613038  0.027504  0.999083;
-%!       0.594638  0.425302  0.562834  0.603537;
-%!       0.383594  0.291238  0.742073  0.085574;
-%!       0.265712  0.268003  0.783553  0.238409;
-%!       0.669966  0.743851  0.457255  0.445057 ];
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrdelete (Q, R, 3, "row");
-%! assert (norm (vec (Q'*Q - eye (4)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps);
-%!
-%!test
-%! AA = [0.364554 + 0.993117i  0.669818 + 0.510234i  0.426568 + 0.041337i  0.847051 + 0.233291i;
-%!       0.049600 + 0.242783i  0.448946 + 0.484022i  0.141155 + 0.074420i  0.446746 + 0.392706i;
-%!       0.581922 + 0.657416i  0.581460 + 0.030016i  0.219909 + 0.447288i  0.201144 + 0.069132i;
-%!       0.694986 + 0.000571i  0.682327 + 0.841712i  0.807537 + 0.166086i  0.192767 + 0.358098i;
-%!       0.945002 + 0.066788i  0.350492 + 0.642638i  0.579629 + 0.048102i  0.600170 + 0.636938i ] * I;
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrdelete (Q, R, 3, "row");
-%! assert (norm (vec (Q'*Q - eye (4)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps);
-
-%!test
-%! AA = single ([0.091364  0.613038  0.027504  0.999083;
-%!               0.594638  0.425302  0.562834  0.603537;
-%!               0.383594  0.291238  0.742073  0.085574;
-%!               0.265712  0.268003  0.783553  0.238409;
-%!               0.669966  0.743851  0.457255  0.445057 ]);
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrdelete (Q, R, 3);
-%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps ("single"));
-%!
-%!test
-%! AA = single ([0.364554 + 0.993117i  0.669818 + 0.510234i  0.426568 + 0.041337i  0.847051 + 0.233291i;
-%!               0.049600 + 0.242783i  0.448946 + 0.484022i  0.141155 + 0.074420i  0.446746 + 0.392706i;
-%!               0.581922 + 0.657416i  0.581460 + 0.030016i  0.219909 + 0.447288i  0.201144 + 0.069132i;
-%!               0.694986 + 0.000571i  0.682327 + 0.841712i  0.807537 + 0.166086i  0.192767 + 0.358098i;
-%!               0.945002 + 0.066788i  0.350492 + 0.642638i  0.579629 + 0.048102i  0.600170 + 0.636938i ]) * I;
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrdelete (Q, R, 3);
-%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps ("single"));
-%!
-%!test
-%! AA = single ([0.091364  0.613038  0.027504  0.999083;
-%!               0.594638  0.425302  0.562834  0.603537;
-%!               0.383594  0.291238  0.742073  0.085574;
-%!               0.265712  0.268003  0.783553  0.238409;
-%!               0.669966  0.743851  0.457255  0.445057 ]);
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrdelete (Q, R, 3, "row");
-%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1.5e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single"));
-%!testif HAVE_QRUPDATE
-%! ## Same test as above but with more precicision
-%! AA = single ([0.091364  0.613038  0.027504  0.999083;
-%!               0.594638  0.425302  0.562834  0.603537;
-%!               0.383594  0.291238  0.742073  0.085574;
-%!               0.265712  0.268003  0.783553  0.238409;
-%!               0.669966  0.743851  0.457255  0.445057 ]);
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrdelete (Q, R, 3, "row");
-%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single"));
-%!
-%!test
-%! AA = single ([0.364554 + 0.993117i  0.669818 + 0.510234i  0.426568 + 0.041337i  0.847051 + 0.233291i;
-%!              0.049600 + 0.242783i  0.448946 + 0.484022i  0.141155 + 0.074420i  0.446746 + 0.392706i;
-%!              0.581922 + 0.657416i  0.581460 + 0.030016i  0.219909 + 0.447288i  0.201144 + 0.069132i;
-%!              0.694986 + 0.000571i  0.682327 + 0.841712i  0.807537 + 0.166086i  0.192767 + 0.358098i;
-%!              0.945002 + 0.066788i  0.350492 + 0.642638i  0.579629 + 0.048102i  0.600170 + 0.636938i ]) * I;
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrdelete (Q, R, 3, "row");
-%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single"));
-*/
-
-DEFUN_DLD (qrshift, args, ,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {[@var{Q1}, @var{R1}] =} qrshift (@var{Q}, @var{R}, @var{i}, @var{j})
-Update a QR factorization given a range of columns to shift in the original
-factored matrix.
-
-Given a QR@tie{}factorization of a real or complex matrix
-@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and
-@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization
-of @w{@var{A}(:,p)}, where @w{p} is the permutation @*
-@code{p = [1:i-1, shift(i:j, 1), j+1:n]} if @w{@var{i} < @var{j}} @*
- or @*
-@code{p = [1:j-1, shift(j:i,-1), i+1:n]} if @w{@var{j} < @var{i}}.  @*
-
-@seealso{qr, qrupdate, qrinsert, qrdelete}
-@end deftypefn */)
-{
-  if (args.length () != 4)
-    print_usage ();
-
-  octave_value argq = args(0);
-  octave_value argr = args(1);
-  octave_value argi = args(2);
-  octave_value argj = args(3);
-
-  if (! argq.isnumeric () || ! argr.isnumeric ())
-    print_usage ();
-
-  if (! check_qr_dims (argq, argr, true))
-    error ("qrshift: dimensions mismatch");
-
-  octave_idx_type i = argi.idx_type_value ();
-  octave_idx_type j = argj.idx_type_value ();
-
-  if (! check_index (argi) || ! check_index (argj))
-    error ("qrshift: invalid index I or J");
-
-  octave_value_list retval;
-
-  if (argq.isreal () && argr.isreal ())
-    {
-      // all real case
-      if (argq.is_single_type ()
-          && argr.is_single_type ())
-        {
-          FloatMatrix Q = argq.float_matrix_value ();
-          FloatMatrix R = argr.float_matrix_value ();
-
-          octave::math::qr<FloatMatrix> fact (Q, R);
-          fact.shift_cols (i-1, j-1);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-      else
-        {
-          Matrix Q = argq.matrix_value ();
-          Matrix R = argr.matrix_value ();
-
-          octave::math::qr<Matrix> fact (Q, R);
-          fact.shift_cols (i-1, j-1);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-    }
-  else
-    {
-      // complex case
-      if (argq.is_single_type ()
-          && argr.is_single_type ())
-        {
-          FloatComplexMatrix Q = argq.float_complex_matrix_value ();
-          FloatComplexMatrix R = argr.float_complex_matrix_value ();
-
-          octave::math::qr<FloatComplexMatrix> fact (Q, R);
-          fact.shift_cols (i-1, j-1);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-      else
-        {
-          ComplexMatrix Q = argq.complex_matrix_value ();
-          ComplexMatrix R = argr.complex_matrix_value ();
-
-          octave::math::qr<ComplexMatrix> fact (Q, R);
-          fact.shift_cols (i-1, j-1);
-
-          retval = ovl (fact.Q (), get_qr_r (fact));
-        }
-    }
-
-  return retval;
-}
-
-/*
-%!test
-%! AA = A.';
-%! i = 2;  j = 4;  p = [1:i-1, shift(i:j,-1), j+1:5];
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrshift (Q, R, i, j);
-%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps);
-%!
-%! j = 2;  i = 4;  p = [1:j-1, shift(j:i,+1), i+1:5];
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrshift (Q, R, i, j);
-%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps);
-%!
-%!test
-%! AA = Ac.';
-%! i = 2;  j = 4;  p = [1:i-1, shift(i:j,-1), j+1:5];
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrshift (Q, R, i, j);
-%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps);
-%!
-%! j = 2;  i = 4;  p = [1:j-1, shift(j:i,+1), i+1:5];
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrshift (Q, R, i, j);
-%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps);
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps);
-
-%!test
-%! AA = single (A).';
-%! i = 2;  j = 4;  p = [1:i-1, shift(i:j,-1), j+1:5];
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrshift (Q, R, i, j);
-%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single"));
-%!
-%! j = 2;  i = 4;  p = [1:j-1, shift(j:i,+1), i+1:5];
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrshift (Q, R, i, j);
-%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single"));
-%!
-%!test
-%! AA = single (Ac).';
-%! i = 2;  j = 4;  p = [1:i-1, shift(i:j,-1), j+1:5];
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrshift (Q, R, i, j);
-%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single"));
-%!
-%! j = 2;  i = 4;  p = [1:j-1, shift(j:i,+1), i+1:5];
-%!
-%! [Q,R] = qr (AA);
-%! [Q,R] = qrshift (Q, R, i, j);
-%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single"));
-%! assert (norm (vec (triu (R) - R), Inf) == 0);
-%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single"));
-*/
--- a/libinterp/dldfcn/symbfact.cc	Sat Jan 25 15:26:07 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,431 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 1998-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include <cmath>
-
-#include <algorithm>
-#include <string>
-
-#include "CSparse.h"
-#include "boolSparse.h"
-#include "dColVector.h"
-#include "dSparse.h"
-#include "oct-locbuf.h"
-#include "oct-sparse.h"
-#include "oct-spparms.h"
-#include "sparse-util.h"
-
-#include "defun-dld.h"
-#include "error.h"
-#include "errwarn.h"
-#include "ovl.h"
-#include "parse.h"
-#include "utils.h"
-
-DEFMETHOD_DLD (symbfact, interp, args, nargout,
-               doc: /* -*- texinfo -*-
-@deftypefn  {} {[@var{count}, @var{h}, @var{parent}, @var{post}, @var{R}] =} symbfact (@var{S})
-@deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ})
-@deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ}, @var{mode})
-
-Perform a symbolic factorization analysis of the sparse matrix @var{S}.
-
-The input variables are
-
-@table @var
-@item S
-@var{S} is a real or complex sparse matrix.
-
-@item typ
-Is the type of the factorization and can be one of
-
-@table @asis
-@item @qcode{"sym"} (default)
-Factorize @var{S}.  Assumes @var{S} is symmetric and uses the upper
-triangular portion of the matrix.
-
-@item @qcode{"col"}
-Factorize @tcode{@var{S}' * @var{S}}.
-
-@item @qcode{"row"}
-Factorize @tcode{@var{S} * @var{S}'}.
-
-@item @qcode{"lo"}
-Factorize @tcode{@var{S}'}.  Assumes @var{S} is symmetric and uses the lower
-triangular portion of the matrix.
-@end table
-
-@item mode
-When @var{mode} is unspecified return the Cholesky@tie{}factorization for
-@var{R}.  If @var{mode} is @qcode{"lower"} or @qcode{"L"} then return
-the conjugate transpose @tcode{@var{R}'} which is a lower triangular factor.
-The conjugate transpose version is faster and uses less memory, but still
-returns the same values for all other outputs: @var{count}, @var{h},
-@var{parent}, and @var{post}.
-@end table
-
-The output variables are:
-
-@table @var
-@item count
-The row counts of the Cholesky@tie{}factorization as determined by
-@var{typ}.  The computational difficulty of performing the true
-factorization using @code{chol} is @code{sum (@var{count} .^ 2)}.
-
-@item h
-The height of the elimination tree.
-
-@item parent
-The elimination tree itself.
-
-@item post
-A sparse boolean matrix whose structure is that of the
-Cholesky@tie{}factorization as determined by @var{typ}.
-@end table
-@seealso{chol, etree, treelayout}
-@end deftypefn */)
-{
-#if defined (HAVE_CHOLMOD)
-
-  int nargin = args.length ();
-
-  if (nargin < 1 || nargin > 3)
-    print_usage ();
-
-  octave_value_list retval;
-
-  double dummy;
-  cholmod_sparse Astore;
-  cholmod_sparse *A = &Astore;
-  A->packed = true;
-  A->sorted = true;
-  A->nz = nullptr;
-#if defined (OCTAVE_ENABLE_64)
-  A->itype = CHOLMOD_LONG;
-#else
-  A->itype = CHOLMOD_INT;
-#endif
-  A->dtype = CHOLMOD_DOUBLE;
-  A->stype = 1;
-  A->x = &dummy;
-
-  if (args(0).isreal ())
-    {
-      const SparseMatrix a = args(0).sparse_matrix_value ();
-      A->nrow = a.rows ();
-      A->ncol = a.cols ();
-      A->p = a.cidx ();
-      A->i = a.ridx ();
-      A->nzmax = a.nnz ();
-      A->xtype = CHOLMOD_REAL;
-
-      if (a.rows () > 0 && a.cols () > 0)
-        A->x = a.data ();
-    }
-  else if (args(0).iscomplex ())
-    {
-      const SparseComplexMatrix a = args(0).sparse_complex_matrix_value ();
-      A->nrow = a.rows ();
-      A->ncol = a.cols ();
-      A->p = a.cidx ();
-      A->i = a.ridx ();
-      A->nzmax = a.nnz ();
-      A->xtype = CHOLMOD_COMPLEX;
-
-      if (a.rows () > 0 && a.cols () > 0)
-        A->x = a.data ();
-    }
-  else
-    err_wrong_type_arg ("symbfact", args(0));
-
-  bool coletree = false;
-  octave_idx_type n = A->nrow;
-
-  if (nargin > 1)
-    {
-      std::string str = args(1).xstring_value ("TYP must be a string");
-      // FIXME: The input validation could be improved to use strncmp
-      char ch;
-      ch = tolower (str[0]);
-      if (ch == 'r')          // 'row'
-        A->stype = 0;
-      else if (ch == 'c')     // 'col'
-        {
-          n = A->ncol;
-          coletree = true;
-          A->stype = 0;
-        }
-      else if (ch == 's')     // 'sym' (default)
-        A->stype = 1;
-      else if (ch == 'l')     // 'lo'
-        A->stype = -1;
-      else
-        error (R"(symbfact: unrecognized TYP "%s")", str.c_str ());
-    }
-
-  if (nargin == 3)
-    {
-      std::string str = args(2).xstring_value ("MODE must be a string");
-      // FIXME: The input validation could be improved to use strncmp
-      char ch;
-      ch = toupper (str[0]);
-      if (ch != 'L')
-        error (R"(symbfact: unrecognized MODE "%s")", str.c_str ());
-    }
-
-  if (A->stype && A->nrow != A->ncol)
-    err_square_matrix_required ("symbfact", "S");
-
-  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, Parent, n);
-  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, Post, n);
-  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, ColCount, n);
-  OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, First, n);
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, Level, n);
-
-  cholmod_common Common;
-  cholmod_common *cm = &Common;
-  CHOLMOD_NAME(start) (cm);
-
-  // Lock the function to not loose the SuiteSparse_config structure
-  interp.mlock ();
-
-  double spu = octave_sparse_params::get_key ("spumoni");
-  if (spu == 0.0)
-    {
-      cm->print = -1;
-      SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, nullptr);
-    }
-  else
-    {
-      cm->print = static_cast<int> (spu) + 2;
-      SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, &SparseCholPrint);
-    }
-
-  cm->error_handler = &SparseCholError;
-  SUITESPARSE_ASSIGN_FPTR2 (divcomplex_func, cm->complex_divide, divcomplex);
-  SUITESPARSE_ASSIGN_FPTR2 (hypot_func, cm->hypotenuse, hypot);
-
-  cholmod_sparse *F = CHOLMOD_NAME(transpose) (A, 0, cm);
-  cholmod_sparse *Aup, *Alo;
-
-  if (A->stype == 1 || coletree)
-    {
-      Aup = A;
-      Alo = F;
-    }
-  else
-    {
-      Aup = F;
-      Alo = A;
-    }
-
-  CHOLMOD_NAME(etree) (Aup, Parent, cm);
-
-  ColumnVector tmp (n);    // Declaration must precede any goto cleanup.
-  std::string err_msg;
-
-  if (cm->status < CHOLMOD_OK)
-    {
-      err_msg = "symbfact: matrix corrupted";
-      goto cleanup;
-    }
-
-  if (CHOLMOD_NAME(postorder) (Parent, n, nullptr, Post, cm) != n)
-    {
-      err_msg = "symbfact: postorder failed";
-      goto cleanup;
-    }
-
-  CHOLMOD_NAME(rowcolcounts) (Alo, nullptr, 0, Parent, Post, nullptr, ColCount,
-                              First, octave::to_suitesparse_intptr (Level), cm);
-
-  if (cm->status < CHOLMOD_OK)
-    {
-      err_msg = "symbfact: matrix corrupted";
-      goto cleanup;
-    }
-
-  if (nargout > 4)
-    {
-      cholmod_sparse *A1, *A2;
-
-      if (A->stype == 1)
-        {
-          A1 = A;
-          A2 = nullptr;
-        }
-      else if (A->stype == -1)
-        {
-          A1 = F;
-          A2 = nullptr;
-        }
-      else if (coletree)
-        {
-          A1 = F;
-          A2 = A;
-        }
-      else
-        {
-          A1 = A;
-          A2 = F;
-        }
-
-      // count the total number of entries in L
-      octave_idx_type lnz = 0;
-      for (octave_idx_type j = 0 ; j < n ; j++)
-        lnz += ColCount[j];
-
-      // allocate the output matrix L (pattern-only)
-      SparseBoolMatrix L (dim_vector (n, n), lnz);
-
-      // initialize column pointers
-      lnz = 0;
-      for (octave_idx_type j = 0 ; j < n ; j++)
-        {
-          L.xcidx(j) = lnz;
-          lnz += ColCount[j];
-        }
-      L.xcidx(n) = lnz;
-
-      // create a copy of the column pointers
-      octave::suitesparse_integer *W = First;
-      for (octave_idx_type j = 0 ; j < n ; j++)
-        W[j] = L.xcidx (j);
-
-      // get workspace for computing one row of L
-      cholmod_sparse *R
-        = CHOLMOD_NAME(allocate_sparse) (n, 1, n, false, true,
-                                         0, CHOLMOD_PATTERN, cm);
-      octave_idx_type *Rp = static_cast<octave_idx_type *> (R->p);
-      octave_idx_type *Ri = static_cast<octave_idx_type *> (R->i);
-
-      // compute L one row at a time
-      for (octave_idx_type k = 0 ; k < n ; k++)
-        {
-          // get the kth row of L and store in the columns of L
-          CHOLMOD_NAME(row_subtree) (A1, A2, k, Parent, R, cm);
-          for (octave_idx_type p = 0 ; p < Rp[1] ; p++)
-            L.xridx (W[Ri[p]]++) = k;
-
-          // add the diagonal entry
-          L.xridx (W[k]++) = k;
-        }
-
-      // free workspace
-      CHOLMOD_NAME(free_sparse) (&R, cm);
-
-      // fill L with one's
-      std::fill_n (L.xdata (), lnz, true);
-
-      // transpose L to get R, or leave as is
-      if (nargin < 3)
-        L = L.transpose ();
-
-      retval(4) = L;
-    }
-
-  if (nargout > 3)
-    {
-      for (octave_idx_type i = 0; i < n; i++)
-        tmp(i) = Post[i] + 1;
-      retval(3) = tmp;
-    }
-
-  if (nargout > 2)
-    {
-      for (octave_idx_type i = 0; i < n; i++)
-        tmp(i) = Parent[i] + 1;
-      retval(2) = tmp;
-    }
-
-  if (nargout > 1)
-    {
-      // compute the elimination tree height
-      octave_idx_type height = 0;
-      for (int i = 0 ; i < n ; i++)
-        height = std::max (height, Level[i]);
-      height++;
-      retval(1) = static_cast<double> (height);
-    }
-
-  for (octave_idx_type i = 0; i < n; i++)
-    tmp(i) = ColCount[i];
-  retval(0) = tmp;
-
-cleanup:
-  CHOLMOD_NAME(free_sparse) (&F, cm);
-  CHOLMOD_NAME(finish) (cm);
-
-  if (! err_msg.empty ())
-    error ("%s", err_msg.c_str ());
-
-  return retval;
-
-#else
-
-  octave_unused_parameter (args);
-  octave_unused_parameter (nargout);
-
-  err_disabled_feature ("symbfact", "CHOLMOD");
-
-#endif
-}
-
-/*
-%!testif HAVE_CHOLMOD
-%! A = sparse (magic (3));
-%! [count, h, parent, post, r] = symbfact (A);
-%! assert (count, [3; 2; 1]);
-%! assert (h, 3);
-%! assert (parent, [2; 3; 0]);
-%! assert (r, sparse (triu (true (3))));
-
-%!testif HAVE_CHOLMOD
-%! ## Test MODE "lower"
-%! A = sparse (magic (3));
-%! [~, ~, ~, ~, l] = symbfact (A, "sym", "lower");
-%! assert (l, sparse (tril (true (3))));
-
-%!testif HAVE_CHOLMOD <*42587>
-%! ## singular matrix
-%! A = sparse ([1 0 8;0 1 8;8 8 1]);
-%! [count, h, parent, post, r] = symbfact (A);
-
-## Test input validation
-%!testif HAVE_CHOLMOD
-%! fail ("symbfact ()");
-%! fail ("symbfact (1,2,3,4)");
-%! fail ("symbfact ({1})", "wrong type argument 'cell'");
-%! fail ("symbfact (sparse (1), {1})", "TYP must be a string");
-%! fail ("symbfact (sparse (1), 'foobar')", 'unrecognized TYP "foobar"');
-%! fail ("symbfact (sparse (1), 'sym', {'L'})", "MODE must be a string");
-%! fail ('symbfact (sparse (1), "sym", "foobar")', 'unrecognized MODE "foobar"');
-%! fail ("symbfact (sparse ([1, 2; 3, 4; 5, 6]))", "S must be a square matrix");
-
-*/
--- a/libinterp/dldfcn/symrcm.cc	Sat Jan 25 15:26:07 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,705 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2007-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-/*
-An implementation of the Reverse Cuthill-McKee algorithm (symrcm)
-
-The implementation of this algorithm is based in the descriptions found in
-
-@INPROCEEDINGS{,
-        author = {E. Cuthill and J. McKee},
-        title = {Reducing the Bandwidth of Sparse Symmetric Matrices},
-        booktitle = {Proceedings of the 24th ACM National Conference},
-        publisher = {Brandon Press},
-        pages = {157 -- 172},
-        location = {New Jersey},
-        year = {1969}
-}
-
-@BOOK{,
-        author = {Alan George and Joseph W. H. Liu},
-        title = {Computer Solution of Large Sparse Positive Definite Systems},
-        publisher = {Prentice Hall Series in Computational Mathematics},
-        ISBN = {0-13-165274-5},
-        year = {1981}
-}
-
-The algorithm represents a heuristic approach to the NP-complete minimum
-bandwidth problem.
-
-Written by Michael Weitzel <michael.weitzel@@uni-siegen.de>
-                           <weitzel@@ldknet.org>
-*/
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include <algorithm>
-
-#include "CSparse.h"
-#include "boolNDArray.h"
-#include "dNDArray.h"
-#include "dSparse.h"
-#include "oct-locbuf.h"
-#include "oct-sparse.h"
-#include "quit.h"
-
-#include "defun-dld.h"
-#include "errwarn.h"
-#include "ov.h"
-#include "ovl.h"
-
-// A node struct for the Cuthill-McKee algorithm
-struct CMK_Node
-{
-  // the node's id (matrix row index)
-  octave_idx_type id;
-  // the node's degree
-  octave_idx_type deg;
-  // minimal distance to the root of the spanning tree
-  octave_idx_type dist;
-};
-
-// A simple queue.
-// Queues Q have a fixed maximum size N (rows,cols of the matrix) and are
-// stored in an array.  qh and qt point to queue head and tail.
-
-// Enqueue operation (adds a node "o" at the tail)
-
-inline static void
-Q_enq (CMK_Node *Q, octave_idx_type N, octave_idx_type& qt, const CMK_Node& o)
-{
-  Q[qt] = o;
-  qt = (qt + 1) % (N + 1);
-}
-
-// Dequeue operation (removes a node from the head)
-
-inline static CMK_Node
-Q_deq (CMK_Node * Q, octave_idx_type N, octave_idx_type& qh)
-{
-  CMK_Node r = Q[qh];
-  qh = (qh + 1) % (N + 1);
-  return r;
-}
-
-// Predicate (queue empty)
-#define Q_empty(Q, N, qh, qt)   ((qh) == (qt))
-
-// A simple, array-based binary heap (used as a priority queue for nodes)
-
-// the left descendant of entry i
-#define LEFT(i)         (((i) << 1) + 1)        // = (2*(i)+1)
-// the right descendant of entry i
-#define RIGHT(i)        (((i) << 1) + 2)        // = (2*(i)+2)
-// the parent of entry i
-#define PARENT(i)       (((i) - 1) >> 1)        // = floor(((i)-1)/2)
-
-// Builds a min-heap (the root contains the smallest element).  A is an array
-// with the graph's nodes, i is a starting position, size is the length of A.
-
-static void
-H_heapify_min (CMK_Node *A, octave_idx_type i, octave_idx_type size)
-{
-  octave_idx_type j = i;
-  for (;;)
-    {
-      octave_idx_type l = LEFT(j);
-      octave_idx_type r = RIGHT(j);
-
-      octave_idx_type smallest;
-      if (l < size && A[l].deg < A[j].deg)
-        smallest = l;
-      else
-        smallest = j;
-
-      if (r < size && A[r].deg < A[smallest].deg)
-        smallest = r;
-
-      if (smallest != j)
-        {
-          std::swap (A[j], A[smallest]);
-          j = smallest;
-        }
-      else
-        break;
-    }
-}
-
-// Heap operation insert.  Running time is O(log(n))
-
-static void
-H_insert (CMK_Node *H, octave_idx_type& h, const CMK_Node& o)
-{
-  octave_idx_type i = h++;
-
-  H[i] = o;
-
-  if (i == 0)
-    return;
-  do
-    {
-      octave_idx_type p = PARENT(i);
-      if (H[i].deg < H[p].deg)
-        {
-          std::swap (H[i], H[p]);
-
-          i = p;
-        }
-      else
-        break;
-    }
-  while (i > 0);
-}
-
-// Heap operation remove-min.  Removes the smallest element in O(1) and
-// reorganizes the heap optionally in O(log(n))
-
-inline static CMK_Node
-H_remove_min (CMK_Node *H, octave_idx_type& h, int reorg/*=1*/)
-{
-  CMK_Node r = H[0];
-  H[0] = H[--h];
-  if (reorg)
-    H_heapify_min (H, 0, h);
-  return r;
-}
-
-// Predicate (heap empty)
-#define H_empty(H, h)   ((h) == 0)
-
-// Helper function for the Cuthill-McKee algorithm.  Tries to determine a
-// pseudo-peripheral node of the graph as starting node.
-
-static octave_idx_type
-find_starting_node (octave_idx_type N, const octave_idx_type *ridx,
-                    const octave_idx_type *cidx, const octave_idx_type *ridx2,
-                    const octave_idx_type *cidx2, octave_idx_type *D,
-                    octave_idx_type start)
-{
-  CMK_Node w;
-
-  OCTAVE_LOCAL_BUFFER (CMK_Node, Q, N+1);
-  boolNDArray btmp (dim_vector (1, N), false);
-  bool *visit = btmp.fortran_vec ();
-
-  octave_idx_type qh = 0;
-  octave_idx_type qt = 0;
-  CMK_Node x;
-  x.id = start;
-  x.deg = D[start];
-  x.dist = 0;
-  Q_enq (Q, N, qt, x);
-  visit[start] = true;
-
-  // distance level
-  octave_idx_type level = 0;
-  // current largest "eccentricity"
-  octave_idx_type max_dist = 0;
-
-  for (;;)
-    {
-      while (! Q_empty (Q, N, qh, qt))
-        {
-          CMK_Node v = Q_deq (Q, N, qh);
-
-          if (v.dist > x.dist || (v.id != x.id && v.deg > x.deg))
-            x = v;
-
-          octave_idx_type i = v.id;
-
-          // add all unvisited neighbors to the queue
-          octave_idx_type j1 = cidx[i];
-          octave_idx_type j2 = cidx2[i];
-          while (j1 < cidx[i+1] || j2 < cidx2[i+1])
-            {
-              octave_quit ();
-
-              if (j1 == cidx[i+1])
-                {
-                  octave_idx_type r2 = ridx2[j2++];
-                  if (! visit[r2])
-                    {
-                      // the distance of node j is dist(i)+1
-                      w.id = r2;
-                      w.deg = D[r2];
-                      w.dist = v.dist+1;
-                      Q_enq (Q, N, qt, w);
-                      visit[r2] = true;
-
-                      if (w.dist > level)
-                        level = w.dist;
-                    }
-                }
-              else if (j2 == cidx2[i+1])
-                {
-                  octave_idx_type r1 = ridx[j1++];
-                  if (! visit[r1])
-                    {
-                      // the distance of node j is dist(i)+1
-                      w.id = r1;
-                      w.deg = D[r1];
-                      w.dist = v.dist+1;
-                      Q_enq (Q, N, qt, w);
-                      visit[r1] = true;
-
-                      if (w.dist > level)
-                        level = w.dist;
-                    }
-                }
-              else
-                {
-                  octave_idx_type r1 = ridx[j1];
-                  octave_idx_type r2 = ridx2[j2];
-                  if (r1 <= r2)
-                    {
-                      if (! visit[r1])
-                        {
-                          w.id = r1;
-                          w.deg = D[r1];
-                          w.dist = v.dist+1;
-                          Q_enq (Q, N, qt, w);
-                          visit[r1] = true;
-
-                          if (w.dist > level)
-                            level = w.dist;
-                        }
-                      j1++;
-                      if (r1 == r2)
-                        j2++;
-                    }
-                  else
-                    {
-                      if (! visit[r2])
-                        {
-                          w.id = r2;
-                          w.deg = D[r2];
-                          w.dist = v.dist+1;
-                          Q_enq (Q, N, qt, w);
-                          visit[r2] = true;
-
-                          if (w.dist > level)
-                            level = w.dist;
-                        }
-                      j2++;
-                    }
-                }
-            }
-        } // finish of BFS
-
-      if (max_dist < x.dist)
-        {
-          max_dist = x.dist;
-
-          for (octave_idx_type i = 0; i < N; i++)
-            visit[i] = false;
-
-          visit[x.id] = true;
-          x.dist = 0;
-          qt = qh = 0;
-          Q_enq (Q, N, qt, x);
-        }
-      else
-        break;
-    }
-  return x.id;
-}
-
-// Calculates the node's degrees.  This means counting the nonzero elements
-// in the symmetric matrix' rows.  This works for non-symmetric matrices
-// as well.
-
-static octave_idx_type
-calc_degrees (octave_idx_type N, const octave_idx_type *ridx,
-              const octave_idx_type *cidx, octave_idx_type *D)
-{
-  octave_idx_type max_deg = 0;
-
-  for (octave_idx_type i = 0; i < N; i++)
-    D[i] = 0;
-
-  for (octave_idx_type j = 0; j < N; j++)
-    {
-      for (octave_idx_type i = cidx[j]; i < cidx[j+1]; i++)
-        {
-          octave_quit ();
-
-          octave_idx_type k = ridx[i];
-          // there is a nonzero element (k,j)
-          D[k]++;
-          if (D[k] > max_deg)
-            max_deg = D[k];
-          // if there is no element (j,k) there is one in
-          // the symmetric matrix:
-          if (k != j)
-            {
-              bool found = false;
-              for (octave_idx_type l = cidx[k]; l < cidx[k + 1]; l++)
-                {
-                  octave_quit ();
-
-                  if (ridx[l] == j)
-                    {
-                      found = true;
-                      break;
-                    }
-                  else if (ridx[l] > j)
-                    break;
-                }
-
-              if (! found)
-                {
-                  // A(j,k) == 0
-                  D[j]++;
-                  if (D[j] > max_deg)
-                    max_deg = D[j];
-                }
-            }
-        }
-    }
-  return max_deg;
-}
-
-// Transpose of the structure of a square sparse matrix
-
-static void
-transpose (octave_idx_type N, const octave_idx_type *ridx,
-           const octave_idx_type *cidx, octave_idx_type *ridx2,
-           octave_idx_type *cidx2)
-{
-  octave_idx_type nz = cidx[N];
-
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, w, N + 1);
-  for (octave_idx_type i = 0; i < N; i++)
-    w[i] = 0;
-  for (octave_idx_type i = 0; i < nz; i++)
-    w[ridx[i]]++;
-  nz = 0;
-  for (octave_idx_type i = 0; i < N; i++)
-    {
-      octave_quit ();
-
-      cidx2[i] = nz;
-      nz += w[i];
-      w[i] = cidx2[i];
-    }
-  cidx2[N] = nz;
-  w[N] = nz;
-
-  for (octave_idx_type j = 0; j < N; j++)
-    for (octave_idx_type k = cidx[j]; k < cidx[j + 1]; k++)
-      {
-        octave_quit ();
-
-        octave_idx_type q = w[ridx[k]]++;
-        ridx2[q] = j;
-      }
-}
-
-// An implementation of the Cuthill-McKee algorithm.
-DEFUN_DLD (symrcm, args, ,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {@var{p} =} symrcm (@var{S})
-Return the symmetric reverse @nospell{Cuthill-McKee} permutation of @var{S}.
-
-@var{p} is a permutation vector such that
-@code{@var{S}(@var{p}, @var{p})} tends to have its diagonal elements closer
-to the diagonal than @var{S}.  This is a good preordering for LU or
-Cholesky@tie{}factorization of matrices that come from ``long, skinny''
-problems.  It works for both symmetric and asymmetric @var{S}.
-
-The algorithm represents a heuristic approach to the NP-complete bandwidth
-minimization problem.  The implementation is based in the descriptions found
-in
-
-@nospell{E. Cuthill, J. McKee}.
-@cite{Reducing the Bandwidth of Sparse Symmetric Matrices}.
-Proceedings of the 24th @nospell{ACM} National Conference,
-157--172 1969, Brandon Press, New Jersey.
-
-@nospell{A. George, J.W.H. Liu}.  @cite{Computer Solution of Large Sparse
-Positive Definite Systems}, Prentice Hall Series in Computational
-Mathematics, ISBN 0-13-165274-5, 1981.
-
-@seealso{colperm, colamd, symamd}
-@end deftypefn */)
-{
-  if (args.length () != 1)
-    print_usage ();
-
-  octave_value arg = args(0);
-
-  // the parameter of the matrix is converted into a sparse matrix
-  //(if necessary)
-  octave_idx_type *cidx;
-  octave_idx_type *ridx;
-  SparseMatrix Ar;
-  SparseComplexMatrix Ac;
-
-  if (arg.isreal ())
-    {
-      Ar = arg.sparse_matrix_value ();
-      // Note cidx/ridx are const, so use xridx and xcidx...
-      cidx = Ar.xcidx ();
-      ridx = Ar.xridx ();
-    }
-  else
-    {
-      Ac = arg.sparse_complex_matrix_value ();
-      cidx = Ac.xcidx ();
-      ridx = Ac.xridx ();
-    }
-
-  octave_idx_type nr = arg.rows ();
-  octave_idx_type nc = arg.columns ();
-
-  if (nr != nc)
-    err_square_matrix_required ("symrcm", "S");
-
-  if (nr == 0 && nc == 0)
-    return ovl (NDArray (dim_vector (1, 0)));
-
-  // sizes of the heaps
-  octave_idx_type s = 0;
-
-  // head- and tail-indices for the queue
-  octave_idx_type qt = 0;
-  octave_idx_type qh = 0;
-  CMK_Node v, w;
-  // dimension of the matrix
-  octave_idx_type N = nr;
-
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, cidx2, N + 1);
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, ridx2, cidx[N]);
-  transpose (N, ridx, cidx, ridx2, cidx2);
-
-  // the permutation vector
-  NDArray P (dim_vector (1, N));
-
-  // compute the node degrees
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, D, N);
-  octave_idx_type max_deg = calc_degrees (N, ridx, cidx, D);
-
-  // if none of the nodes has a degree > 0 (a matrix of zeros)
-  // the return value corresponds to the identity permutation
-  if (max_deg == 0)
-    {
-      for (octave_idx_type i = 0; i < N; i++)
-        P(i) = i;
-
-      return ovl (P);
-    }
-
-  // a heap for the a node's neighbors.  The number of neighbors is
-  // limited by the maximum degree max_deg:
-  OCTAVE_LOCAL_BUFFER (CMK_Node, S, max_deg);
-
-  // a queue for the BFS.  The array is always one element larger than
-  // the number of entries that are stored.
-  OCTAVE_LOCAL_BUFFER (CMK_Node, Q, N+1);
-
-  // a counter (for building the permutation)
-  octave_idx_type c = -1;
-
-  // upper bound for the bandwidth (=quality of solution)
-  // initialize the bandwidth of the graph with 0.  B contains the
-  // the maximum of the theoretical lower limits of the subgraphs
-  // bandwidths.
-  octave_idx_type B = 0;
-
-  // mark all nodes as unvisited; with the exception of the nodes
-  // that have degree==0 and build a CC of the graph.
-
-  boolNDArray btmp (dim_vector (1, N), false);
-  bool *visit = btmp.fortran_vec ();
-
-  do
-    {
-      // locate an unvisited starting node of the graph
-      octave_idx_type i;
-      for (i = 0; i < N; i++)
-        if (! visit[i])
-          break;
-
-      // locate a probably better starting node
-      v.id = find_starting_node (N, ridx, cidx, ridx2, cidx2, D, i);
-
-      // mark the node as visited and enqueue it (a starting node
-      // for the BFS).  Since the node will be a root of a spanning
-      // tree, its dist is 0.
-      v.deg = D[v.id];
-      v.dist = 0;
-      visit[v.id] = true;
-      Q_enq (Q, N, qt, v);
-
-      // lower bound for the bandwidth of a subgraph
-      // keep a "level" in the spanning tree (= min. distance to the
-      // root) for determining the bandwidth of the computed
-      // permutation P
-      octave_idx_type Bsub = 0;
-      // min. dist. to the root is 0
-      octave_idx_type level = 0;
-      // the root is the first/only node on level 0
-      octave_idx_type level_N = 1;
-
-      while (! Q_empty (Q, N, qh, qt))
-        {
-          v = Q_deq (Q, N, qh);
-          i = v.id;
-
-          c++;
-
-          // for computing the inverse permutation P where
-          // A(inv(P),inv(P)) or P'*A*P is banded
-          //         P(i) = c;
-
-          // for computing permutation P where
-          // A(P(i),P(j)) or P*A*P' is banded
-          P(c) = i;
-
-          // put all unvisited neighbors j of node i on the heap
-          s = 0;
-          octave_idx_type j1 = cidx[i];
-          octave_idx_type j2 = cidx2[i];
-
-          octave_quit ();
-
-          while (j1 < cidx[i+1] || j2 < cidx2[i+1])
-            {
-              octave_quit ();
-
-              if (j1 == cidx[i+1])
-                {
-                  octave_idx_type r2 = ridx2[j2++];
-                  if (! visit[r2])
-                    {
-                      // the distance of node j is dist(i)+1
-                      w.id = r2;
-                      w.deg = D[r2];
-                      w.dist = v.dist+1;
-                      H_insert (S, s, w);
-                      visit[r2] = true;
-                    }
-                }
-              else if (j2 == cidx2[i+1])
-                {
-                  octave_idx_type r1 = ridx[j1++];
-                  if (! visit[r1])
-                    {
-                      w.id = r1;
-                      w.deg = D[r1];
-                      w.dist = v.dist+1;
-                      H_insert (S, s, w);
-                      visit[r1] = true;
-                    }
-                }
-              else
-                {
-                  octave_idx_type r1 = ridx[j1];
-                  octave_idx_type r2 = ridx2[j2];
-                  if (r1 <= r2)
-                    {
-                      if (! visit[r1])
-                        {
-                          w.id = r1;
-                          w.deg = D[r1];
-                          w.dist = v.dist+1;
-                          H_insert (S, s, w);
-                          visit[r1] = true;
-                        }
-                      j1++;
-                      if (r1 == r2)
-                        j2++;
-                    }
-                  else
-                    {
-                      if (! visit[r2])
-                        {
-                          w.id = r2;
-                          w.deg = D[r2];
-                          w.dist = v.dist+1;
-                          H_insert (S, s, w);
-                          visit[r2] = true;
-                        }
-                      j2++;
-                    }
-                }
-            }
-
-          // add the neighbors to the queue (sorted by node degree)
-          while (! H_empty (S, s))
-            {
-              octave_quit ();
-
-              // locate a neighbor of i with minimal degree in O(log(N))
-              v = H_remove_min (S, s, 1);
-
-              // entered the BFS a new level?
-              if (v.dist > level)
-                {
-                  // adjustment of bandwidth:
-                  // "[...] the minimum bandwidth that
-                  // can be obtained [...] is the
-                  //  maximum number of nodes per level"
-                  if (Bsub < level_N)
-                    Bsub = level_N;
-
-                  level = v.dist;
-                  // v is the first node on the new level
-                  level_N = 1;
-                }
-              else
-                {
-                  // there is no new level but another node on
-                  // this level:
-                  level_N++;
-                }
-
-              // enqueue v in O(1)
-              Q_enq (Q, N, qt, v);
-            }
-
-          // synchronize the bandwidth with level_N once again:
-          if (Bsub < level_N)
-            Bsub = level_N;
-        }
-      // finish of BFS.  If there are still unvisited nodes in the graph
-      // then it is split into CCs.  The computed bandwidth is the maximum
-      // of all subgraphs.  Update:
-      if (Bsub > B)
-        B = Bsub;
-    }
-  // are there any nodes left?
-  while (c+1 < N);
-
-  // compute the reverse-ordering
-  s = N / 2 - 1;
-  for (octave_idx_type i = 0, j = N - 1; i <= s; i++, j--)
-    std::swap (P.elem (i), P.elem (j));
-
-  // increment all indices, since Octave is not C
-  return ovl (P+1);
-}
--- a/scripts/help/type.m	Sat Jan 25 15:26:07 2020 +0100
+++ b/scripts/help/type.m	Wed Jan 29 06:30:40 2020 -0500
@@ -150,7 +150,7 @@
 %! txt = type ("ls", "-q");
 %! assert (regexp (txt{1}, '[#\s]*Copyright \(C\) 2006'));
 
-%!assert (type ("amd"){1}, "amd is a dynamically-linked function")
+%!assert (type ("fftw"){1}, "fftw is a dynamically-linked function")
 %!assert (type ("cat"){1}, "cat is a built-in function")
 %!assert (type ("+"){1}, "+ is an operator")
 %!assert (type ("end"){1}, "end is a keyword")
--- a/scripts/help/which.m	Sat Jan 25 15:26:07 2020 +0100
+++ b/scripts/help/which.m	Wed Jan 29 06:30:40 2020 -0500
@@ -91,8 +91,8 @@
 %! str = which ("ls");
 %! assert (str(end-17:end), fullfile ("miscellaneous", "ls.m"));
 %!test
-%! str = which ("amd");
-%! assert (str(end-6:end), "amd.oct");
+%! str = which ("fftw");
+%! assert (str(end-7:end), "fftw.oct");
 %!test
 %! str = which ("inputParser");
 %! assert (str, "built-in function");
@@ -104,14 +104,14 @@
 %!assert (which ("__NO_SUCH_NAME__"), "")
 
 %!test
-%! str = which ("amd");
-%! assert (str(end-6:end), "amd.oct");
-%! amd = 12;
-%! str = which ("amd");
+%! str = which ("fftw");
+%! assert (str(end-7:end), "fftw.oct");
+%! fftw = 12;
+%! str = which ("fftw");
 %! assert (str, "variable");
-%! clear amd;
-%! str = which ("amd");
-%! assert (str(end-6:end), "amd.oct");
+%! clear fftw;
+%! str = which ("fftw");
+%! assert (str(end-7:end), "fftw.oct");
 
 %!error which ()
 %!error which (1)