diff liboctave/numeric/sparse-qr.cc @ 31607:aac27ad79be6 stable

maint: Re-indent code after switch to using namespace macros. * build-env.h, build-env.in.cc, Cell.h, __betainc__.cc, __eigs__.cc, __ftp__.cc, __ichol__.cc, __ilu__.cc, __isprimelarge__.cc, __magick_read__.cc, __pchip_deriv__.cc, amd.cc, base-text-renderer.cc, base-text-renderer.h, besselj.cc, bitfcns.cc, bsxfun.cc, c-file-ptr-stream.h, call-stack.cc, call-stack.h, ccolamd.cc, cellfun.cc, chol.cc, colamd.cc, dasrt.cc, data.cc, debug.cc, defaults.cc, defaults.h, det.cc, display.cc, display.h, dlmread.cc, dynamic-ld.cc, dynamic-ld.h, ellipj.cc, environment.cc, environment.h, error.cc, error.h, errwarn.h, event-manager.cc, event-manager.h, event-queue.cc, event-queue.h, fcn-info.cc, fcn-info.h, fft.cc, fft2.cc, file-io.cc, filter.cc, find.cc, ft-text-renderer.cc, ft-text-renderer.h, gcd.cc, gl-render.cc, gl-render.h, gl2ps-print.cc, gl2ps-print.h, graphics-toolkit.cc, graphics-toolkit.h, graphics.cc, gsvd.cc, gtk-manager.cc, gtk-manager.h, help.cc, help.h, hook-fcn.cc, hook-fcn.h, input.cc, input.h, interpreter-private.cc, interpreter-private.h, interpreter.cc, interpreter.h, inv.cc, jsondecode.cc, jsonencode.cc, latex-text-renderer.cc, latex-text-renderer.h, load-path.cc, load-path.h, load-save.cc, load-save.h, lookup.cc, ls-hdf5.cc, ls-mat4.cc, ls-mat5.cc, lsode.cc, lu.cc, mappers.cc, matrix_type.cc, max.cc, mex.cc, mexproto.h, mxarray.h, mxtypes.in.h, oct-errno.in.cc, oct-hdf5-types.cc, oct-hist.cc, oct-hist.h, oct-map.cc, oct-map.h, oct-opengl.h, oct-prcstrm.h, oct-process.cc, oct-process.h, oct-stdstrm.h, oct-stream.cc, oct-stream.h, oct-strstrm.h, octave-default-image.h, ordqz.cc, ordschur.cc, pager.cc, pager.h, pinv.cc, pow2.cc, pr-output.cc, psi.cc, qr.cc, quadcc.cc, rand.cc, regexp.cc, settings.cc, settings.h, sighandlers.cc, sighandlers.h, sparse-xpow.cc, sqrtm.cc, stack-frame.cc, stack-frame.h, stream-euler.cc, strfns.cc, svd.cc, syminfo.cc, syminfo.h, symrcm.cc, symrec.cc, symrec.h, symscope.cc, symscope.h, symtab.cc, symtab.h, sysdep.cc, sysdep.h, text-engine.cc, text-engine.h, text-renderer.cc, text-renderer.h, time.cc, toplev.cc, typecast.cc, url-handle-manager.cc, url-handle-manager.h, urlwrite.cc, utils.cc, utils.h, variables.cc, variables.h, xdiv.cc, __delaunayn__.cc, __init_fltk__.cc, __init_gnuplot__.cc, __ode15__.cc, __voronoi__.cc, audioread.cc, convhulln.cc, gzip.cc, cdef-class.cc, cdef-class.h, cdef-fwd.h, cdef-manager.cc, cdef-manager.h, cdef-method.cc, cdef-method.h, cdef-object.cc, cdef-object.h, cdef-package.cc, cdef-package.h, cdef-property.cc, cdef-property.h, cdef-utils.cc, cdef-utils.h, ov-base-diag.cc, ov-base-int.cc, ov-base-mat.cc, ov-base-mat.h, ov-base-scalar.cc, ov-base.cc, ov-base.h, ov-bool-mat.cc, ov-bool-mat.h, ov-bool-sparse.cc, ov-bool.cc, ov-builtin.h, ov-cell.cc, ov-ch-mat.cc, ov-class.cc, ov-class.h, ov-classdef.cc, ov-classdef.h, ov-complex.cc, ov-cx-diag.cc, ov-cx-mat.cc, ov-cx-sparse.cc, ov-dld-fcn.cc, ov-dld-fcn.h, ov-fcn-handle.cc, ov-fcn-handle.h, ov-fcn.h, ov-float.cc, ov-flt-complex.cc, ov-flt-cx-diag.cc, ov-flt-cx-mat.cc, ov-flt-re-diag.cc, ov-flt-re-mat.cc, ov-flt-re-mat.h, ov-intx.h, ov-java.cc, ov-lazy-idx.cc, ov-legacy-range.cc, ov-magic-int.cc, ov-mex-fcn.cc, ov-mex-fcn.h, ov-null-mat.cc, ov-perm.cc, ov-range.cc, ov-re-diag.cc, ov-re-mat.cc, ov-re-mat.h, ov-re-sparse.cc, ov-scalar.cc, ov-str-mat.cc, ov-struct.cc, ov-typeinfo.cc, ov-typeinfo.h, ov-usr-fcn.cc, ov-usr-fcn.h, ov.cc, ov.h, ovl.h, octave.cc, octave.h, op-b-sbm.cc, op-bm-sbm.cc, op-cs-scm.cc, op-fm-fcm.cc, op-fs-fcm.cc, op-s-scm.cc, op-scm-cs.cc, op-scm-s.cc, op-sm-cs.cc, ops.h, anon-fcn-validator.cc, anon-fcn-validator.h, bp-table.cc, bp-table.h, comment-list.cc, comment-list.h, filepos.h, lex.h, oct-lvalue.cc, oct-lvalue.h, parse.h, profiler.cc, profiler.h, pt-anon-scopes.cc, pt-anon-scopes.h, pt-arg-list.cc, pt-arg-list.h, pt-args-block.cc, pt-args-block.h, pt-array-list.cc, pt-array-list.h, pt-assign.cc, pt-assign.h, pt-binop.cc, pt-binop.h, pt-bp.cc, pt-bp.h, pt-cbinop.cc, pt-cbinop.h, pt-cell.cc, pt-cell.h, pt-check.cc, pt-check.h, pt-classdef.cc, pt-classdef.h, pt-cmd.h, pt-colon.cc, pt-colon.h, pt-const.cc, pt-const.h, pt-decl.cc, pt-decl.h, pt-eval.cc, pt-eval.h, pt-except.cc, pt-except.h, pt-exp.cc, pt-exp.h, pt-fcn-handle.cc, pt-fcn-handle.h, pt-id.cc, pt-id.h, pt-idx.cc, pt-idx.h, pt-jump.h, pt-loop.cc, pt-loop.h, pt-mat.cc, pt-mat.h, pt-misc.cc, pt-misc.h, pt-pr-code.cc, pt-pr-code.h, pt-select.cc, pt-select.h, pt-spmd.cc, pt-spmd.h, pt-stmt.cc, pt-stmt.h, pt-tm-const.cc, pt-tm-const.h, pt-unop.cc, pt-unop.h, pt-walk.cc, pt-walk.h, pt.cc, pt.h, token.cc, token.h, Range.cc, Range.h, idx-vector.cc, idx-vector.h, range-fwd.h, CollocWt.cc, CollocWt.h, aepbalance.cc, aepbalance.h, chol.cc, chol.h, gepbalance.cc, gepbalance.h, gsvd.cc, gsvd.h, hess.cc, hess.h, lo-mappers.cc, lo-mappers.h, lo-specfun.cc, lo-specfun.h, lu.cc, lu.h, oct-convn.cc, oct-convn.h, oct-fftw.cc, oct-fftw.h, oct-norm.cc, oct-norm.h, oct-rand.cc, oct-rand.h, oct-spparms.cc, oct-spparms.h, qr.cc, qr.h, qrp.cc, qrp.h, randgamma.cc, randgamma.h, randmtzig.cc, randmtzig.h, randpoisson.cc, randpoisson.h, schur.cc, schur.h, sparse-chol.cc, sparse-chol.h, sparse-lu.cc, sparse-lu.h, sparse-qr.cc, sparse-qr.h, svd.cc, svd.h, child-list.cc, child-list.h, dir-ops.cc, dir-ops.h, file-ops.cc, file-ops.h, file-stat.cc, file-stat.h, lo-sysdep.cc, lo-sysdep.h, lo-sysinfo.cc, lo-sysinfo.h, mach-info.cc, mach-info.h, oct-env.cc, oct-env.h, oct-group.cc, oct-group.h, oct-password.cc, oct-password.h, oct-syscalls.cc, oct-syscalls.h, oct-time.cc, oct-time.h, oct-uname.cc, oct-uname.h, action-container.cc, action-container.h, base-list.h, cmd-edit.cc, cmd-edit.h, cmd-hist.cc, cmd-hist.h, f77-fcn.h, file-info.cc, file-info.h, lo-array-errwarn.cc, lo-array-errwarn.h, lo-hash.cc, lo-hash.h, lo-ieee.h, lo-regexp.cc, lo-regexp.h, lo-utils.cc, lo-utils.h, oct-base64.cc, oct-base64.h, oct-glob.cc, oct-glob.h, oct-inttypes.h, oct-mutex.cc, oct-mutex.h, oct-refcount.h, oct-shlib.cc, oct-shlib.h, oct-sparse.cc, oct-sparse.h, oct-string.h, octave-preserve-stream-state.h, pathsearch.cc, pathsearch.h, quit.cc, quit.h, unwind-prot.cc, unwind-prot.h, url-transfer.cc, url-transfer.h: Re-indent code after switch to using namespace macros.
author Rik <rik@octave.org>
date Thu, 01 Dec 2022 18:02:15 -0800
parents e88a07dec498
children 597f3ee61a48
line wrap: on
line diff
--- a/liboctave/numeric/sparse-qr.cc	Thu Dec 01 14:23:45 2022 -0800
+++ b/liboctave/numeric/sparse-qr.cc	Thu Dec 01 18:02:15 2022 -0800
@@ -44,3311 +44,3311 @@
 OCTAVE_BEGIN_NAMESPACE(math)
 
 #if defined (HAVE_CXSPARSE)
-    template <typename SPARSE_T>
-    class
-    cxsparse_types
-    { };
-
-    template <>
-    class
-    cxsparse_types<SparseMatrix>
-    {
-    public:
-      typedef CXSPARSE_DNAME (s) symbolic_type;
-      typedef CXSPARSE_DNAME (n) numeric_type;
-    };
-
-    template <>
-    class
-    cxsparse_types<SparseComplexMatrix>
-    {
-    public:
-      typedef CXSPARSE_ZNAME (s) symbolic_type;
-      typedef CXSPARSE_ZNAME (n) numeric_type;
-    };
+template <typename SPARSE_T>
+class
+cxsparse_types
+{ };
+
+template <>
+class
+cxsparse_types<SparseMatrix>
+{
+public:
+  typedef CXSPARSE_DNAME (s) symbolic_type;
+  typedef CXSPARSE_DNAME (n) numeric_type;
+};
+
+template <>
+class
+cxsparse_types<SparseComplexMatrix>
+{
+public:
+  typedef CXSPARSE_ZNAME (s) symbolic_type;
+  typedef CXSPARSE_ZNAME (n) numeric_type;
+};
 #endif
 
-    template <typename SPARSE_T>
-    class sparse_qr<SPARSE_T>::sparse_qr_rep
-    {
-    public:
-
-      sparse_qr_rep (const SPARSE_T& a, int order);
-
-      // No copying!
-
-      sparse_qr_rep (const sparse_qr_rep&) = delete;
-
-      sparse_qr_rep& operator = (const sparse_qr_rep&) = delete;
-
-      ~sparse_qr_rep (void);
-
-      bool ok (void) const
-      {
+template <typename SPARSE_T>
+class sparse_qr<SPARSE_T>::sparse_qr_rep
+{
+public:
+
+  sparse_qr_rep (const SPARSE_T& a, int order);
+
+  // No copying!
+
+  sparse_qr_rep (const sparse_qr_rep&) = delete;
+
+  sparse_qr_rep& operator = (const sparse_qr_rep&) = delete;
+
+  ~sparse_qr_rep (void);
+
+  bool ok (void) const
+  {
 #if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-        return (m_H && m_Htau && m_HPinv && m_R && m_E);
+    return (m_H && m_Htau && m_HPinv && m_R && m_E);
 #elif defined (HAVE_CXSPARSE)
-        return (N && S);
+    return (N && S);
 #else
-        return false;
+    return false;
 #endif
-      }
-
-      SPARSE_T V (void) const;
-
-      ColumnVector Pinv (void) const;
-
-      ColumnVector P (void) const;
-
-      ColumnVector E (void) const;
-
-      SPARSE_T R (bool econ) const;
-
-      typename SPARSE_T::dense_matrix_type
-      C (const typename SPARSE_T::dense_matrix_type& b, bool econ = false);
-
-      typename SPARSE_T::dense_matrix_type Q (bool econ = false);
-
-      octave_idx_type nrows;
-      octave_idx_type ncols;
+  }
+
+  SPARSE_T V (void) const;
+
+  ColumnVector Pinv (void) const;
+
+  ColumnVector P (void) const;
+
+  ColumnVector E (void) const;
+
+  SPARSE_T R (bool econ) const;
+
+  typename SPARSE_T::dense_matrix_type
+  C (const typename SPARSE_T::dense_matrix_type& b, bool econ = false);
+
+  typename SPARSE_T::dense_matrix_type Q (bool econ = false);
+
+  octave_idx_type nrows;
+  octave_idx_type ncols;
 
 #if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
 
-      template <typename RHS_T, typename RET_T>
-      RET_T solve (const RHS_T& b, octave_idx_type& info) const;
+  template <typename RHS_T, typename RET_T>
+  RET_T solve (const RHS_T& b, octave_idx_type& info) const;
 
 #endif
 
 #if defined (HAVE_CXSPARSE)
 
-      typename cxsparse_types<SPARSE_T>::symbolic_type *S;
-      typename cxsparse_types<SPARSE_T>::numeric_type *N;
+  typename cxsparse_types<SPARSE_T>::symbolic_type *S;
+  typename cxsparse_types<SPARSE_T>::numeric_type *N;
 
 #endif
 
-      template <typename RHS_T, typename RET_T>
-      RET_T tall_solve (const RHS_T& b, octave_idx_type& info);
-
-      template <typename RHS_T, typename RET_T>
-      RET_T wide_solve (const RHS_T& b, octave_idx_type& info) const;
+  template <typename RHS_T, typename RET_T>
+  RET_T tall_solve (const RHS_T& b, octave_idx_type& info);
+
+  template <typename RHS_T, typename RET_T>
+  RET_T wide_solve (const RHS_T& b, octave_idx_type& info) const;
 
 #if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
 
-    private:
-
-      cholmod_common m_cc;
-      cholmod_sparse *m_R;  // R factor
-      // Column permutation for A. Fill-reducing ordering.
-      SuiteSparse_long *m_E;
-      cholmod_sparse *m_H;  // Householder vectors
-      cholmod_dense *m_Htau;  // beta scalars
-      SuiteSparse_long *m_HPinv;
+private:
+
+  cholmod_common m_cc;
+  cholmod_sparse *m_R;  // R factor
+                        // Column permutation for A. Fill-reducing ordering.
+  SuiteSparse_long *m_E;
+  cholmod_sparse *m_H;  // Householder vectors
+  cholmod_dense *m_Htau;  // beta scalars
+  SuiteSparse_long *m_HPinv;
 
 #endif
-    };
-
-    template <typename SPARSE_T>
-    ColumnVector
-    sparse_qr<SPARSE_T>::sparse_qr_rep::Pinv (void) const
-    {
+};
+
+template <typename SPARSE_T>
+ColumnVector
+sparse_qr<SPARSE_T>::sparse_qr_rep::Pinv (void) const
+{
 #if defined (HAVE_CXSPARSE)
 
-      ColumnVector ret (N->L->m);
-
-      for (octave_idx_type i = 0; i < N->L->m; i++)
-        ret.xelem (i) = S->pinv[i];
-
-      return ret;
+  ColumnVector ret (N->L->m);
+
+  for (octave_idx_type i = 0; i < N->L->m; i++)
+    ret.xelem (i) = S->pinv[i];
+
+  return ret;
 
 #else
 
-      return ColumnVector ();
+  return ColumnVector ();
 
 #endif
-    }
-
-    template <typename SPARSE_T>
-    ColumnVector
-    sparse_qr<SPARSE_T>::sparse_qr_rep::P (void) const
-    {
+}
+
+template <typename SPARSE_T>
+ColumnVector
+sparse_qr<SPARSE_T>::sparse_qr_rep::P (void) const
+{
 #if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
 
-      ColumnVector ret (nrows);
-
-      // FIXME: Is ret.xelem (m_HPinv[i]) = i + 1 correct?
-      for (octave_idx_type i = 0; i < nrows; i++)
-        ret.xelem (from_suitesparse_long (m_HPinv[i])) = i + 1;
-
-      return ret;
+  ColumnVector ret (nrows);
+
+  // FIXME: Is ret.xelem (m_HPinv[i]) = i + 1 correct?
+  for (octave_idx_type i = 0; i < nrows; i++)
+    ret.xelem (from_suitesparse_long (m_HPinv[i])) = i + 1;
+
+  return ret;
 
 #elif defined (HAVE_CXSPARSE)
 
-      ColumnVector ret (N->L->m);
-
-      for (octave_idx_type i = 0; i < N->L->m; i++)
-        ret.xelem (S->pinv[i]) = i;
-
-      return ret;
+  ColumnVector ret (N->L->m);
+
+  for (octave_idx_type i = 0; i < N->L->m; i++)
+    ret.xelem (S->pinv[i]) = i;
+
+  return ret;
 
 #else
 
-      return ColumnVector ();
+  return ColumnVector ();
 
 #endif
-    }
-
-    template <typename SPARSE_T>
-    ColumnVector
-    sparse_qr<SPARSE_T>::sparse_qr_rep::E (void) const
-    {
+}
+
+template <typename SPARSE_T>
+ColumnVector
+sparse_qr<SPARSE_T>::sparse_qr_rep::E (void) const
+{
 #if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
 
-      ColumnVector ret (ncols);
-
-      if (m_E)
-        for (octave_idx_type i = 0; i < ncols; i++)
-          ret(i) = from_suitesparse_long (m_E[i]) + 1;
-      else
-        for (octave_idx_type i = 0; i < ncols; i++)
-          ret(i) = i + 1;
-
-      return ret;
+  ColumnVector ret (ncols);
+
+  if (m_E)
+    for (octave_idx_type i = 0; i < ncols; i++)
+      ret(i) = from_suitesparse_long (m_E[i]) + 1;
+  else
+    for (octave_idx_type i = 0; i < ncols; i++)
+      ret(i) = i + 1;
+
+  return ret;
 
 #else
 
-      return ColumnVector ();
+  return ColumnVector ();
 
 #endif
-    }
+}
 
 #if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
 
-    // Convert real sparse octave matrix to real sparse cholmod matrix.
-    // Returns a "shallow" copy of a.
-    static cholmod_sparse
-    ros2rcs (const SparseMatrix& a)
+// Convert real sparse octave matrix to real sparse cholmod matrix.
+// Returns a "shallow" copy of a.
+static cholmod_sparse
+ros2rcs (const SparseMatrix& a)
+{
+  cholmod_sparse A;
+
+  octave_idx_type ncols = a.cols ();
+  octave_idx_type nnz = a.nnz ();
+
+  A.ncol = ncols;
+  A.nrow = a.rows ();
+  A.itype = CHOLMOD_LONG;
+  A.nzmax = nnz;
+  A.sorted = 0;
+  A.packed = 1;
+  A.stype = 0;
+  A.xtype = CHOLMOD_REAL;
+  A.dtype = CHOLMOD_DOUBLE;
+  A.nz = nullptr;
+  A.z = nullptr;
+  if (sizeof (octave_idx_type) == sizeof (SuiteSparse_long))
+    {
+      A.p = reinterpret_cast<SuiteSparse_long *> (a.cidx ());
+      A.i = reinterpret_cast<SuiteSparse_long *> (a.ridx ());
+    }
+  else
     {
-      cholmod_sparse A;
-
-      octave_idx_type ncols = a.cols ();
-      octave_idx_type nnz = a.nnz ();
-
-      A.ncol = ncols;
-      A.nrow = a.rows ();
-      A.itype = CHOLMOD_LONG;
-      A.nzmax = nnz;
-      A.sorted = 0;
-      A.packed = 1;
-      A.stype = 0;
-      A.xtype = CHOLMOD_REAL;
-      A.dtype = CHOLMOD_DOUBLE;
-      A.nz = nullptr;
-      A.z = nullptr;
-      if (sizeof (octave_idx_type) == sizeof (SuiteSparse_long))
-        {
-          A.p = reinterpret_cast<SuiteSparse_long *> (a.cidx ());
-          A.i = reinterpret_cast<SuiteSparse_long *> (a.ridx ());
-        }
-      else
-        {
-          SuiteSparse_long *A_p;
-          A_p = new SuiteSparse_long[ncols+1];
-          for (octave_idx_type i = 0; i < ncols+1; i++)
-            A_p[i] = a.cidx (i);
-          A.p = A_p;
-          SuiteSparse_long *A_i;
-          A_i = new SuiteSparse_long[nnz];
-          for (octave_idx_type i = 0; i < nnz; i++)
-            A_i[i] = a.ridx (i);
-          A.i = A_i;
-        }
-      A.x = const_cast<double *> (a.data ());
-
-      return A;
+      SuiteSparse_long *A_p;
+      A_p = new SuiteSparse_long[ncols+1];
+      for (octave_idx_type i = 0; i < ncols+1; i++)
+        A_p[i] = a.cidx (i);
+      A.p = A_p;
+      SuiteSparse_long *A_i;
+      A_i = new SuiteSparse_long[nnz];
+      for (octave_idx_type i = 0; i < nnz; i++)
+        A_i[i] = a.ridx (i);
+      A.i = A_i;
     }
-
-    // Convert complex sparse octave matrix to complex sparse cholmod matrix.
-    // Returns a "shallow" copy of a.
-    static cholmod_sparse
-    cos2ccs (const SparseComplexMatrix& a)
+  A.x = const_cast<double *> (a.data ());
+
+  return A;
+}
+
+// Convert complex sparse octave matrix to complex sparse cholmod matrix.
+// Returns a "shallow" copy of a.
+static cholmod_sparse
+cos2ccs (const SparseComplexMatrix& a)
+{
+  cholmod_sparse A;
+
+  octave_idx_type ncols = a.cols ();
+  octave_idx_type nnz = a.nnz ();
+
+  A.ncol = ncols;
+  A.nrow = a.rows ();
+  A.itype = CHOLMOD_LONG;
+  A.nzmax = nnz;
+  A.sorted = 0;
+  A.packed = 1;
+  A.stype = 0;
+  A.xtype = CHOLMOD_COMPLEX;
+  A.dtype = CHOLMOD_DOUBLE;
+  A.nz = nullptr;
+  A.z = nullptr;
+  if (sizeof (octave_idx_type) == sizeof (SuiteSparse_long))
+    {
+      A.p = reinterpret_cast<SuiteSparse_long *> (a.cidx ());
+      A.i = reinterpret_cast<SuiteSparse_long *> (a.ridx ());
+    }
+  else
     {
-      cholmod_sparse A;
-
-      octave_idx_type ncols = a.cols ();
-      octave_idx_type nnz = a.nnz ();
-
-      A.ncol = ncols;
-      A.nrow = a.rows ();
-      A.itype = CHOLMOD_LONG;
-      A.nzmax = nnz;
-      A.sorted = 0;
-      A.packed = 1;
-      A.stype = 0;
-      A.xtype = CHOLMOD_COMPLEX;
-      A.dtype = CHOLMOD_DOUBLE;
-      A.nz = nullptr;
-      A.z = nullptr;
-      if (sizeof (octave_idx_type) == sizeof (SuiteSparse_long))
-        {
-          A.p = reinterpret_cast<SuiteSparse_long *> (a.cidx ());
-          A.i = reinterpret_cast<SuiteSparse_long *> (a.ridx ());
-        }
-      else
-        {
-          SuiteSparse_long *A_p;
-          A_p = new SuiteSparse_long[ncols+1];
-          for (octave_idx_type i = 0; i < ncols+1; i++)
-            A_p[i] = a.cidx (i);
-          A.p = A_p;
-          SuiteSparse_long *A_i;
-          A_i = new SuiteSparse_long[nnz];
-          for (octave_idx_type i = 0; i < nnz; i++)
-            A_i[i] = a.ridx (i);
-          A.i = A_i;
-        }
-      A.x = const_cast<Complex *>
-                   (reinterpret_cast<const Complex *> (a.data ()));
-
-      return A;
+      SuiteSparse_long *A_p;
+      A_p = new SuiteSparse_long[ncols+1];
+      for (octave_idx_type i = 0; i < ncols+1; i++)
+        A_p[i] = a.cidx (i);
+      A.p = A_p;
+      SuiteSparse_long *A_i;
+      A_i = new SuiteSparse_long[nnz];
+      for (octave_idx_type i = 0; i < nnz; i++)
+        A_i[i] = a.ridx (i);
+      A.i = A_i;
     }
-
-    // Convert real dense octave matrix to complex dense cholmod matrix.
-    // Returns a "deep" copy of a.
-    static cholmod_dense *
-    rod2ccd (const MArray<double>& a, cholmod_common *cc1)
+  A.x = const_cast<Complex *>
+        (reinterpret_cast<const Complex *> (a.data ()));
+
+  return A;
+}
+
+// Convert real dense octave matrix to complex dense cholmod matrix.
+// Returns a "deep" copy of a.
+static cholmod_dense *
+rod2ccd (const MArray<double>& a, cholmod_common *cc1)
+{
+  cholmod_dense *A
+    = cholmod_l_allocate_dense (a.rows (), a.cols (), a.rows(),
+                                CHOLMOD_COMPLEX, cc1);
+
+  const double *a_x = a.data ();
+
+  Complex *A_x = reinterpret_cast<Complex *> (A->x);
+  for (octave_idx_type j = 0; j < a.cols() * a.rows() ; j++)
+    A_x[j] = Complex (a_x[j], 0.0);
+
+  return A;
+}
+
+// Convert real dense octave matrix to real dense cholmod matrix.
+// Returns a "shallow" copy of a.
+static cholmod_dense
+rod2rcd (const MArray<double>& a)
+{
+  cholmod_dense A;
+
+  A.ncol = a.cols ();
+  A.nrow = a.rows ();
+  A.nzmax = a.cols () * a.rows ();
+  A.xtype = CHOLMOD_REAL;
+  A.dtype = CHOLMOD_DOUBLE;
+  A.z = nullptr;
+  A.d = a.rows ();
+  A.x = const_cast<double *> (a.data ());
+
+  return A;
+}
+
+// Convert complex dense octave matrix to complex dense cholmod matrix.
+// Returns a "shallow" copy of a.
+static cholmod_dense
+cod2ccd (const ComplexMatrix& a)
+{
+  cholmod_dense A;
+
+  A.ncol = a.cols ();
+  A.nrow = a.rows ();
+  A.nzmax = a.cols () * a.rows ();
+  A.xtype = CHOLMOD_COMPLEX;
+  A.dtype = CHOLMOD_DOUBLE;
+  A.z = nullptr;
+  A.d = a.rows ();
+  A.x = const_cast<Complex *> (reinterpret_cast<const Complex *> (a.data ()));
+
+  return A;
+}
+
+// Convert real sparse cholmod matrix to real sparse octave matrix.
+// Returns a "shallow" copy of y.
+static SparseMatrix
+rcs2ros (const cholmod_sparse *y)
+{
+  octave_idx_type nrow = from_size_t (y->nrow);
+  octave_idx_type ncol = from_size_t (y->ncol);
+  octave_idx_type nz = from_size_t (y->nzmax);
+  SparseMatrix ret (nrow, ncol, nz);
+
+  SuiteSparse_long *y_p = reinterpret_cast<SuiteSparse_long *> (y->p);
+  for (octave_idx_type j = 0; j < ncol + 1; j++)
+    ret.xcidx (j) = from_suitesparse_long (y_p[j]);
+
+  SuiteSparse_long *y_i = reinterpret_cast<SuiteSparse_long *> (y->i);
+  double *y_x = reinterpret_cast<double *> (y->x);
+  for (octave_idx_type j = 0; j < nz; j++)
+    {
+      ret.xridx (j) = from_suitesparse_long (y_i[j]);
+      ret.xdata (j) = y_x[j];
+    }
+
+  return ret;
+}
+
+// Convert complex sparse cholmod matrix to complex sparse octave matrix.
+// Returns a "deep" copy of a.
+static SparseComplexMatrix
+ccs2cos (const cholmod_sparse *a)
+{
+  octave_idx_type nrow = from_size_t (a->nrow);
+  octave_idx_type ncol = from_size_t (a->ncol);
+  octave_idx_type nz = from_size_t (a->nzmax);
+  SparseComplexMatrix ret (nrow, ncol, nz);
+
+  SuiteSparse_long *a_p = reinterpret_cast<SuiteSparse_long *> (a->p);
+  for (octave_idx_type j = 0; j < ncol + 1; j++)
+    ret.xcidx(j) = from_suitesparse_long (a_p[j]);
+
+  SuiteSparse_long *a_i = reinterpret_cast<SuiteSparse_long *> (a->i);
+  Complex *a_x = reinterpret_cast<Complex *> (a->x);
+  for (octave_idx_type j = 0; j < nz; j++)
     {
-      cholmod_dense *A
-        = cholmod_l_allocate_dense (a.rows (), a.cols (), a.rows(),
-                                    CHOLMOD_COMPLEX, cc1);
-
-      const double *a_x = a.data ();
-
-      Complex *A_x = reinterpret_cast<Complex *> (A->x);
-      for (octave_idx_type j = 0; j < a.cols() * a.rows() ; j++)
-        A_x[j] = Complex (a_x[j], 0.0);
-
-      return A;
+      ret.xridx(j) = from_suitesparse_long (a_i[j]);
+      ret.xdata(j) = a_x[j];
     }
 
-    // Convert real dense octave matrix to real dense cholmod matrix.
-    // Returns a "shallow" copy of a.
-    static cholmod_dense
-    rod2rcd (const MArray<double>& a)
+  return ret;
+}
+
+// Convert real sparse octave matrix to complex sparse cholmod matrix.
+// Returns a "deep" copy of a.
+static cholmod_sparse *
+ros2ccs (const SparseMatrix& a, cholmod_common *cc)
+{
+  cholmod_sparse *A
+    = cholmod_l_allocate_sparse (a.rows (), a.cols (), a.nnz (), 0, 1, 0,
+                                 CHOLMOD_COMPLEX, cc);
+
+  octave_idx_type ncol = a.cols ();
+  SuiteSparse_long *A_p = reinterpret_cast<SuiteSparse_long *> (A->p);
+  for (octave_idx_type j = 0; j < ncol + 1; j++)
+    A_p[j] = a.cidx(j);
+
+  const double *a_x = a.data ();
+  Complex *A_x = reinterpret_cast<Complex *> (A->x);
+  SuiteSparse_long *A_i = reinterpret_cast<SuiteSparse_long *> (A->i);
+  for (octave_idx_type j = 0; j < a.nnz (); j++)
+    {
+      A_x[j] = Complex (a_x[j], 0.0);
+      A_i[j] = a.ridx(j);
+    }
+  return A;
+}
+
+static suitesparse_integer
+suitesparse_long_to_suitesparse_integer (SuiteSparse_long x)
+{
+  if (x < std::numeric_limits<suitesparse_integer>::min ()
+      || x > std::numeric_limits<suitesparse_integer>::max ())
+    (*current_liboctave_error_handler)
+      ("integer dimension or index out of range for SuiteSparse's indexing type");
+
+  return static_cast<suitesparse_integer> (x);
+}
+
+static void
+spqr_error_handler (const cholmod_common *cc)
+{
+  if (cc->status >= 0)
+    return;
+
+  switch (cc->status)
     {
-      cholmod_dense A;
-
-      A.ncol = a.cols ();
-      A.nrow = a.rows ();
-      A.nzmax = a.cols () * a.rows ();
-      A.xtype = CHOLMOD_REAL;
-      A.dtype = CHOLMOD_DOUBLE;
-      A.z = nullptr;
-      A.d = a.rows ();
-      A.x = const_cast<double *> (a.data ());
-
-      return A;
+    case CHOLMOD_OUT_OF_MEMORY:
+      (*current_liboctave_error_handler)
+        ("sparse_qr: sparse matrix QR factorization failed"
+         " - out of memory");
+    case CHOLMOD_TOO_LARGE:
+      (*current_liboctave_error_handler)
+        ("sparse_qr: sparse matrix QR factorization failed"
+         " - integer overflow occurred");
+    default:
+      (*current_liboctave_error_handler)
+        ("sparse_qr: sparse matrix QR factorization failed"
+         " - error %d", cc->status);
     }
 
-    // Convert complex dense octave matrix to complex dense cholmod matrix.
-    // Returns a "shallow" copy of a.
-    static cholmod_dense
-    cod2ccd (const ComplexMatrix& a)
-    {
-      cholmod_dense A;
-
-      A.ncol = a.cols ();
-      A.nrow = a.rows ();
-      A.nzmax = a.cols () * a.rows ();
-      A.xtype = CHOLMOD_COMPLEX;
-      A.dtype = CHOLMOD_DOUBLE;
-      A.z = nullptr;
-      A.d = a.rows ();
-      A.x = const_cast<Complex *> (reinterpret_cast<const Complex *> (a.data ()));
-
-      return A;
-    }
-
-    // Convert real sparse cholmod matrix to real sparse octave matrix.
-    // Returns a "shallow" copy of y.
-    static SparseMatrix
-    rcs2ros (const cholmod_sparse* y)
+  // FIXME: Free memory?
+  // FIXME: Can cc-status > 0 (CHOLMOD_NOT_POSDEF, CHOLMOD_DSMALL) occur?
+}
+#endif
+
+// Specializations.
+
+// Real-valued matrices.
+
+// Arguments for parameter order (taken from SuiteSparseQR documentation).
+// 0: fixed ordering 0 (no permutation of columns)
+// 1: natural ordering 1 (only singleton columns are permuted to the left of
+//    the matrix)
+// 2: colamd
+// 3:
+// 4: CHOLMOD best-effort (COLAMD, METIS,...)
+// 5: AMD(a'*a)
+// 6: metis(a'*a)
+// 7: SuiteSparseQR default ordering
+// 8: try COLAMD, AMD, and METIS; pick best
+// 9: try COLAMD and AMD; pick best
+//FIXME: What is order = 3?
+template <>
+sparse_qr<SparseMatrix>::sparse_qr_rep::sparse_qr_rep
+(const SparseMatrix& a, int order)
+  : nrows (a.rows ()), ncols (a.columns ())
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+  , m_cc (), m_R (nullptr), m_E (nullptr), m_H (nullptr), m_Htau (nullptr),
+    m_HPinv (nullptr)
+{
+  octave_idx_type nr = a.rows ();
+  octave_idx_type nc = a.cols ();
+
+  if (nr < 0 || nc < 0)
+    (*current_liboctave_error_handler)
+      ("matrix dimension with negative size");
+
+  if (order < 0 || order > 9)
+    (*current_liboctave_error_handler)
+      ("ordering %d is not supported by SPQR", order);
+
+  cholmod_l_start (&m_cc);
+  cholmod_sparse A = ros2rcs (a);
+
+  SuiteSparseQR<double> (order, static_cast<double> (SPQR_DEFAULT_TOL),
+                         static_cast<SuiteSparse_long> (A.nrow),
+                         &A, &m_R, &m_E, &m_H, &m_HPinv, &m_Htau, &m_cc);
+  spqr_error_handler (&m_cc);
+
+  if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
     {
-      octave_idx_type nrow = from_size_t (y->nrow);
-      octave_idx_type ncol = from_size_t (y->ncol);
-      octave_idx_type nz = from_size_t (y->nzmax);
-      SparseMatrix ret (nrow, ncol, nz);
-
-      SuiteSparse_long *y_p = reinterpret_cast<SuiteSparse_long *> (y->p);
-      for (octave_idx_type j = 0; j < ncol + 1; j++)
-        ret.xcidx (j) = from_suitesparse_long (y_p[j]);
-
-      SuiteSparse_long *y_i = reinterpret_cast<SuiteSparse_long *> (y->i);
-      double *y_x = reinterpret_cast<double *> (y->x);
-      for (octave_idx_type j = 0; j < nz; j++)
-        {
-          ret.xridx (j) = from_suitesparse_long (y_i[j]);
-          ret.xdata (j) = y_x[j];
-        }
-
-      return ret;
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
     }
-
-    // Convert complex sparse cholmod matrix to complex sparse octave matrix.
-    // Returns a "deep" copy of a.
-    static SparseComplexMatrix
-    ccs2cos (const cholmod_sparse *a)
+}
+
+#elif defined (HAVE_CXSPARSE)
+  , S (nullptr), N (nullptr)
+{
+  CXSPARSE_DNAME () A;
+
+  A.nzmax = a.nnz ();
+  A.m = nrows;
+  A.n = ncols;
+  // Cast away const on A, with full knowledge that CSparse won't touch it
+  // Prevents the methods below making a copy of the data.
+  A.p = const_cast<suitesparse_integer *>
+        (to_suitesparse_intptr (a.cidx ()));
+  A.i = const_cast<suitesparse_integer *>
+        (to_suitesparse_intptr (a.ridx ()));
+  A.x = const_cast<double *> (a.data ());
+  A.nz = -1;
+
+  S = CXSPARSE_DNAME (_sqr) (order, &A, 1);
+  N = CXSPARSE_DNAME (_qr) (&A, S);
+
+  if (! N)
+    (*current_liboctave_error_handler)
+      ("sparse_qr: sparse matrix QR factorization filled");
+
+}
+
+#else
+
+{
+  octave_unused_parameter (order);
+
+  (*current_liboctave_error_handler)
+    ("sparse_qr: support for SPQR or CXSparse was unavailable or disabled when liboctave was built");
+}
+
+#endif
+
+template <>
+sparse_qr<SparseMatrix>::sparse_qr_rep::~sparse_qr_rep (void)
+{
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+
+  cholmod_l_free_sparse (&m_R, &m_cc);
+  cholmod_l_free_sparse (&m_H, &m_cc);
+  cholmod_l_free_dense (&m_Htau, &m_cc);
+  free (m_E);  // FIXME: use cholmod_l_free
+  free (m_HPinv);
+  cholmod_l_finish (&m_cc);
+
+#elif defined (HAVE_CXSPARSE)
+
+  CXSPARSE_DNAME (_sfree) (S);
+  CXSPARSE_DNAME (_nfree) (N);
+
+#endif
+}
+
+template <>
+SparseMatrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::V (void) const
+{
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+
+  return rcs2ros (m_H);
+
+#elif defined (HAVE_CXSPARSE)
+
+  // Drop zeros from V and sort
+  // FIXME: Is the double transpose to sort necessary?
+
+  CXSPARSE_DNAME (_dropzeros) (N->L);
+  CXSPARSE_DNAME () *D = CXSPARSE_DNAME (_transpose) (N->L, 1);
+  CXSPARSE_DNAME (_spfree) (N->L);
+  N->L = CXSPARSE_DNAME (_transpose) (D, 1);
+  CXSPARSE_DNAME (_spfree) (D);
+
+  octave_idx_type nc = N->L->n;
+  octave_idx_type nz = N->L->nzmax;
+  SparseMatrix ret (N->L->m, nc, nz);
+
+  for (octave_idx_type j = 0; j < nc+1; j++)
+    ret.xcidx (j) = N->L->p[j];
+
+  for (octave_idx_type j = 0; j < nz; j++)
     {
-      octave_idx_type nrow = from_size_t (a->nrow);
-      octave_idx_type ncol = from_size_t (a->ncol);
-      octave_idx_type nz = from_size_t (a->nzmax);
-      SparseComplexMatrix ret (nrow, ncol, nz);
-
-      SuiteSparse_long *a_p = reinterpret_cast<SuiteSparse_long *> (a->p);
-      for (octave_idx_type j = 0; j < ncol + 1; j++)
-        ret.xcidx(j) = from_suitesparse_long (a_p[j]);
-
-      SuiteSparse_long *a_i = reinterpret_cast<SuiteSparse_long *> (a->i);
-      Complex *a_x = reinterpret_cast<Complex *> (a->x);
-      for (octave_idx_type j = 0; j < nz; j++)
-        {
-          ret.xridx(j) = from_suitesparse_long (a_i[j]);
-          ret.xdata(j) = a_x[j];
-        }
-
-      return ret;
+      ret.xridx (j) = N->L->i[j];
+      ret.xdata (j) = N->L->x[j];
     }
 
-    // Convert real sparse octave matrix to complex sparse cholmod matrix.
-    // Returns a "deep" copy of a.
-    static cholmod_sparse *
-    ros2ccs (const SparseMatrix& a, cholmod_common *cc)
+  return ret;
+
+#else
+
+  return SparseMatrix ();
+
+#endif
+}
+
+template <>
+SparseMatrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::R (bool econ) const
+{
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+
+  octave_idx_type nr = static_cast<octave_idx_type> (m_R->nrow);
+  octave_idx_type nc = static_cast<octave_idx_type> (m_R->ncol);
+  octave_idx_type nz = static_cast<octave_idx_type> (m_R->nzmax);
+
+  // FIXME: Does this work if econ = true?
+  SparseMatrix ret ((econ ? (nc > nr ? nr : nc) : nr), nc, nz);
+  SuiteSparse_long *Rp = reinterpret_cast<SuiteSparse_long *> (m_R->p);
+  SuiteSparse_long *Ri = reinterpret_cast<SuiteSparse_long *> (m_R->i);
+
+  for (octave_idx_type j = 0; j < nc + 1; j++)
+    ret.xcidx (j) = from_suitesparse_long (Rp[j]);
+
+  for (octave_idx_type j = 0; j < nz; j++)
     {
-      cholmod_sparse *A
-        = cholmod_l_allocate_sparse (a.rows (), a.cols (), a.nnz (), 0, 1, 0,
-                                     CHOLMOD_COMPLEX, cc);
-
-      octave_idx_type ncol = a.cols ();
-      SuiteSparse_long *A_p = reinterpret_cast<SuiteSparse_long *> (A->p);
-      for (octave_idx_type j = 0; j < ncol + 1; j++)
-        A_p[j] = a.cidx(j);
-
-      const double *a_x = a.data ();
-      Complex *A_x = reinterpret_cast<Complex *> (A->x);
-      SuiteSparse_long *A_i = reinterpret_cast<SuiteSparse_long *> (A->i);
-      for (octave_idx_type j = 0; j < a.nnz (); j++)
-        {
-          A_x[j] = Complex (a_x[j], 0.0);
-          A_i[j] = a.ridx(j);
-        }
-      return A;
+      ret.xridx (j) = from_suitesparse_long (Ri[j]);
+      ret.xdata (j) = (reinterpret_cast<double *> (m_R->x))[j];
     }
 
-    static suitesparse_integer
-    suitesparse_long_to_suitesparse_integer (SuiteSparse_long x)
+  return ret;
+
+#elif defined (HAVE_CXSPARSE)
+
+  // Drop zeros from R and sort
+  // FIXME: Is the double transpose to sort necessary?
+
+  CXSPARSE_DNAME (_dropzeros) (N->U);
+  CXSPARSE_DNAME () *D = CXSPARSE_DNAME (_transpose) (N->U, 1);
+  CXSPARSE_DNAME (_spfree) (N->U);
+  N->U = CXSPARSE_DNAME (_transpose) (D, 1);
+  CXSPARSE_DNAME (_spfree) (D);
+
+  octave_idx_type nc = N->U->n;
+  octave_idx_type nz = N->U->nzmax;
+
+  SparseMatrix ret ((econ ? (nc > nrows ? nrows : nc) : nrows), nc, nz);
+
+  for (octave_idx_type j = 0; j < nc+1; j++)
+    ret.xcidx (j) = N->U->p[j];
+
+  for (octave_idx_type j = 0; j < nz; j++)
     {
-      if (x < std::numeric_limits<suitesparse_integer>::min ()
-          || x > std::numeric_limits<suitesparse_integer>::max ())
-        (*current_liboctave_error_handler)
-          ("integer dimension or index out of range for SuiteSparse's indexing type");
-
-      return static_cast<suitesparse_integer> (x);
+      ret.xridx (j) = N->U->i[j];
+      ret.xdata (j) = N->U->x[j];
     }
 
-    static void
-    spqr_error_handler (const cholmod_common *cc)
-    {
-      if (cc->status >= 0)
-        return;
-
-      switch (cc->status)
-        {
-        case CHOLMOD_OUT_OF_MEMORY:
-          (*current_liboctave_error_handler)
-            ("sparse_qr: sparse matrix QR factorization failed"
-             " - out of memory");
-        case CHOLMOD_TOO_LARGE:
-          (*current_liboctave_error_handler)
-            ("sparse_qr: sparse matrix QR factorization failed"
-             " - integer overflow occurred");
-        default:
-          (*current_liboctave_error_handler)
-            ("sparse_qr: sparse matrix QR factorization failed"
-             " - error %d", cc->status);
-        }
-
-      // FIXME: Free memory?
-      // FIXME: Can cc-status > 0 (CHOLMOD_NOT_POSDEF, CHOLMOD_DSMALL) occur?
-    }
+  return ret;
+
+#else
+
+  octave_unused_parameter (econ);
+
+  return SparseMatrix ();
+
 #endif
-
-    // Specializations.
-
-    // Real-valued matrices.
-
-    // Arguments for parameter order (taken from SuiteSparseQR documentation).
-    // 0: fixed ordering 0 (no permutation of columns)
-    // 1: natural ordering 1 (only singleton columns are permuted to the left of
-    //    the matrix)
-    // 2: colamd
-    // 3:
-    // 4: CHOLMOD best-effort (COLAMD, METIS,...)
-    // 5: AMD(a'*a)
-    // 6: metis(a'*a)
-    // 7: SuiteSparseQR default ordering
-    // 8: try COLAMD, AMD, and METIS; pick best
-    // 9: try COLAMD and AMD; pick best
-    //FIXME: What is order = 3?
-    template <>
-    sparse_qr<SparseMatrix>::sparse_qr_rep::sparse_qr_rep
-    (const SparseMatrix& a, int order)
-      : nrows (a.rows ()), ncols (a.columns ())
+}
+
+template <>
+Matrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::C (const Matrix& b, bool econ)
+{
 #if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-      , m_cc (), m_R (nullptr), m_E (nullptr), m_H (nullptr), m_Htau (nullptr),
-        m_HPinv (nullptr)
+  octave_idx_type nr = (econ
+                        ? (ncols > nrows ? nrows : ncols)
+                        : nrows);
+  octave_idx_type b_nr = b.rows ();
+  octave_idx_type b_nc = b.cols ();
+  Matrix ret (nr, b_nc);
+
+  if (nrows != b_nr)
+    (*current_liboctave_error_handler)
+      ("sparse_qr: matrix dimension mismatch");
+  else if (b_nc < 0 || b_nr < 0)
+    (*current_liboctave_error_handler)
+      ("sparse_qr: matrix dimension with negative size");
+
+  cholmod_dense *QTB;  // Q' * B
+  cholmod_dense B = rod2rcd (b);
+
+  QTB = SuiteSparseQR_qmult<double> (SPQR_QTX, m_H, m_Htau, m_HPinv, &B,
+                                     &m_cc);
+  spqr_error_handler (&m_cc);
+
+  // copy QTB into ret
+  double *QTB_x = reinterpret_cast<double *> (QTB->x);
+  double *ret_vec = reinterpret_cast<double *> (ret.fortran_vec ());
+  for (octave_idx_type j = 0; j < b_nc; j++)
+    for (octave_idx_type i = 0; i < nr; i++)
+      ret_vec[j * nr + i] = QTB_x[j * b_nr + i];
+
+  cholmod_l_free_dense (&QTB, &m_cc);
+
+  return ret;
+
+#elif defined (HAVE_CXSPARSE)
+
+  if (econ)
+    (*current_liboctave_error_handler)
+      ("sparse-qr: economy mode with CXSparse not supported");
+
+  octave_idx_type b_nr = b.rows ();
+  octave_idx_type b_nc = b.cols ();
+
+  octave_idx_type nc = N->L->n;
+  octave_idx_type nr = nrows;
+
+  const double *bvec = b.data ();
+
+  Matrix ret (b_nr, b_nc);
+  double *vec = ret.fortran_vec ();
+
+  if (nr < 0 || nc < 0 || nr != b_nr)
+    (*current_liboctave_error_handler) ("matrix dimension mismatch");
+
+  if (nr == 0 || nc == 0 || b_nc == 0)
+    ret = Matrix (nc, b_nc, 0.0);
+  else
     {
-      octave_idx_type nr = a.rows ();
-      octave_idx_type nc = a.cols ();
-
-      if (nr < 0 || nc < 0)
-        (*current_liboctave_error_handler)
-          ("matrix dimension with negative size");
-
-      if (order < 0 || order > 9)
-        (*current_liboctave_error_handler)
-          ("ordering %d is not supported by SPQR", order);
-
-      cholmod_l_start (&m_cc);
-      cholmod_sparse A = ros2rcs (a);
-
-      SuiteSparseQR<double> (order, static_cast<double> (SPQR_DEFAULT_TOL),
-                             static_cast<SuiteSparse_long> (A.nrow),
-                             &A, &m_R, &m_E, &m_H, &m_HPinv, &m_Htau, &m_cc);
-      spqr_error_handler (&m_cc);
-
-      if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
+      OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
+
+      for (volatile octave_idx_type j = 0, idx = 0;
+           j < b_nc;
+           j++, idx += b_nr)
         {
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
+          octave_quit ();
+
+          for (octave_idx_type i = nr; i < S->m2; i++)
+            buf[i] = 0.;
+
+          volatile octave_idx_type nm = (nr < nc ? nr : nc);
+
+          CXSPARSE_DNAME (_ipvec) (S->pinv, bvec + idx, buf, b_nr);
+
+          for (volatile octave_idx_type i = 0; i < nm; i++)
+            {
+              octave_quit ();
+
+              CXSPARSE_DNAME (_happly) (N->L, i, N->B[i], buf);
+            }
+
+          for (octave_idx_type i = 0; i < b_nr; i++)
+            vec[i+idx] = buf[i];
+        }
+    }
+
+  return ret;
+
+#else
+
+  octave_unused_parameter (b);
+  octave_unused_parameter (econ);
+
+  return Matrix ();
+
+#endif
+}
+
+template <>
+Matrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::Q (bool econ)
+{
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+
+  octave_idx_type nc = (econ
+                        ? (ncols > nrows ? nrows : ncols)
+                        : nrows);
+  Matrix ret (nrows, nc);
+  cholmod_dense *q;
+
+  // I is nrows x nrows identity matrix
+  cholmod_dense *I
+    = cholmod_l_allocate_dense (nrows, nrows, nrows, CHOLMOD_REAL, &m_cc);
+
+  for (octave_idx_type i = 0; i < nrows * nrows; i++)
+    (reinterpret_cast<double *> (I->x))[i] = 0.0;
+
+  for (octave_idx_type i = 0; i < nrows; i++)
+    (reinterpret_cast<double *> (I->x))[i * nrows + i] = 1.0;
+
+  q = SuiteSparseQR_qmult<double> (SPQR_QX, m_H, m_Htau, m_HPinv, I, &m_cc);
+  spqr_error_handler (&m_cc);
+
+  double *q_x = reinterpret_cast<double *> (q->x);
+  double *ret_vec = const_cast<double *> (ret.fortran_vec ());
+  for (octave_idx_type j = 0; j < nc; j++)
+    for (octave_idx_type i = 0; i < nrows; i++)
+      ret_vec[j * nrows + i] = q_x[j * nrows + i];
+
+  cholmod_l_free_dense (&q, &m_cc);
+  cholmod_l_free_dense (&I, &m_cc);
+
+  return ret;
+
+#elif defined (HAVE_CXSPARSE)
+
+  if (econ)
+    (*current_liboctave_error_handler)
+      ("sparse-qr: economy mode with CXSparse not supported");
+
+  octave_idx_type nc = N->L->n;
+  octave_idx_type nr = nrows;
+  Matrix ret (nr, nr);
+  double *ret_vec = ret.fortran_vec ();
+
+  if (nr < 0 || nc < 0)
+    (*current_liboctave_error_handler) ("matrix dimension mismatch");
+
+  if (nr == 0 || nc == 0)
+    ret = Matrix (nc, nr, 0.0);
+  else
+    {
+      OCTAVE_LOCAL_BUFFER (double, bvec, nr + 1);
+
+      for (octave_idx_type i = 0; i < nr; i++)
+        bvec[i] = 0.;
+
+      OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
+
+      for (volatile octave_idx_type j = 0, idx = 0; j < nr; j++, idx += nr)
+        {
+          octave_quit ();
+
+          bvec[j] = 1.0;
+          for (octave_idx_type i = nr; i < S->m2; i++)
+            buf[i] = 0.;
+
+          volatile octave_idx_type nm = (nr < nc ? nr : nc);
+
+          CXSPARSE_DNAME (_ipvec) (S->pinv, bvec, buf, nr);
+
+          for (volatile octave_idx_type i = 0; i < nm; i++)
+            {
+              octave_quit ();
+
+              CXSPARSE_DNAME (_happly) (N->L, i, N->B[i], buf);
+            }
+
+          for (octave_idx_type i = 0; i < nr; i++)
+            ret_vec[i+idx] = buf[i];
+
+          bvec[j] = 0.0;
         }
     }
 
-#elif defined (HAVE_CXSPARSE)
-      , S (nullptr), N (nullptr)
-    {
-      CXSPARSE_DNAME () A;
-
-      A.nzmax = a.nnz ();
-      A.m = nrows;
-      A.n = ncols;
-      // Cast away const on A, with full knowledge that CSparse won't touch it
-      // Prevents the methods below making a copy of the data.
-      A.p = const_cast<suitesparse_integer *>
-              (to_suitesparse_intptr (a.cidx ()));
-      A.i = const_cast<suitesparse_integer *>
-              (to_suitesparse_intptr (a.ridx ()));
-      A.x = const_cast<double *> (a.data ());
-      A.nz = -1;
-
-      S = CXSPARSE_DNAME (_sqr) (order, &A, 1);
-      N = CXSPARSE_DNAME (_qr) (&A, S);
-
-      if (! N)
-        (*current_liboctave_error_handler)
-          ("sparse_qr: sparse matrix QR factorization filled");
-
-    }
+  return ret.transpose ();
 
 #else
 
-    {
-      octave_unused_parameter (order);
-
-      (*current_liboctave_error_handler)
-        ("sparse_qr: support for SPQR or CXSparse was unavailable or disabled when liboctave was built");
-    }
-
-#endif
-
-    template <>
-    sparse_qr<SparseMatrix>::sparse_qr_rep::~sparse_qr_rep (void)
-    {
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-
-      cholmod_l_free_sparse (&m_R, &m_cc);
-      cholmod_l_free_sparse (&m_H, &m_cc);
-      cholmod_l_free_dense (&m_Htau, &m_cc);
-      free (m_E);  // FIXME: use cholmod_l_free
-      free (m_HPinv);
-      cholmod_l_finish (&m_cc);
-
-#elif defined (HAVE_CXSPARSE)
-
-      CXSPARSE_DNAME (_sfree) (S);
-      CXSPARSE_DNAME (_nfree) (N);
+  octave_unused_parameter (econ);
+
+  return Matrix ();
 
 #endif
-    }
-
-    template <>
-    SparseMatrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::V (void) const
+}
+
+template <>
+template <>
+Matrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::tall_solve<MArray<double>, Matrix>
+(const MArray<double>& b, octave_idx_type& info)
+{
+  info = -1;
+
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD) && defined (HAVE_CXSPARSE))
+
+  octave_idx_type b_nr = b.rows ();
+  octave_idx_type b_nc = b.cols ();
+  Matrix x (ncols, b_nc);  // X = m_E'*(m_R\(Q'*B))
+
+  if (nrows < 0 || ncols < 0 || b_nc < 0 || b_nr < 0)
+    (*current_liboctave_error_handler)
+      ("matrix dimension with negative size");
+
+  if (nrows < 0 || ncols < 0 || nrows != b_nr)
+    (*current_liboctave_error_handler) ("matrix dimension mismatch");
+
+  cholmod_dense *QTB;  // Q' * B
+  cholmod_dense B = rod2rcd (b);
+
+  // FIXME: Process b column by column as in the CXSPARSE version below.
+  // This avoids a large dense matrix Q' * B in memory.
+  QTB = SuiteSparseQR_qmult<double> (SPQR_QTX, m_H, m_Htau, m_HPinv, &B,
+                                     &m_cc);
+
+  spqr_error_handler (&m_cc);
+
+  // convert m_R into CXSPARSE matrix R2
+  CXSPARSE_DNAME (_sparse) R2;
+  R2.n = ncols;
+  R2.m = ncols;
+  R2.nzmax = m_R->nzmax;
+  R2.x = reinterpret_cast<double *> (m_R->x);
+  suitesparse_integer *R2_p;
+  suitesparse_integer *R2_i;
+  if (sizeof (suitesparse_integer) == sizeof (SuiteSparse_long))
     {
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-
-      return rcs2ros (m_H);
-
-#elif defined (HAVE_CXSPARSE)
-
-      // Drop zeros from V and sort
-      // FIXME: Is the double transpose to sort necessary?
-
-      CXSPARSE_DNAME (_dropzeros) (N->L);
-      CXSPARSE_DNAME () *D = CXSPARSE_DNAME (_transpose) (N->L, 1);
-      CXSPARSE_DNAME (_spfree) (N->L);
-      N->L = CXSPARSE_DNAME (_transpose) (D, 1);
-      CXSPARSE_DNAME (_spfree) (D);
-
-      octave_idx_type nc = N->L->n;
-      octave_idx_type nz = N->L->nzmax;
-      SparseMatrix ret (N->L->m, nc, nz);
-
-      for (octave_idx_type j = 0; j < nc+1; j++)
-        ret.xcidx (j) = N->L->p[j];
-
-      for (octave_idx_type j = 0; j < nz; j++)
-        {
-          ret.xridx (j) = N->L->i[j];
-          ret.xdata (j) = N->L->x[j];
-        }
-
-      return ret;
-
-#else
-
-      return SparseMatrix ();
-
-#endif
+      R2.p = reinterpret_cast<suitesparse_integer *> (m_R->p);
+      R2.i = reinterpret_cast<suitesparse_integer *> (m_R->i);
+    }
+  else
+    {
+      R2_p = new suitesparse_integer[ncols+1];
+      SuiteSparse_long *R_p = reinterpret_cast<SuiteSparse_long *> (m_R->p);
+      for (octave_idx_type i = 0; i < ncols+1; i++)
+        R2_p[i] = suitesparse_long_to_suitesparse_integer (R_p[i]);
+      R2.p = R2_p;
+      octave_idx_type nnz = m_R->nzmax;
+      R2_i = new suitesparse_integer[nnz];
+      SuiteSparse_long *R_i = reinterpret_cast<SuiteSparse_long *> (m_R->i);
+      for (octave_idx_type i = 0; i < nnz; i++)
+        R2_i[i] =  suitesparse_long_to_suitesparse_integer (R_i[i]);
+      R2.i = R2_i;
+    }
+  R2.nz = -1;
+  double *x_vec = const_cast<double *> (x.fortran_vec ());
+  suitesparse_integer *E;
+  if (sizeof (suitesparse_integer) != sizeof (SuiteSparse_long))
+    {
+      E = new suitesparse_integer [ncols];
+      for (octave_idx_type i = 0; i < ncols; i++)
+        E[i] = suitesparse_long_to_suitesparse_integer (m_E[i]);
     }
-
-    template <>
-    SparseMatrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::R (bool econ) const
+  else
+    E = reinterpret_cast<suitesparse_integer *> (m_E);
+  for (volatile octave_idx_type j = 0; j < b_nc; j++)
     {
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-
-      octave_idx_type nr = static_cast<octave_idx_type> (m_R->nrow);
-      octave_idx_type nc = static_cast<octave_idx_type> (m_R->ncol);
-      octave_idx_type nz = static_cast<octave_idx_type> (m_R->nzmax);
-
-      // FIXME: Does this work if econ = true?
-      SparseMatrix ret ((econ ? (nc > nr ? nr : nc) : nr), nc, nz);
-      SuiteSparse_long *Rp = reinterpret_cast<SuiteSparse_long *> (m_R->p);
-      SuiteSparse_long *Ri = reinterpret_cast<SuiteSparse_long *> (m_R->i);
-
-      for (octave_idx_type j = 0; j < nc + 1; j++)
-        ret.xcidx (j) = from_suitesparse_long (Rp[j]);
-
-      for (octave_idx_type j = 0; j < nz; j++)
-        {
-          ret.xridx (j) = from_suitesparse_long (Ri[j]);
-          ret.xdata (j) = (reinterpret_cast<double *> (m_R->x))[j];
-        }
-
-      return ret;
+      // fill x(:,j)
+      // solve (m_R\(Q'*B(:,j)) and store result in QTB(:,j)
+      CXSPARSE_DNAME (_usolve)
+      (&R2, &(reinterpret_cast<double *> (QTB->x)[j * b_nr]));
+      // x(:,j) = m_E' * (m_R\(Q'*B(:,j))
+      CXSPARSE_DNAME (_ipvec)
+      (E, &(reinterpret_cast<double *> (QTB->x)[j * b_nr]),
+       &x_vec[j * ncols], ncols);
+    }
+
+  if (sizeof (suitesparse_integer) != sizeof (SuiteSparse_long))
+    {
+      delete [] R2_p;
+      delete [] R2_i;
+      delete [] E;
+    }
+  cholmod_l_free_dense (&QTB, &m_cc);
+
+  info = 0;
+
+  return x;
 
 #elif defined (HAVE_CXSPARSE)
 
-      // Drop zeros from R and sort
-      // FIXME: Is the double transpose to sort necessary?
-
-      CXSPARSE_DNAME (_dropzeros) (N->U);
-      CXSPARSE_DNAME () *D = CXSPARSE_DNAME (_transpose) (N->U, 1);
-      CXSPARSE_DNAME (_spfree) (N->U);
-      N->U = CXSPARSE_DNAME (_transpose) (D, 1);
-      CXSPARSE_DNAME (_spfree) (D);
-
-      octave_idx_type nc = N->U->n;
-      octave_idx_type nz = N->U->nzmax;
-
-      SparseMatrix ret ((econ ? (nc > nrows ? nrows : nc) : nrows), nc, nz);
-
-      for (octave_idx_type j = 0; j < nc+1; j++)
-        ret.xcidx (j) = N->U->p[j];
-
-      for (octave_idx_type j = 0; j < nz; j++)
+  octave_idx_type nr = nrows;
+  octave_idx_type nc = ncols;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  const double *bvec = b.data ();
+
+  Matrix x (nc, b_nc);
+  double *vec = x.fortran_vec ();
+
+  OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
+
+  for (volatile octave_idx_type i = 0, idx = 0, bidx = 0; i < b_nc;
+       i++, idx+=nc, bidx+=b_nr)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = nr; j < S->m2; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_ipvec) (S->pinv, bvec + bidx, buf, nr);
+
+      for (volatile octave_idx_type j = 0; j < nc; j++)
         {
-          ret.xridx (j) = N->U->i[j];
-          ret.xdata (j) = N->U->x[j];
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
         }
 
-      return ret;
+      CXSPARSE_DNAME (_usolve) (N->U, buf);
+      CXSPARSE_DNAME (_ipvec) (S->q, buf, vec + idx, nc);
+    }
+
+  info = 0;
+
+  return x;
+
+#else
+
+  octave_unused_parameter (b);
+
+  return Matrix ();
+
+#endif
+}
+
+template <>
+template <>
+Matrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::wide_solve<MArray<double>, Matrix>
+(const MArray<double>& b, octave_idx_type& info) const
+{
+  info = -1;
+#if defined (HAVE_CXSPARSE)
+
+  // These are swapped because the original matrix was transposed in
+  // sparse_qr<SparseMatrix>::solve.
+
+  octave_idx_type nr = ncols;
+  octave_idx_type nc = nrows;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  const double *bvec = b.data ();
+
+  Matrix x (nc, b_nc);
+  double *vec = x.fortran_vec ();
+
+  volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
+
+  OCTAVE_LOCAL_BUFFER (double, buf, nbuf);
+
+  for (volatile octave_idx_type i = 0, idx = 0, bidx = 0; i < b_nc;
+       i++, idx+=nc, bidx+=b_nr)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = nr; j < nbuf; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_pvec) (S->q, bvec + bidx, buf, nr);
+      CXSPARSE_DNAME (_utsolve) (N->U, buf);
+
+      for (volatile octave_idx_type j = nr-1; j >= 0; j--)
+        {
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_DNAME (_pvec) (S->pinv, buf, vec + idx, nc);
+    }
+
+  info = 0;
+
+  return x;
 
 #else
-
-      octave_unused_parameter (econ);
-
-      return SparseMatrix ();
+  octave_unused_parameter (b);
+
+  return Matrix ();
+
+#endif
+}
+
+template <>
+template <>
+SparseMatrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::tall_solve<SparseMatrix, SparseMatrix>
+(const SparseMatrix& b, octave_idx_type& info)
+{
+  info = -1;
+
+#if defined (HAVE_CXSPARSE)
+
+  octave_idx_type nr = nrows;
+  octave_idx_type nc = ncols;
+
+  octave_idx_type b_nr = b.rows ();
+  octave_idx_type b_nc = b.cols ();
+
+  SparseMatrix x (nc, b_nc, b.nnz ());
+  x.xcidx (0) = 0;
+
+  volatile octave_idx_type x_nz = b.nnz ();
+  volatile octave_idx_type ii = 0;
+
+  OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        Xx[j] = b.xelem (j, i);
+
+      for (octave_idx_type j = nr; j < S->m2; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_ipvec) (S->pinv, Xx, buf, nr);
+
+      for (volatile octave_idx_type j = 0; j < nc; j++)
+        {
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_DNAME (_usolve) (N->U, buf);
+      CXSPARSE_DNAME (_ipvec) (S->q, buf, Xx, nc);
+
+      for (octave_idx_type j = 0; j < nc; j++)
+        {
+          double tmp = Xx[j];
+
+          if (tmp != 0.0)
+            {
+              if (ii == x_nz)
+                {
+                  // Resize the sparse matrix
+                  octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
+                  sz = (sz > 10 ? sz : 10) + x_nz;
+                  x.change_capacity (sz);
+                  x_nz = sz;
+                }
+
+              x.xdata (ii) = tmp;
+              x.xridx (ii++) = j;
+            }
+        }
+
+      x.xcidx (i+1) = ii;
+    }
+
+  info = 0;
+
+  return x;
+
+#else
+
+  octave_unused_parameter (b);
+
+  return SparseMatrix ();
+
+#endif
+}
+
+template <>
+template <>
+SparseMatrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::wide_solve<SparseMatrix, SparseMatrix>
+(const SparseMatrix& b, octave_idx_type& info) const
+{
+  info = -1;
+
+#if defined (HAVE_CXSPARSE)
+
+  // These are swapped because the original matrix was transposed in
+  // sparse_qr<SparseMatrix>::solve.
+
+  octave_idx_type nr = ncols;
+  octave_idx_type nc = nrows;
+
+  octave_idx_type b_nr = b.rows ();
+  octave_idx_type b_nc = b.cols ();
+
+  SparseMatrix x (nc, b_nc, b.nnz ());
+  x.xcidx (0) = 0;
+
+  volatile octave_idx_type x_nz = b.nnz ();
+  volatile octave_idx_type ii = 0;
+  volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
+
+  OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (double, buf, nbuf);
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        Xx[j] = b.xelem (j, i);
+
+      for (octave_idx_type j = nr; j < nbuf; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_pvec) (S->q, Xx, buf, nr);
+      CXSPARSE_DNAME (_utsolve) (N->U, buf);
+
+      for (volatile octave_idx_type j = nr-1; j >= 0; j--)
+        {
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_DNAME (_pvec) (S->pinv, buf, Xx, nc);
+
+      for (octave_idx_type j = 0; j < nc; j++)
+        {
+          double tmp = Xx[j];
+
+          if (tmp != 0.0)
+            {
+              if (ii == x_nz)
+                {
+                  // Resize the sparse matrix
+                  octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
+                  sz = (sz > 10 ? sz : 10) + x_nz;
+                  x.change_capacity (sz);
+                  x_nz = sz;
+                }
+
+              x.xdata (ii) = tmp;
+              x.xridx (ii++) = j;
+            }
+        }
+
+      x.xcidx (i+1) = ii;
+    }
+
+  info = 0;
+
+  x.maybe_compress ();
+
+  return x;
+
+#else
+
+  octave_unused_parameter (b);
+
+  return SparseMatrix ();
 
 #endif
+}
+
+template <>
+template <>
+ComplexMatrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::tall_solve<MArray<Complex>, ComplexMatrix>
+(const MArray<Complex>& b, octave_idx_type& info)
+{
+  info = -1;
+
+#if defined (HAVE_CXSPARSE)
+
+  octave_idx_type nr = nrows;
+  octave_idx_type nc = ncols;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  ComplexMatrix x (nc, b_nc);
+  Complex *vec = x.fortran_vec ();
+
+  OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (double, Xz, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        {
+          Complex c = b.xelem (j, i);
+          Xx[j] = c.real ();
+          Xz[j] = c.imag ();
+        }
+
+      for (octave_idx_type j = nr; j < S->m2; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_ipvec) (S->pinv, Xx, buf, nr);
+
+      for (volatile octave_idx_type j = 0; j < nc; j++)
+        {
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_DNAME (_usolve) (N->U, buf);
+      CXSPARSE_DNAME (_ipvec) (S->q, buf, Xx, nc);
+
+      for (octave_idx_type j = nr; j < S->m2; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_ipvec) (S->pinv, Xz, buf, nr);
+
+      for (volatile octave_idx_type j = 0; j < nc; j++)
+        {
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_DNAME (_usolve) (N->U, buf);
+      CXSPARSE_DNAME (_ipvec) (S->q, buf, Xz, nc);
+
+      for (octave_idx_type j = 0; j < nc; j++)
+        vec[j+idx] = Complex (Xx[j], Xz[j]);
     }
 
-    template <>
-    Matrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::C (const Matrix& b, bool econ)
+  info = 0;
+
+  return x;
+
+#else
+
+  octave_unused_parameter (b);
+
+  return ComplexMatrix ();
+
+#endif
+}
+
+template <>
+template <>
+ComplexMatrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::wide_solve<MArray<Complex>, ComplexMatrix>
+(const MArray<Complex>& b, octave_idx_type& info) const
+{
+  info = -1;
+
+#if defined (HAVE_CXSPARSE)
+
+  // These are swapped because the original matrix was transposed in
+  // sparse_qr<SparseMatrix>::solve.
+
+  octave_idx_type nr = ncols;
+  octave_idx_type nc = nrows;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  ComplexMatrix x (nc, b_nc);
+  Complex *vec = x.fortran_vec ();
+
+  volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
+
+  OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (double, Xz, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (double, buf, nbuf);
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
     {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        {
+          Complex c = b.xelem (j, i);
+          Xx[j] = c.real ();
+          Xz[j] = c.imag ();
+        }
+
+      for (octave_idx_type j = nr; j < nbuf; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_pvec) (S->q, Xx, buf, nr);
+      CXSPARSE_DNAME (_utsolve) (N->U, buf);
+
+      for (volatile octave_idx_type j = nr-1; j >= 0; j--)
+        {
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_DNAME (_pvec) (S->pinv, buf, Xx, nc);
+
+      for (octave_idx_type j = nr; j < nbuf; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_pvec) (S->q, Xz, buf, nr);
+      CXSPARSE_DNAME (_utsolve) (N->U, buf);
+
+      for (volatile octave_idx_type j = nr-1; j >= 0; j--)
+        {
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_DNAME (_pvec) (S->pinv, buf, Xz, nc);
+
+      for (octave_idx_type j = 0; j < nc; j++)
+        vec[j+idx] = Complex (Xx[j], Xz[j]);
+    }
+
+  info = 0;
+
+  return x;
+
+#else
+
+  octave_unused_parameter (b);
+
+  return ComplexMatrix ();
+
+#endif
+}
+
+// Complex-valued matrices.
+
+template <>
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::sparse_qr_rep
+(const SparseComplexMatrix& a, int order)
+  : nrows (a.rows ()), ncols (a.columns ())
 #if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-      octave_idx_type nr = (econ
-                            ? (ncols > nrows ? nrows : ncols)
-                            : nrows);
-      octave_idx_type b_nr = b.rows ();
-      octave_idx_type b_nc = b.cols ();
-      Matrix ret (nr, b_nc);
-
-      if (nrows != b_nr)
-        (*current_liboctave_error_handler)
-          ("sparse_qr: matrix dimension mismatch");
-      else if (b_nc < 0 || b_nr < 0)
-        (*current_liboctave_error_handler)
-          ("sparse_qr: matrix dimension with negative size");
-
-      cholmod_dense *QTB;  // Q' * B
-      cholmod_dense B = rod2rcd (b);
-
-      QTB = SuiteSparseQR_qmult<double> (SPQR_QTX, m_H, m_Htau, m_HPinv, &B,
-                                         &m_cc);
-      spqr_error_handler (&m_cc);
-
-      // copy QTB into ret
-      double *QTB_x = reinterpret_cast<double *> (QTB->x);
-      double *ret_vec = reinterpret_cast<double *> (ret.fortran_vec ());
-      for (octave_idx_type j = 0; j < b_nc; j++)
-        for (octave_idx_type i = 0; i < nr; i++)
-          ret_vec[j * nr + i] = QTB_x[j * b_nr + i];
-
-      cholmod_l_free_dense (&QTB, &m_cc);
-
-      return ret;
+  , m_cc (), m_R (nullptr), m_E (nullptr), m_H (nullptr),
+    m_Htau (nullptr), m_HPinv (nullptr)
+{
+  octave_idx_type nr = a.rows ();
+  octave_idx_type nc = a.cols ();
+
+  if (nr < 0 || nc < 0)
+    (*current_liboctave_error_handler)
+      ("matrix dimension with negative size");
+
+  if (order < 0 || order > 9)
+    (*current_liboctave_error_handler)
+      ("ordering %d is not supported by SPQR", order);
+
+  cholmod_l_start (&m_cc);
+  cholmod_sparse A = cos2ccs (a);
+
+  SuiteSparseQR<Complex> (order, static_cast<double> (SPQR_DEFAULT_TOL),
+                          static_cast<SuiteSparse_long> (A.nrow),
+                          &A, &m_R, &m_E, &m_H,
+                          &m_HPinv, &m_Htau, &m_cc);
+  spqr_error_handler (&m_cc);
+
+  if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
+    {
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
+    }
+}
 
 #elif defined (HAVE_CXSPARSE)
-
-      if (econ)
-        (*current_liboctave_error_handler)
-          ("sparse-qr: economy mode with CXSparse not supported");
-
-      octave_idx_type b_nr = b.rows ();
-      octave_idx_type b_nc = b.cols ();
-
-      octave_idx_type nc = N->L->n;
-      octave_idx_type nr = nrows;
-
-      const double *bvec = b.data ();
-
-      Matrix ret (b_nr, b_nc);
-      double *vec = ret.fortran_vec ();
-
-      if (nr < 0 || nc < 0 || nr != b_nr)
-        (*current_liboctave_error_handler) ("matrix dimension mismatch");
-
-      if (nr == 0 || nc == 0 || b_nc == 0)
-        ret = Matrix (nc, b_nc, 0.0);
-      else
-        {
-          OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
-
-          for (volatile octave_idx_type j = 0, idx = 0;
-               j < b_nc;
-               j++, idx += b_nr)
-            {
-              octave_quit ();
-
-              for (octave_idx_type i = nr; i < S->m2; i++)
-                buf[i] = 0.;
-
-              volatile octave_idx_type nm = (nr < nc ? nr : nc);
-
-              CXSPARSE_DNAME (_ipvec) (S->pinv, bvec + idx, buf, b_nr);
-
-              for (volatile octave_idx_type i = 0; i < nm; i++)
-                {
-                  octave_quit ();
-
-                  CXSPARSE_DNAME (_happly) (N->L, i, N->B[i], buf);
-                }
-
-              for (octave_idx_type i = 0; i < b_nr; i++)
-                vec[i+idx] = buf[i];
-            }
-        }
-
-      return ret;
+  , S (nullptr), N (nullptr)
+{
+  CXSPARSE_ZNAME () A;
+
+  A.nzmax = a.nnz ();
+  A.m = nrows;
+  A.n = ncols;
+  // Cast away const on A, with full knowledge that CSparse won't touch it
+  // Prevents the methods below making a copy of the data.
+  A.p = const_cast<suitesparse_integer *>
+        (to_suitesparse_intptr (a.cidx ()));
+  A.i = const_cast<suitesparse_integer *>
+        (to_suitesparse_intptr (a.ridx ()));
+  A.x = const_cast<cs_complex_t *>
+        (reinterpret_cast<const cs_complex_t *> (a.data ()));
+  A.nz = -1;
+
+  S = CXSPARSE_ZNAME (_sqr) (order, &A, 1);
+  N = CXSPARSE_ZNAME (_qr) (&A, S);
+
+  if (! N)
+    (*current_liboctave_error_handler)
+      ("sparse_qr: sparse matrix QR factorization filled");
+
+}
 
 #else
 
-      octave_unused_parameter (b);
-      octave_unused_parameter (econ);
-
-      return Matrix ();
+{
+  octave_unused_parameter (order);
+
+  (*current_liboctave_error_handler)
+    ("sparse_qr: support for CXSparse was unavailable or disabled when liboctave was built");
+}
+
+#endif
+
+template <>
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::~sparse_qr_rep (void)
+{
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+
+  cholmod_l_free_sparse (&m_R, &m_cc);
+  cholmod_l_free_sparse (&m_H, &m_cc);
+  cholmod_l_free_dense (&m_Htau, &m_cc);
+  free (m_E);  // FIXME: use cholmod_l_free
+  free (m_HPinv);
+  cholmod_l_finish (&m_cc);
+
+#elif defined (HAVE_CXSPARSE)
+
+  CXSPARSE_ZNAME (_sfree) (S);
+  CXSPARSE_ZNAME (_nfree) (N);
+
+#endif
+}
+
+template <>
+SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::V (void) const
+{
+#if defined (HAVE_CXSPARSE)
+  // Drop zeros from V and sort
+  // FIXME: Is the double transpose to sort necessary?
+
+  CXSPARSE_ZNAME (_dropzeros) (N->L);
+  CXSPARSE_ZNAME () *D = CXSPARSE_ZNAME (_transpose) (N->L, 1);
+  CXSPARSE_ZNAME (_spfree) (N->L);
+  N->L = CXSPARSE_ZNAME (_transpose) (D, 1);
+  CXSPARSE_ZNAME (_spfree) (D);
+
+  octave_idx_type nc = N->L->n;
+  octave_idx_type nz = N->L->nzmax;
+  SparseComplexMatrix ret (N->L->m, nc, nz);
+
+  for (octave_idx_type j = 0; j < nc+1; j++)
+    ret.xcidx (j) = N->L->p[j];
+
+  for (octave_idx_type j = 0; j < nz; j++)
+    {
+      ret.xridx (j) = N->L->i[j];
+      ret.xdata (j) = reinterpret_cast<Complex *> (N->L->x)[j];
+    }
+
+  return ret;
+
+#else
+
+  return SparseComplexMatrix ();
+
+#endif
+}
+
+template <>
+SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::R (bool econ) const
+{
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+
+  octave_idx_type nr = from_size_t (m_R->nrow);
+  octave_idx_type nc = from_size_t (m_R->ncol);
+  octave_idx_type nz = from_size_t (m_R->nzmax);
+
+  // FIXME: Does this work if econ = true?
+  SparseComplexMatrix ret ((econ ? (nc > nr ? nr : nc) : nr), nc, nz);
+  SuiteSparse_long *Rp = reinterpret_cast<SuiteSparse_long *> (m_R->p);
+  SuiteSparse_long *Ri = reinterpret_cast<SuiteSparse_long *> (m_R->i);
+
+  for (octave_idx_type j = 0; j < nc + 1; j++)
+    ret.xcidx (j) = from_suitesparse_long (Rp[j]);
+
+  for (octave_idx_type j = 0; j < nz; j++)
+    {
+      ret.xridx (j) = from_suitesparse_long (Ri[j]);
+      ret.xdata (j) = (reinterpret_cast<Complex *> (m_R->x))[j];
+    }
+
+  return ret;
+
+#elif defined (HAVE_CXSPARSE)
+
+  // Drop zeros from R and sort
+  // FIXME: Is the double transpose to sort necessary?
+
+  CXSPARSE_ZNAME (_dropzeros) (N->U);
+  CXSPARSE_ZNAME () *D = CXSPARSE_ZNAME (_transpose) (N->U, 1);
+  CXSPARSE_ZNAME (_spfree) (N->U);
+  N->U = CXSPARSE_ZNAME (_transpose) (D, 1);
+  CXSPARSE_ZNAME (_spfree) (D);
+
+  octave_idx_type nc = N->U->n;
+  octave_idx_type nz = N->U->nzmax;
+
+  SparseComplexMatrix ret ((econ ? (nc > nrows ? nrows : nc) : nrows),
+                           nc, nz);
+
+  for (octave_idx_type j = 0; j < nc+1; j++)
+    ret.xcidx (j) = N->U->p[j];
+
+  for (octave_idx_type j = 0; j < nz; j++)
+    {
+      ret.xridx (j) = N->U->i[j];
+      ret.xdata (j) = reinterpret_cast<Complex *>(N->U->x)[j];
+    }
+
+  return ret;
+
+#else
+
+  octave_unused_parameter (econ);
+
+  return SparseComplexMatrix ();
 
 #endif
-    }
-
-    template <>
-    Matrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::Q (bool econ)
-    {
+}
+
+template <>
+ComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::C
+(const ComplexMatrix& b, bool econ)
+{
 #if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
 
-      octave_idx_type nc = (econ
-                            ? (ncols > nrows ? nrows : ncols)
-                            : nrows);
-      Matrix ret (nrows, nc);
-      cholmod_dense *q;
-
-      // I is nrows x nrows identity matrix
-      cholmod_dense *I
-        = cholmod_l_allocate_dense (nrows, nrows, nrows, CHOLMOD_REAL, &m_cc);
-
-      for (octave_idx_type i = 0; i < nrows * nrows; i++)
-        (reinterpret_cast<double *> (I->x))[i] = 0.0;
-
-      for (octave_idx_type i = 0; i < nrows; i++)
-        (reinterpret_cast<double *> (I->x))[i * nrows + i] = 1.0;
-
-      q = SuiteSparseQR_qmult<double> (SPQR_QX, m_H, m_Htau, m_HPinv, I, &m_cc);
-      spqr_error_handler (&m_cc);
-
-      double *q_x = reinterpret_cast<double *> (q->x);
-      double *ret_vec = const_cast<double *> (ret.fortran_vec ());
-      for (octave_idx_type j = 0; j < nc; j++)
-        for (octave_idx_type i = 0; i < nrows; i++)
-          ret_vec[j * nrows + i] = q_x[j * nrows + i];
-
-      cholmod_l_free_dense (&q, &m_cc);
-      cholmod_l_free_dense (&I, &m_cc);
-
-      return ret;
+  // FIXME: not tested
+  octave_idx_type nr = (econ
+                        ? (ncols > nrows ? nrows : ncols)
+                        : nrows);
+  octave_idx_type b_nr = b.rows ();
+  octave_idx_type b_nc = b.cols ();
+  ComplexMatrix ret (nr, b_nc);
+
+  if (nrows != b_nr)
+    (*current_liboctave_error_handler) ("matrix dimension mismatch");
+
+  if (b_nc < 0 || b_nr < 0)
+    (*current_liboctave_error_handler)
+      ("matrix dimension with negative size");
+
+  cholmod_dense *QTB;  // Q' * B
+  cholmod_dense B = cod2ccd (b);
+
+  QTB = SuiteSparseQR_qmult<Complex> (SPQR_QTX, m_H, m_Htau, m_HPinv, &B,
+                                      &m_cc);
+  spqr_error_handler (&m_cc);
+
+  // copy QTB into ret
+  Complex *QTB_x = reinterpret_cast<Complex *> (QTB->x);
+  Complex *ret_vec = reinterpret_cast<Complex *> (ret.fortran_vec ());
+  for (octave_idx_type j = 0; j < b_nc; j++)
+    for (octave_idx_type i = 0; i < nr; i++)
+      ret_vec[j * nr + i] = QTB_x[j * b_nr + i];
+
+  cholmod_l_free_dense (&QTB, &m_cc);
+
+  return ret;
 
 #elif defined (HAVE_CXSPARSE)
 
-      if (econ)
-        (*current_liboctave_error_handler)
-          ("sparse-qr: economy mode with CXSparse not supported");
-
-      octave_idx_type nc = N->L->n;
-      octave_idx_type nr = nrows;
-      Matrix ret (nr, nr);
-      double *ret_vec = ret.fortran_vec ();
-
-      if (nr < 0 || nc < 0)
-        (*current_liboctave_error_handler) ("matrix dimension mismatch");
-
-      if (nr == 0 || nc == 0)
-        ret = Matrix (nc, nr, 0.0);
-      else
+  if (econ)
+    (*current_liboctave_error_handler)
+      ("sparse-qr: economy mode with CXSparse not supported");
+
+  octave_idx_type b_nr = b.rows ();
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type nc = N->L->n;
+  octave_idx_type nr = nrows;
+  const cs_complex_t *bvec
+    = reinterpret_cast<const cs_complex_t *> (b.data ());
+  ComplexMatrix ret (b_nr, b_nc);
+  Complex *vec = ret.fortran_vec ();
+
+  if (nr < 0 || nc < 0 || nr != b_nr)
+    (*current_liboctave_error_handler) ("matrix dimension mismatch");
+
+  if (nr == 0 || nc == 0 || b_nc == 0)
+    ret = ComplexMatrix (nc, b_nc, Complex (0.0, 0.0));
+  else
+    {
+      OCTAVE_LOCAL_BUFFER (Complex, buf, S->m2);
+
+      for (volatile octave_idx_type j = 0, idx = 0;
+           j < b_nc;
+           j++, idx += b_nr)
         {
-          OCTAVE_LOCAL_BUFFER (double, bvec, nr + 1);
-
-          for (octave_idx_type i = 0; i < nr; i++)
-            bvec[i] = 0.;
-
-          OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
-
-          for (volatile octave_idx_type j = 0, idx = 0; j < nr; j++, idx += nr)
+          octave_quit ();
+
+          volatile octave_idx_type nm = (nr < nc ? nr : nc);
+
+          CXSPARSE_ZNAME (_ipvec) (S->pinv, bvec + idx,
+                                   reinterpret_cast<cs_complex_t *> (buf),
+                                   b_nr);
+
+          for (volatile octave_idx_type i = 0; i < nm; i++)
             {
               octave_quit ();
 
-              bvec[j] = 1.0;
-              for (octave_idx_type i = nr; i < S->m2; i++)
-                buf[i] = 0.;
-
-              volatile octave_idx_type nm = (nr < nc ? nr : nc);
-
-              CXSPARSE_DNAME (_ipvec) (S->pinv, bvec, buf, nr);
-
-              for (volatile octave_idx_type i = 0; i < nm; i++)
-                {
-                  octave_quit ();
-
-                  CXSPARSE_DNAME (_happly) (N->L, i, N->B[i], buf);
-                }
-
-              for (octave_idx_type i = 0; i < nr; i++)
-                ret_vec[i+idx] = buf[i];
-
-              bvec[j] = 0.0;
+              CXSPARSE_ZNAME (_happly) (N->L, i, N->B[i],
+                                        reinterpret_cast<cs_complex_t *> (buf));
             }
+
+          for (octave_idx_type i = 0; i < b_nr; i++)
+            vec[i+idx] = buf[i];
         }
-
-      return ret.transpose ();
+    }
+
+  return ret;
 
 #else
 
-      octave_unused_parameter (econ);
-
-      return Matrix ();
+  octave_unused_parameter (b);
+  octave_unused_parameter (econ);
+
+  return ComplexMatrix ();
 
 #endif
-    }
-
-    template <>
-    template <>
-    Matrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::tall_solve<MArray<double>, Matrix>
-    (const MArray<double>& b, octave_idx_type& info)
-    {
-      info = -1;
-
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD) && defined (HAVE_CXSPARSE))
-
-      octave_idx_type b_nr = b.rows ();
-      octave_idx_type b_nc = b.cols ();
-      Matrix x (ncols, b_nc);  // X = m_E'*(m_R\(Q'*B))
-
-      if (nrows < 0 || ncols < 0 || b_nc < 0 || b_nr < 0)
-        (*current_liboctave_error_handler)
-          ("matrix dimension with negative size");
-
-      if (nrows < 0 || ncols < 0 || nrows != b_nr)
-        (*current_liboctave_error_handler) ("matrix dimension mismatch");
-
-      cholmod_dense *QTB;  // Q' * B
-      cholmod_dense B = rod2rcd (b);
-
-      // FIXME: Process b column by column as in the CXSPARSE version below.
-      // This avoids a large dense matrix Q' * B in memory.
-      QTB = SuiteSparseQR_qmult<double> (SPQR_QTX, m_H, m_Htau, m_HPinv, &B,
-                                         &m_cc);
-
-      spqr_error_handler (&m_cc);
-
-      // convert m_R into CXSPARSE matrix R2
-      CXSPARSE_DNAME (_sparse) R2;
-      R2.n = ncols;
-      R2.m = ncols;
-      R2.nzmax = m_R->nzmax;
-      R2.x = reinterpret_cast<double *> (m_R->x);
-      suitesparse_integer *R2_p;
-      suitesparse_integer *R2_i;
-      if (sizeof (suitesparse_integer) == sizeof (SuiteSparse_long))
-        {
-          R2.p = reinterpret_cast<suitesparse_integer *> (m_R->p);
-          R2.i = reinterpret_cast<suitesparse_integer *> (m_R->i);
-        }
-      else
-        {
-          R2_p = new suitesparse_integer[ncols+1];
-          SuiteSparse_long *R_p = reinterpret_cast<SuiteSparse_long *> (m_R->p);
-          for (octave_idx_type i = 0; i < ncols+1; i++)
-            R2_p[i] = suitesparse_long_to_suitesparse_integer (R_p[i]);
-          R2.p = R2_p;
-          octave_idx_type nnz = m_R->nzmax;
-          R2_i = new suitesparse_integer[nnz];
-          SuiteSparse_long *R_i = reinterpret_cast<SuiteSparse_long *> (m_R->i);
-          for (octave_idx_type i = 0; i < nnz; i++)
-            R2_i[i] =  suitesparse_long_to_suitesparse_integer (R_i[i]);
-          R2.i = R2_i;
-        }
-      R2.nz = -1;
-      double *x_vec = const_cast<double *> (x.fortran_vec ());
-      suitesparse_integer *E;
-      if (sizeof (suitesparse_integer) != sizeof (SuiteSparse_long))
-        {
-          E = new suitesparse_integer [ncols];
-          for (octave_idx_type i = 0; i < ncols; i++)
-            E[i] = suitesparse_long_to_suitesparse_integer (m_E[i]);
-        }
-      else
-        E = reinterpret_cast<suitesparse_integer *> (m_E);
-      for (volatile octave_idx_type j = 0; j < b_nc; j++)
-        {
-          // fill x(:,j)
-          // solve (m_R\(Q'*B(:,j)) and store result in QTB(:,j)
-          CXSPARSE_DNAME (_usolve)
-            (&R2, &(reinterpret_cast<double *> (QTB->x)[j * b_nr]));
-          // x(:,j) = m_E' * (m_R\(Q'*B(:,j))
-          CXSPARSE_DNAME (_ipvec)
-            (E, &(reinterpret_cast<double *> (QTB->x)[j * b_nr]),
-             &x_vec[j * ncols], ncols);
-        }
-
-      if (sizeof (suitesparse_integer) != sizeof (SuiteSparse_long))
-        {
-          delete [] R2_p;
-          delete [] R2_i;
-          delete [] E;
-        }
-      cholmod_l_free_dense (&QTB, &m_cc);
-
-      info = 0;
-
-      return x;
+}
+
+template <>
+ComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::Q (bool econ)
+{
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+
+  octave_idx_type nc = (econ
+                        ? (ncols > nrows ? nrows : ncols)
+                        : nrows);
+  ComplexMatrix ret (nrows, nc);
+  cholmod_dense *q;
+
+  // I is nrows x nrows identity matrix
+  cholmod_dense *I
+    = reinterpret_cast<cholmod_dense *>
+      (cholmod_l_allocate_dense (nrows, nrows, nrows, CHOLMOD_COMPLEX, &m_cc));
+
+  for (octave_idx_type i = 0; i < nrows * nrows; i++)
+    (reinterpret_cast<Complex *> (I->x))[i] = 0.0;
+
+  for (octave_idx_type i = 0; i < nrows; i++)
+    (reinterpret_cast<Complex *> (I->x))[i * nrows + i] = 1.0;
+
+  q = SuiteSparseQR_qmult<Complex> (SPQR_QX, m_H, m_Htau, m_HPinv, I,
+                                    &m_cc);
+  spqr_error_handler (&m_cc);
+
+  Complex *q_x = reinterpret_cast<Complex *> (q->x);
+  Complex *ret_vec = const_cast<Complex *> (ret.fortran_vec ());
+
+  for (octave_idx_type j = 0; j < nc; j++)
+    for (octave_idx_type i = 0; i < nrows; i++)
+      ret_vec[j * nrows + i] = q_x[j * nrows + i];
+
+  cholmod_l_free_dense (&q, &m_cc);
+  cholmod_l_free_dense (&I, &m_cc);
+
+  return ret;
 
 #elif defined (HAVE_CXSPARSE)
 
-      octave_idx_type nr = nrows;
-      octave_idx_type nc = ncols;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      const double *bvec = b.data ();
-
-      Matrix x (nc, b_nc);
-      double *vec = x.fortran_vec ();
-
-      OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
-
-      for (volatile octave_idx_type i = 0, idx = 0, bidx = 0; i < b_nc;
-           i++, idx+=nc, bidx+=b_nr)
+  if (econ)
+    (*current_liboctave_error_handler)
+      ("sparse-qr: economy mode with CXSparse not supported");
+
+  octave_idx_type nc = N->L->n;
+  octave_idx_type nr = nrows;
+  ComplexMatrix ret (nr, nr);
+  Complex *vec = ret.fortran_vec ();
+
+  if (nr < 0 || nc < 0)
+    (*current_liboctave_error_handler) ("matrix dimension mismatch");
+
+  if (nr == 0 || nc == 0)
+    ret = ComplexMatrix (nc, nr, Complex (0.0, 0.0));
+  else
+    {
+      OCTAVE_LOCAL_BUFFER (cs_complex_t, bvec, nr);
+
+      for (octave_idx_type i = 0; i < nr; i++)
+        bvec[i] = cs_complex_t (0.0, 0.0);
+
+      OCTAVE_LOCAL_BUFFER (Complex, buf, S->m2);
+
+      for (volatile octave_idx_type j = 0, idx = 0; j < nr; j++, idx+=nr)
         {
           octave_quit ();
 
-          for (octave_idx_type j = nr; j < S->m2; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_ipvec) (S->pinv, bvec + bidx, buf, nr);
-
-          for (volatile octave_idx_type j = 0; j < nc; j++)
-            {
-              octave_quit ();
-
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_DNAME (_usolve) (N->U, buf);
-          CXSPARSE_DNAME (_ipvec) (S->q, buf, vec + idx, nc);
-        }
-
-      info = 0;
-
-      return x;
-
-#else
-
-      octave_unused_parameter (b);
-
-      return Matrix ();
-
-#endif
-    }
-
-    template <>
-    template <>
-    Matrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::wide_solve<MArray<double>, Matrix>
-    (const MArray<double>& b, octave_idx_type& info) const
-    {
-      info = -1;
-#if defined (HAVE_CXSPARSE)
-
-      // These are swapped because the original matrix was transposed in
-      // sparse_qr<SparseMatrix>::solve.
-
-      octave_idx_type nr = ncols;
-      octave_idx_type nc = nrows;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      const double *bvec = b.data ();
-
-      Matrix x (nc, b_nc);
-      double *vec = x.fortran_vec ();
-
-      volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
-
-      OCTAVE_LOCAL_BUFFER (double, buf, nbuf);
-
-      for (volatile octave_idx_type i = 0, idx = 0, bidx = 0; i < b_nc;
-           i++, idx+=nc, bidx+=b_nr)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = nr; j < nbuf; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_pvec) (S->q, bvec + bidx, buf, nr);
-          CXSPARSE_DNAME (_utsolve) (N->U, buf);
-
-          for (volatile octave_idx_type j = nr-1; j >= 0; j--)
-            {
-              octave_quit ();
-
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_DNAME (_pvec) (S->pinv, buf, vec + idx, nc);
-        }
-
-      info = 0;
-
-      return x;
-
-#else
-      octave_unused_parameter (b);
-
-      return Matrix ();
-
-#endif
-    }
-
-    template <>
-    template <>
-    SparseMatrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::tall_solve<SparseMatrix, SparseMatrix>
-    (const SparseMatrix& b, octave_idx_type& info)
-    {
-      info = -1;
-
-#if defined (HAVE_CXSPARSE)
-
-      octave_idx_type nr = nrows;
-      octave_idx_type nc = ncols;
-
-      octave_idx_type b_nr = b.rows ();
-      octave_idx_type b_nc = b.cols ();
-
-      SparseMatrix x (nc, b_nc, b.nnz ());
-      x.xcidx (0) = 0;
-
-      volatile octave_idx_type x_nz = b.nnz ();
-      volatile octave_idx_type ii = 0;
-
-      OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = 0; j < b_nr; j++)
-            Xx[j] = b.xelem (j, i);
-
-          for (octave_idx_type j = nr; j < S->m2; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_ipvec) (S->pinv, Xx, buf, nr);
-
-          for (volatile octave_idx_type j = 0; j < nc; j++)
+          bvec[j] = cs_complex_t (1.0, 0.0);
+
+          volatile octave_idx_type nm = (nr < nc ? nr : nc);
+
+          CXSPARSE_ZNAME (_ipvec) (S->pinv, bvec,
+                                   reinterpret_cast<cs_complex_t *> (buf),
+                                   nr);
+
+          for (volatile octave_idx_type i = 0; i < nm; i++)
             {
               octave_quit ();
 
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+              CXSPARSE_ZNAME (_happly) (N->L, i, N->B[i],
+                                        reinterpret_cast<cs_complex_t *> (buf));
             }
 
-          CXSPARSE_DNAME (_usolve) (N->U, buf);
-          CXSPARSE_DNAME (_ipvec) (S->q, buf, Xx, nc);
-
-          for (octave_idx_type j = 0; j < nc; j++)
+          for (octave_idx_type i = 0; i < nr; i++)
+            vec[i+idx] = buf[i];
+
+          bvec[j] = cs_complex_t (0.0, 0.0);
+        }
+    }
+
+  return ret.hermitian ();
+
+#else
+
+  octave_unused_parameter (econ);
+
+  return ComplexMatrix ();
+
+#endif
+}
+
+template <>
+template <>
+SparseComplexMatrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::tall_solve<SparseComplexMatrix,
+          SparseComplexMatrix>
+          (const SparseComplexMatrix& b, octave_idx_type& info)
+{
+  info = -1;
+
+#if defined (HAVE_CXSPARSE)
+
+  octave_idx_type nr = nrows;
+  octave_idx_type nc = ncols;
+
+  octave_idx_type b_nr = b.rows ();
+  octave_idx_type b_nc = b.cols ();
+
+  SparseComplexMatrix x (nc, b_nc, b.nnz ());
+  x.xcidx (0) = 0;
+
+  volatile octave_idx_type x_nz = b.nnz ();
+  volatile octave_idx_type ii = 0;
+
+  OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (double, Xz, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        {
+          Complex c = b.xelem (j, i);
+          Xx[j] = c.real ();
+          Xz[j] = c.imag ();
+        }
+
+      for (octave_idx_type j = nr; j < S->m2; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_ipvec) (S->pinv, Xx, buf, nr);
+
+      for (volatile octave_idx_type j = 0; j < nc; j++)
+        {
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_DNAME (_usolve) (N->U, buf);
+      CXSPARSE_DNAME (_ipvec) (S->q, buf, Xx, nc);
+
+      for (octave_idx_type j = nr; j < S->m2; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_ipvec) (S->pinv, Xz, buf, nr);
+
+      for (volatile octave_idx_type j = 0; j < nc; j++)
+        {
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_DNAME (_usolve) (N->U, buf);
+      CXSPARSE_DNAME (_ipvec) (S->q, buf, Xz, nc);
+
+      for (octave_idx_type j = 0; j < nc; j++)
+        {
+          Complex tmp = Complex (Xx[j], Xz[j]);
+
+          if (tmp != 0.0)
             {
-              double tmp = Xx[j];
-
-              if (tmp != 0.0)
+              if (ii == x_nz)
                 {
-                  if (ii == x_nz)
-                    {
-                      // Resize the sparse matrix
-                      octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
-                      sz = (sz > 10 ? sz : 10) + x_nz;
-                      x.change_capacity (sz);
-                      x_nz = sz;
-                    }
-
-                  x.xdata (ii) = tmp;
-                  x.xridx (ii++) = j;
+                  // Resize the sparse matrix
+                  octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
+                  sz = (sz > 10 ? sz : 10) + x_nz;
+                  x.change_capacity (sz);
+                  x_nz = sz;
                 }
+
+              x.xdata (ii) = tmp;
+              x.xridx (ii++) = j;
             }
-
-          x.xcidx (i+1) = ii;
         }
 
-      info = 0;
-
-      return x;
+      x.xcidx (i+1) = ii;
+    }
+
+  info = 0;
+
+  return x;
 
 #else
 
-      octave_unused_parameter (b);
-
-      return SparseMatrix ();
+  octave_unused_parameter (b);
+
+  return SparseComplexMatrix ();
 
 #endif
-    }
-
-    template <>
-    template <>
-    SparseMatrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::wide_solve<SparseMatrix, SparseMatrix>
-    (const SparseMatrix& b, octave_idx_type& info) const
-    {
-      info = -1;
+}
+
+template <>
+template <>
+SparseComplexMatrix
+sparse_qr<SparseMatrix>::sparse_qr_rep::wide_solve<SparseComplexMatrix,
+          SparseComplexMatrix>
+          (const SparseComplexMatrix& b, octave_idx_type& info) const
+{
+  info = -1;
 
 #if defined (HAVE_CXSPARSE)
 
-      // These are swapped because the original matrix was transposed in
-      // sparse_qr<SparseMatrix>::solve.
-
-      octave_idx_type nr = ncols;
-      octave_idx_type nc = nrows;
-
-      octave_idx_type b_nr = b.rows ();
-      octave_idx_type b_nc = b.cols ();
-
-      SparseMatrix x (nc, b_nc, b.nnz ());
-      x.xcidx (0) = 0;
-
-      volatile octave_idx_type x_nz = b.nnz ();
-      volatile octave_idx_type ii = 0;
-      volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
-
-      OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (double, buf, nbuf);
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+  // These are swapped because the original matrix was transposed in
+  // sparse_qr<SparseMatrix>::solve.
+
+  octave_idx_type nr = ncols;
+  octave_idx_type nc = nrows;
+
+  octave_idx_type b_nr = b.rows ();
+  octave_idx_type b_nc = b.cols ();
+
+  SparseComplexMatrix x (nc, b_nc, b.nnz ());
+  x.xcidx (0) = 0;
+
+  volatile octave_idx_type x_nz = b.nnz ();
+  volatile octave_idx_type ii = 0;
+  volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
+
+  OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (double, Xz, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (double, buf, nbuf);
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        {
+          Complex c = b.xelem (j, i);
+          Xx[j] = c.real ();
+          Xz[j] = c.imag ();
+        }
+
+      for (octave_idx_type j = nr; j < nbuf; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_pvec) (S->q, Xx, buf, nr);
+      CXSPARSE_DNAME (_utsolve) (N->U, buf);
+
+      for (volatile octave_idx_type j = nr-1; j >= 0; j--)
+        {
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_DNAME (_pvec) (S->pinv, buf, Xx, nc);
+
+      for (octave_idx_type j = nr; j < nbuf; j++)
+        buf[j] = 0.;
+
+      CXSPARSE_DNAME (_pvec) (S->q, Xz, buf, nr);
+      CXSPARSE_DNAME (_utsolve) (N->U, buf);
+
+      for (volatile octave_idx_type j = nr-1; j >= 0; j--)
+        {
+          octave_quit ();
+
+          CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_DNAME (_pvec) (S->pinv, buf, Xz, nc);
+
+      for (octave_idx_type j = 0; j < nc; j++)
+        {
+          Complex tmp = Complex (Xx[j], Xz[j]);
+
+          if (tmp != 0.0)
+            {
+              if (ii == x_nz)
+                {
+                  // Resize the sparse matrix
+                  octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
+                  sz = (sz > 10 ? sz : 10) + x_nz;
+                  x.change_capacity (sz);
+                  x_nz = sz;
+                }
+
+              x.xdata (ii) = tmp;
+              x.xridx (ii++) = j;
+            }
+        }
+
+      x.xcidx (i+1) = ii;
+    }
+
+  info = 0;
+
+  x.maybe_compress ();
+
+  return x;
+
+#else
+
+  octave_unused_parameter (b);
+
+  return SparseComplexMatrix ();
+
+#endif
+}
+
+template <>
+template <>
+ComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::tall_solve<MArray<double>,
+          ComplexMatrix>
+          (const MArray<double>& b, octave_idx_type& info)
+{
+  info = -1;
+
+#if defined (HAVE_CXSPARSE)
+
+  octave_idx_type nr = nrows;
+  octave_idx_type nc = ncols;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  ComplexMatrix x (nc, b_nc);
+  cs_complex_t *vec = reinterpret_cast<cs_complex_t *> (x.fortran_vec ());
+
+  OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, S->m2);
+  OCTAVE_LOCAL_BUFFER (Complex, Xx, b_nr);
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        Xx[j] = b.xelem (j, i);
+
+      for (octave_idx_type j = nr; j < S->m2; j++)
+        buf[j] = cs_complex_t (0.0, 0.0);
+
+      CXSPARSE_ZNAME (_ipvec) (S->pinv,
+                               reinterpret_cast<cs_complex_t *>(Xx),
+                               buf, nr);
+
+      for (volatile octave_idx_type j = 0; j < nc; j++)
         {
           octave_quit ();
 
-          for (octave_idx_type j = 0; j < b_nr; j++)
-            Xx[j] = b.xelem (j, i);
-
-          for (octave_idx_type j = nr; j < nbuf; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_pvec) (S->q, Xx, buf, nr);
-          CXSPARSE_DNAME (_utsolve) (N->U, buf);
-
-          for (volatile octave_idx_type j = nr-1; j >= 0; j--)
-            {
-              octave_quit ();
-
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_DNAME (_pvec) (S->pinv, buf, Xx, nc);
-
-          for (octave_idx_type j = 0; j < nc; j++)
+          CXSPARSE_ZNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_ZNAME (_usolve) (N->U, buf);
+      CXSPARSE_ZNAME (_ipvec) (S->q, buf, vec + idx, nc);
+    }
+
+  info = 0;
+
+  return x;
+
+#else
+
+  octave_unused_parameter (b);
+
+  return ComplexMatrix ();
+
+#endif
+}
+
+template <>
+template <>
+ComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::wide_solve<MArray<double>,
+          ComplexMatrix>
+          (const MArray<double>& b, octave_idx_type& info) const
+{
+  info = -1;
+
+#if defined (HAVE_CXSPARSE)
+
+  // These are swapped because the original matrix was transposed in
+  // sparse_qr<SparseComplexMatrix>::solve.
+
+  octave_idx_type nr = ncols;
+  octave_idx_type nc = nrows;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  ComplexMatrix x (nc, b_nc);
+  cs_complex_t *vec = reinterpret_cast<cs_complex_t *> (x.fortran_vec ());
+
+  volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
+
+  OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, nbuf);
+  OCTAVE_LOCAL_BUFFER (Complex, Xx, b_nr);
+  OCTAVE_LOCAL_BUFFER (double, B, nr);
+
+  for (octave_idx_type i = 0; i < nr; i++)
+    B[i] = N->B[i];
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        Xx[j] = b.xelem (j, i);
+
+      for (octave_idx_type j = nr; j < nbuf; j++)
+        buf[j] = cs_complex_t (0.0, 0.0);
+
+      CXSPARSE_ZNAME (_pvec) (S->q, reinterpret_cast<cs_complex_t *> (Xx),
+                              buf, nr);
+      CXSPARSE_ZNAME (_utsolve) (N->U, buf);
+
+      for (volatile octave_idx_type j = nr-1; j >= 0; j--)
+        {
+          octave_quit ();
+
+          CXSPARSE_ZNAME (_happly) (N->L, j, B[j], buf);
+        }
+
+      CXSPARSE_ZNAME (_pvec) (S->pinv, buf, vec + idx, nc);
+    }
+
+  info = 0;
+
+  return x;
+
+#else
+
+  octave_unused_parameter (b);
+
+  return ComplexMatrix ();
+
+#endif
+}
+
+template <>
+template <>
+SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::tall_solve<SparseMatrix,
+          SparseComplexMatrix>
+          (const SparseMatrix& b, octave_idx_type& info)
+{
+  info = -1;
+
+#if defined (HAVE_CXSPARSE)
+
+  octave_idx_type nr = nrows;
+  octave_idx_type nc = ncols;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  SparseComplexMatrix x (nc, b_nc, b.nnz ());
+  x.xcidx (0) = 0;
+
+  volatile octave_idx_type x_nz = b.nnz ();
+  volatile octave_idx_type ii = 0;
+
+  OCTAVE_LOCAL_BUFFER (Complex, Xx, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, S->m2);
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        Xx[j] = b.xelem (j, i);
+
+      for (octave_idx_type j = nr; j < S->m2; j++)
+        buf[j] = cs_complex_t (0.0, 0.0);
+
+      CXSPARSE_ZNAME (_ipvec) (S->pinv,
+                               reinterpret_cast<cs_complex_t *> (Xx),
+                               buf, nr);
+
+      for (volatile octave_idx_type j = 0; j < nc; j++)
+        {
+          octave_quit ();
+
+          CXSPARSE_ZNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_ZNAME (_usolve) (N->U, buf);
+      CXSPARSE_ZNAME (_ipvec) (S->q, buf,
+                               reinterpret_cast<cs_complex_t *> (Xx),
+                               nc);
+
+      for (octave_idx_type j = 0; j < nc; j++)
+        {
+          Complex tmp = Xx[j];
+
+          if (tmp != 0.0)
             {
-              double tmp = Xx[j];
-
-              if (tmp != 0.0)
+              if (ii == x_nz)
                 {
-                  if (ii == x_nz)
-                    {
-                      // Resize the sparse matrix
-                      octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
-                      sz = (sz > 10 ? sz : 10) + x_nz;
-                      x.change_capacity (sz);
-                      x_nz = sz;
-                    }
-
-                  x.xdata (ii) = tmp;
-                  x.xridx (ii++) = j;
+                  // Resize the sparse matrix
+                  octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
+                  sz = (sz > 10 ? sz : 10) + x_nz;
+                  x.change_capacity (sz);
+                  x_nz = sz;
                 }
+
+              x.xdata (ii) = tmp;
+              x.xridx (ii++) = j;
             }
-
-          x.xcidx (i+1) = ii;
         }
 
-      info = 0;
-
-      x.maybe_compress ();
-
-      return x;
+      x.xcidx (i+1) = ii;
+    }
+
+  info = 0;
+
+  x.maybe_compress ();
+
+  return x;
 
 #else
 
-      octave_unused_parameter (b);
-
-      return SparseMatrix ();
+  octave_unused_parameter (b);
+
+  return SparseComplexMatrix ();
 
 #endif
-    }
-
-    template <>
-    template <>
-    ComplexMatrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::tall_solve<MArray<Complex>, ComplexMatrix>
-    (const MArray<Complex>& b, octave_idx_type& info)
-    {
-      info = -1;
+}
+
+template <>
+template <>
+SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::wide_solve<SparseMatrix,
+          SparseComplexMatrix>
+          (const SparseMatrix& b, octave_idx_type& info) const
+{
+  info = -1;
 
 #if defined (HAVE_CXSPARSE)
 
-      octave_idx_type nr = nrows;
-      octave_idx_type nc = ncols;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      ComplexMatrix x (nc, b_nc);
-      Complex *vec = x.fortran_vec ();
-
-      OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (double, Xz, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+  // These are swapped because the original matrix was transposed in
+  // sparse_qr<SparseComplexMatrix>::solve.
+
+  octave_idx_type nr = ncols;
+  octave_idx_type nc = nrows;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  SparseComplexMatrix x (nc, b_nc, b.nnz ());
+  x.xcidx (0) = 0;
+
+  volatile octave_idx_type x_nz = b.nnz ();
+  volatile octave_idx_type ii = 0;
+  volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
+
+  OCTAVE_LOCAL_BUFFER (Complex, Xx, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, nbuf);
+  OCTAVE_LOCAL_BUFFER (double, B, nr);
+
+  for (octave_idx_type i = 0; i < nr; i++)
+    B[i] = N->B[i];
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        Xx[j] = b.xelem (j, i);
+
+      for (octave_idx_type j = nr; j < nbuf; j++)
+        buf[j] = cs_complex_t (0.0, 0.0);
+
+      CXSPARSE_ZNAME (_pvec) (S->q,
+                              reinterpret_cast<cs_complex_t *> (Xx),
+                              buf, nr);
+      CXSPARSE_ZNAME (_utsolve) (N->U, buf);
+
+      for (volatile octave_idx_type j = nr-1; j >= 0; j--)
+        {
+          octave_quit ();
+
+          CXSPARSE_ZNAME (_happly) (N->L, j, B[j], buf);
+        }
+
+      CXSPARSE_ZNAME (_pvec) (S->pinv, buf,
+                              reinterpret_cast<cs_complex_t *> (Xx),
+                              nc);
+
+      for (octave_idx_type j = 0; j < nc; j++)
+        {
+          Complex tmp = Xx[j];
+
+          if (tmp != 0.0)
+            {
+              if (ii == x_nz)
+                {
+                  // Resize the sparse matrix
+                  octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
+                  sz = (sz > 10 ? sz : 10) + x_nz;
+                  x.change_capacity (sz);
+                  x_nz = sz;
+                }
+
+              x.xdata (ii) = tmp;
+              x.xridx (ii++) = j;
+            }
+        }
+
+      x.xcidx (i+1) = ii;
+    }
+
+  info = 0;
+
+  x.maybe_compress ();
+
+  return x;
+
+#else
+
+  octave_unused_parameter (b);
+
+  return SparseComplexMatrix ();
+
+#endif
+}
+
+template <>
+template <>
+ComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::tall_solve<MArray<Complex>,
+          ComplexMatrix>
+          (const MArray<Complex>& b, octave_idx_type& info)
+{
+  info = -1;
+
+#if defined (HAVE_CXSPARSE)
+
+  octave_idx_type nr = nrows;
+  octave_idx_type nc = ncols;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  const cs_complex_t *bvec = reinterpret_cast<const cs_complex_t *>
+                             (b.data ());
+
+  ComplexMatrix x (nc, b_nc);
+  cs_complex_t *vec = reinterpret_cast<cs_complex_t *>
+                      (x.fortran_vec ());
+
+  OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, S->m2);
+
+  for (volatile octave_idx_type i = 0, idx = 0, bidx = 0; i < b_nc;
+       i++, idx+=nc, bidx+=b_nr)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = nr; j < S->m2; j++)
+        buf[j] = cs_complex_t (0.0, 0.0);
+
+      CXSPARSE_ZNAME (_ipvec) (S->pinv, bvec + bidx, buf, nr);
+
+      for (volatile octave_idx_type j = 0; j < nc; j++)
+        {
+          octave_quit ();
+
+          CXSPARSE_ZNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_ZNAME (_usolve) (N->U, buf);
+      CXSPARSE_ZNAME (_ipvec) (S->q, buf, vec + idx, nc);
+    }
+
+  info = 0;
+
+  return x;
+
+#else
+
+  octave_unused_parameter (b);
+
+  return ComplexMatrix ();
+
+#endif
+}
+
+template <>
+template <>
+ComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::wide_solve<MArray<Complex>,
+          ComplexMatrix>
+          (const MArray<Complex>& b, octave_idx_type& info) const
+{
+  info = -1;
+
+#if defined (HAVE_CXSPARSE)
+
+  // These are swapped because the original matrix was transposed in
+  // sparse_qr<SparseComplexMatrix>::solve.
+
+  octave_idx_type nr = ncols;
+  octave_idx_type nc = nrows;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  const cs_complex_t *bvec = reinterpret_cast<const cs_complex_t *>
+                             (b.data ());
+
+  ComplexMatrix x (nc, b_nc);
+  cs_complex_t *vec = reinterpret_cast<cs_complex_t *> (x.fortran_vec ());
+
+  volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
+
+  OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, nbuf);
+  OCTAVE_LOCAL_BUFFER (double, B, nr);
+
+  for (octave_idx_type i = 0; i < nr; i++)
+    B[i] = N->B[i];
+
+  for (volatile octave_idx_type i = 0, idx = 0, bidx = 0; i < b_nc;
+       i++, idx+=nc, bidx+=b_nr)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = nr; j < nbuf; j++)
+        buf[j] = cs_complex_t (0.0, 0.0);
+
+      CXSPARSE_ZNAME (_pvec) (S->q, bvec + bidx, buf, nr);
+      CXSPARSE_ZNAME (_utsolve) (N->U, buf);
+
+      for (volatile octave_idx_type j = nr-1; j >= 0; j--)
         {
           octave_quit ();
 
-          for (octave_idx_type j = 0; j < b_nr; j++)
-            {
-              Complex c = b.xelem (j, i);
-              Xx[j] = c.real ();
-              Xz[j] = c.imag ();
-            }
-
-          for (octave_idx_type j = nr; j < S->m2; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_ipvec) (S->pinv, Xx, buf, nr);
-
-          for (volatile octave_idx_type j = 0; j < nc; j++)
-            {
-              octave_quit ();
-
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_DNAME (_usolve) (N->U, buf);
-          CXSPARSE_DNAME (_ipvec) (S->q, buf, Xx, nc);
-
-          for (octave_idx_type j = nr; j < S->m2; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_ipvec) (S->pinv, Xz, buf, nr);
-
-          for (volatile octave_idx_type j = 0; j < nc; j++)
-            {
-              octave_quit ();
-
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_DNAME (_usolve) (N->U, buf);
-          CXSPARSE_DNAME (_ipvec) (S->q, buf, Xz, nc);
-
-          for (octave_idx_type j = 0; j < nc; j++)
-            vec[j+idx] = Complex (Xx[j], Xz[j]);
+          CXSPARSE_ZNAME (_happly) (N->L, j, B[j], buf);
         }
 
-      info = 0;
-
-      return x;
+      CXSPARSE_ZNAME (_pvec) (S->pinv, buf, vec + idx, nc);
+    }
+
+  info = 0;
+
+  return x;
 
 #else
 
-      octave_unused_parameter (b);
-
-      return ComplexMatrix ();
+  octave_unused_parameter (b);
+
+  return ComplexMatrix ();
 
 #endif
-    }
-
-    template <>
-    template <>
-    ComplexMatrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::wide_solve<MArray<Complex>, ComplexMatrix>
-    (const MArray<Complex>& b, octave_idx_type& info) const
-    {
-      info = -1;
+}
+
+template <>
+template <>
+SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::tall_solve<SparseComplexMatrix,
+          SparseComplexMatrix>
+          (const SparseComplexMatrix& b, octave_idx_type& info)
+{
+  info = -1;
 
 #if defined (HAVE_CXSPARSE)
 
-      // These are swapped because the original matrix was transposed in
-      // sparse_qr<SparseMatrix>::solve.
-
-      octave_idx_type nr = ncols;
-      octave_idx_type nc = nrows;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      ComplexMatrix x (nc, b_nc);
-      Complex *vec = x.fortran_vec ();
-
-      volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
-
-      OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (double, Xz, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (double, buf, nbuf);
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+  octave_idx_type nr = nrows;
+  octave_idx_type nc = ncols;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  SparseComplexMatrix x (nc, b_nc, b.nnz ());
+  x.xcidx (0) = 0;
+
+  volatile octave_idx_type x_nz = b.nnz ();
+  volatile octave_idx_type ii = 0;
+
+  OCTAVE_LOCAL_BUFFER (Complex, Xx, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, S->m2);
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        Xx[j] = b.xelem (j, i);
+
+      for (octave_idx_type j = nr; j < S->m2; j++)
+        buf[j] = cs_complex_t (0.0, 0.0);
+
+      CXSPARSE_ZNAME (_ipvec) (S->pinv,
+                               reinterpret_cast<cs_complex_t *> (Xx),
+                               buf, nr);
+
+      for (volatile octave_idx_type j = 0; j < nc; j++)
+        {
+          octave_quit ();
+
+          CXSPARSE_ZNAME (_happly) (N->L, j, N->B[j], buf);
+        }
+
+      CXSPARSE_ZNAME (_usolve) (N->U, buf);
+      CXSPARSE_ZNAME (_ipvec) (S->q, buf,
+                               reinterpret_cast<cs_complex_t *> (Xx),
+                               nc);
+
+      for (octave_idx_type j = 0; j < nc; j++)
+        {
+          Complex tmp = Xx[j];
+
+          if (tmp != 0.0)
+            {
+              if (ii == x_nz)
+                {
+                  // Resize the sparse matrix
+                  octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
+                  sz = (sz > 10 ? sz : 10) + x_nz;
+                  x.change_capacity (sz);
+                  x_nz = sz;
+                }
+
+              x.xdata (ii) = tmp;
+              x.xridx (ii++) = j;
+            }
+        }
+
+      x.xcidx (i+1) = ii;
+    }
+
+  info = 0;
+
+  x.maybe_compress ();
+
+  return x;
+
+#else
+
+  octave_unused_parameter (b);
+
+  return SparseComplexMatrix ();
+
+#endif
+}
+
+template <>
+template <>
+SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::sparse_qr_rep::wide_solve<SparseComplexMatrix,
+          SparseComplexMatrix>
+          (const SparseComplexMatrix& b, octave_idx_type& info) const
+{
+  info = -1;
+
+#if defined (HAVE_CXSPARSE)
+
+  // These are swapped because the original matrix was transposed in
+  // sparse_qr<SparseComplexMatrix>::solve.
+
+  octave_idx_type nr = ncols;
+  octave_idx_type nc = nrows;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  SparseComplexMatrix x (nc, b_nc, b.nnz ());
+  x.xcidx (0) = 0;
+
+  volatile octave_idx_type x_nz = b.nnz ();
+  volatile octave_idx_type ii = 0;
+  volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
+
+  OCTAVE_LOCAL_BUFFER (Complex, Xx, (b_nr > nc ? b_nr : nc));
+  OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, nbuf);
+  OCTAVE_LOCAL_BUFFER (double, B, nr);
+
+  for (octave_idx_type i = 0; i < nr; i++)
+    B[i] = N->B[i];
+
+  for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
+    {
+      octave_quit ();
+
+      for (octave_idx_type j = 0; j < b_nr; j++)
+        Xx[j] = b.xelem (j, i);
+
+      for (octave_idx_type j = nr; j < nbuf; j++)
+        buf[j] = cs_complex_t (0.0, 0.0);
+
+      CXSPARSE_ZNAME (_pvec) (S->q, reinterpret_cast<cs_complex_t *>(Xx),
+                              buf, nr);
+      CXSPARSE_ZNAME (_utsolve) (N->U, buf);
+
+      for (volatile octave_idx_type j = nr-1; j >= 0; j--)
         {
           octave_quit ();
 
-          for (octave_idx_type j = 0; j < b_nr; j++)
+          CXSPARSE_ZNAME (_happly) (N->L, j, B[j], buf);
+        }
+
+      CXSPARSE_ZNAME (_pvec) (S->pinv, buf,
+                              reinterpret_cast<cs_complex_t *>(Xx), nc);
+
+      for (octave_idx_type j = 0; j < nc; j++)
+        {
+          Complex tmp = Xx[j];
+
+          if (tmp != 0.0)
             {
-              Complex c = b.xelem (j, i);
-              Xx[j] = c.real ();
-              Xz[j] = c.imag ();
-            }
-
-          for (octave_idx_type j = nr; j < nbuf; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_pvec) (S->q, Xx, buf, nr);
-          CXSPARSE_DNAME (_utsolve) (N->U, buf);
-
-          for (volatile octave_idx_type j = nr-1; j >= 0; j--)
-            {
-              octave_quit ();
-
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
+              if (ii == x_nz)
+                {
+                  // Resize the sparse matrix
+                  octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
+                  sz = (sz > 10 ? sz : 10) + x_nz;
+                  x.change_capacity (sz);
+                  x_nz = sz;
+                }
+
+              x.xdata (ii) = tmp;
+              x.xridx (ii++) = j;
             }
-
-          CXSPARSE_DNAME (_pvec) (S->pinv, buf, Xx, nc);
-
-          for (octave_idx_type j = nr; j < nbuf; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_pvec) (S->q, Xz, buf, nr);
-          CXSPARSE_DNAME (_utsolve) (N->U, buf);
-
-          for (volatile octave_idx_type j = nr-1; j >= 0; j--)
-            {
-              octave_quit ();
-
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_DNAME (_pvec) (S->pinv, buf, Xz, nc);
-
-          for (octave_idx_type j = 0; j < nc; j++)
-            vec[j+idx] = Complex (Xx[j], Xz[j]);
         }
 
-      info = 0;
-
-      return x;
+      x.xcidx (i+1) = ii;
+    }
+
+  info = 0;
+
+  x.maybe_compress ();
+
+  return x;
 
 #else
 
-      octave_unused_parameter (b);
-
-      return ComplexMatrix ();
+  octave_unused_parameter (b);
+
+  return SparseComplexMatrix ();
 
 #endif
+}
+
+template <typename SPARSE_T>
+sparse_qr<SPARSE_T>::sparse_qr (void)
+  : m_rep (new sparse_qr_rep (SPARSE_T (), 0))
+{ }
+
+template <typename SPARSE_T>
+sparse_qr<SPARSE_T>::sparse_qr (const SPARSE_T& a, int order)
+  : m_rep (new sparse_qr_rep (a, order))
+{ }
+
+template <typename SPARSE_T>
+bool
+sparse_qr<SPARSE_T>::ok (void) const
+{
+  return m_rep->ok ();
+}
+
+template <typename SPARSE_T>
+SPARSE_T
+sparse_qr<SPARSE_T>::V (void) const
+{
+  return m_rep->V ();
+}
+
+template <typename SPARSE_T>
+ColumnVector
+sparse_qr<SPARSE_T>::Pinv (void) const
+{
+  return m_rep->P ();
+}
+
+template <typename SPARSE_T>
+ColumnVector
+sparse_qr<SPARSE_T>::P (void) const
+{
+  return m_rep->P ();
+}
+
+template <typename SPARSE_T>
+ColumnVector
+sparse_qr<SPARSE_T>::E (void) const
+{
+  return m_rep->E();
+}
+
+
+template <typename SPARSE_T>
+SparseMatrix
+sparse_qr<SPARSE_T>::E_MAT (void) const
+{
+  ColumnVector perm = m_rep->E ();
+  octave_idx_type nrows = perm.rows ();
+  SparseMatrix ret (nrows, nrows, nrows);
+  for (octave_idx_type i = 0; i < nrows; i++)
+    ret(perm(i) - 1, i) = 1.0;
+  return ret;
+}
+
+
+template <typename SPARSE_T>
+SPARSE_T
+sparse_qr<SPARSE_T>::R (bool econ) const
+{
+  return m_rep->R (econ);
+}
+
+template <typename SPARSE_T>
+typename SPARSE_T::dense_matrix_type
+sparse_qr<SPARSE_T>::C (const typename SPARSE_T::dense_matrix_type& b,
+                        bool econ) const
+{
+  return m_rep->C (b, econ);
+}
+
+template <typename SPARSE_T>
+typename SPARSE_T::dense_matrix_type
+sparse_qr<SPARSE_T>::Q (bool econ) const
+{
+  return m_rep->Q (econ);
+}
+
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+//specializations of function min2norm_solve
+template <>
+template <>
+OCTAVE_API Matrix
+sparse_qr<SparseMatrix>::min2norm_solve<MArray<double>, Matrix>
+(const SparseMatrix& a, const MArray<double>& b,
+ octave_idx_type& info, int order)
+{
+  info = -1;
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type nc = a.cols ();
+  Matrix x (nc, b_nc);
+  cholmod_common cc;
+
+  cholmod_l_start (&cc);
+  cholmod_sparse A = ros2rcs (a);
+  cholmod_dense B = rod2rcd (b);
+  cholmod_dense *X;
+
+  X = SuiteSparseQR_min2norm<double> (order, SPQR_DEFAULT_TOL, &A, &B, &cc);
+  spqr_error_handler (&cc);
+
+  double *vec = x.fortran_vec ();
+  for (volatile octave_idx_type i = 0; i < nc * b_nc; i++)
+    vec[i] = reinterpret_cast<double *> (X->x)[i];
+
+  info = 0;
+  if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
+    {
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
     }
-
-    // Complex-valued matrices.
-
-    template <>
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::sparse_qr_rep
-    (const SparseComplexMatrix& a, int order)
-      : nrows (a.rows ()), ncols (a.columns ())
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-        , m_cc (), m_R (nullptr), m_E (nullptr), m_H (nullptr),
-          m_Htau (nullptr), m_HPinv (nullptr)
+  cholmod_l_finish (&cc);
+
+  return x;
+
+}
+
+template <>
+template <>
+OCTAVE_API SparseMatrix
+sparse_qr<SparseMatrix>::min2norm_solve<SparseMatrix, SparseMatrix>
+(const SparseMatrix& a, const SparseMatrix& b, octave_idx_type& info,
+ int order)
+{
+  info = -1;
+  SparseMatrix x;
+  cholmod_common cc;
+
+  cholmod_l_start (&cc);
+  cholmod_sparse A = ros2rcs (a);
+  cholmod_sparse B = ros2rcs (b);
+  cholmod_sparse *X;
+
+  X = SuiteSparseQR_min2norm<double> (order, SPQR_DEFAULT_TOL, &A, &B, &cc);
+  spqr_error_handler (&cc);
+
+  if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
+    {
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
+      delete [] reinterpret_cast<SuiteSparse_long *> (B.p);
+      delete [] reinterpret_cast<SuiteSparse_long *> (B.i);
+    }
+
+  x = rcs2ros (X);
+  cholmod_l_finish (&cc);
+  info = 0;
+
+  return x;
+
+}
+
+template <>
+template <>
+OCTAVE_API ComplexMatrix
+sparse_qr<SparseMatrix>::min2norm_solve<MArray<Complex>, ComplexMatrix>
+(const SparseMatrix& a, const MArray<Complex>& b,
+ octave_idx_type& info, int order)
+{
+  info = -1;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type nc = a.cols ();
+
+  ComplexMatrix x (nc, b_nc);
+
+  cholmod_common cc;
+
+  cholmod_l_start (&cc);
+
+  cholmod_sparse *A = ros2ccs (a, &cc);
+  cholmod_dense B = cod2ccd (b);
+  cholmod_dense *X;
+
+  X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, A, &B, &cc);
+  spqr_error_handler (&cc);
+
+  Complex *vec = x.fortran_vec ();
+  for (volatile octave_idx_type i = 0; i < nc * b_nc; i++)
+    vec[i] = reinterpret_cast<Complex *> (X->x)[i];
+
+  cholmod_l_free_sparse (&A, &cc);
+  cholmod_l_finish (&cc);
+
+  info = 0;
+
+  return x;
+
+}
+
+template <>
+template <>
+OCTAVE_API SparseComplexMatrix
+sparse_qr<SparseMatrix>::min2norm_solve<SparseComplexMatrix,
+          SparseComplexMatrix>
+          (const SparseMatrix& a, const SparseComplexMatrix& b,
+           octave_idx_type& info, int order)
+{
+  info = -1;
+
+  cholmod_common cc;
+
+  cholmod_l_start (&cc);
+
+  cholmod_sparse *A = ros2ccs (a, &cc);
+  cholmod_sparse B = cos2ccs (b);
+  cholmod_sparse *X;
+
+  X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, A, &B, &cc);
+  spqr_error_handler (&cc);
+
+  cholmod_l_free_sparse (&A, &cc);
+  if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
     {
-      octave_idx_type nr = a.rows ();
-      octave_idx_type nc = a.cols ();
-
-      if (nr < 0 || nc < 0)
-        (*current_liboctave_error_handler)
-          ("matrix dimension with negative size");
-
-      if (order < 0 || order > 9)
-        (*current_liboctave_error_handler)
-          ("ordering %d is not supported by SPQR", order);
-
-      cholmod_l_start (&m_cc);
-      cholmod_sparse A = cos2ccs (a);
-
-      SuiteSparseQR<Complex> (order, static_cast<double> (SPQR_DEFAULT_TOL),
-                              static_cast<SuiteSparse_long> (A.nrow),
-                              &A, &m_R, &m_E, &m_H,
-                              &m_HPinv, &m_Htau, &m_cc);
-      spqr_error_handler (&m_cc);
-
-      if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
-        {
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
-        }
+      delete [] reinterpret_cast<SuiteSparse_long *> (B.p);
+      delete [] reinterpret_cast<SuiteSparse_long *> (B.i);
+    }
+  cholmod_l_finish (&cc);
+
+  SparseComplexMatrix ret = ccs2cos(X);
+
+  info = 0;
+
+  return ret;
+
+}
+
+template <>
+template <>
+OCTAVE_API ComplexMatrix
+sparse_qr<SparseComplexMatrix>::min2norm_solve<MArray<Complex>,
+          ComplexMatrix>
+          (const SparseComplexMatrix& a, const MArray<Complex>& b,
+           octave_idx_type& info, int order)
+{
+  info = -1;
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type nc = a.cols ();
+  ComplexMatrix x (nc, b_nc);
+
+  cholmod_common cc;
+
+  cholmod_l_start (&cc);
+
+  cholmod_sparse A = cos2ccs (a);
+  cholmod_dense B = cod2ccd (b);
+  cholmod_dense *X;
+
+  X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, &A, &B, &cc);
+  spqr_error_handler (&cc);
+
+  Complex *vec = x.fortran_vec ();
+  for (volatile octave_idx_type i = 0; i < nc * b_nc; i++)
+    vec[i] = reinterpret_cast<Complex *> (X->x)[i];
+
+  if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
+    {
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
+    }
+  cholmod_l_finish (&cc);
+
+  info = 0;
+
+  return x;
+
+}
+
+template <>
+template <>
+OCTAVE_API ComplexMatrix
+sparse_qr<SparseComplexMatrix>::min2norm_solve<MArray<double>,
+          ComplexMatrix>
+          (const SparseComplexMatrix& a, const MArray<double>& b,
+           octave_idx_type& info, int order)
+{
+  info = -1;
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type nc = a.cols ();
+  ComplexMatrix x (nc, b_nc);
+
+  cholmod_common cc;
+
+  cholmod_l_start (&cc);
+
+  cholmod_sparse A = cos2ccs (a);
+  cholmod_dense *B = rod2ccd (b, &cc);
+  cholmod_dense *X;
+
+  X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, &A, B, &cc);
+  spqr_error_handler (&cc);
+
+  Complex *vec = x.fortran_vec ();
+
+  for (volatile octave_idx_type i = 0; i < nc * b_nc; i++)
+    vec[i] = reinterpret_cast<Complex *> (X->x)[i];
+
+  if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
+    {
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
     }
+  cholmod_l_free_dense (&B, &cc);
+  cholmod_l_finish (&cc);
+
+  info = 0;
+
+  return x;
+
+}
+
+template <>
+template <>
+OCTAVE_API SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::min2norm_solve<SparseComplexMatrix,
+          SparseComplexMatrix>
+          (const SparseComplexMatrix& a, const SparseComplexMatrix& b,
+           octave_idx_type& info, int order)
+{
+  info = -1;
+
+  cholmod_common cc;
+
+  cholmod_l_start (&cc);
+
+  cholmod_sparse A = cos2ccs (a);
+  cholmod_sparse B = cos2ccs (b);
+  cholmod_sparse *X;
+
+  X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, &A, &B, &cc);
+  spqr_error_handler (&cc);
+
+  if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
+    {
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
+      delete [] reinterpret_cast<SuiteSparse_long *> (B.p);
+      delete [] reinterpret_cast<SuiteSparse_long *> (B.i);
+    }
+  cholmod_l_finish (&cc);
+
+  info = 0;
+
+  return ccs2cos (X);
+
+}
+
+template <>
+template <>
+OCTAVE_API SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::min2norm_solve<SparseMatrix,
+          SparseComplexMatrix>
+          (const SparseComplexMatrix& a, const SparseMatrix& b,
+           octave_idx_type& info, int order)
+{
+  info = -1;
+
+  cholmod_common cc;
+
+  cholmod_l_start (&cc);
+
+  cholmod_sparse A = cos2ccs (a);
+  cholmod_sparse *B = ros2ccs (b, &cc);
+  cholmod_sparse *X;
+
+  X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, &A, B, &cc);
+  spqr_error_handler (&cc);
+
+  SparseComplexMatrix ret = ccs2cos(X);
+
+  if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
+    {
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
+      delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
+    }
+  cholmod_l_free_sparse (&B, &cc);
+  cholmod_l_finish (&cc);
+
+  info = 0;
+
+  return ret;
+
+}
+#endif
+
+// FIXME: Why is the "order" of the QR calculation as used in the
+// CXSparse function sqr 3 for real matrices and 2 for complex?  These
+// values seem to be required but there was no explanation in David
+// Bateman's original code.
+
+template <typename SPARSE_T>
+class
+cxsparse_defaults
+{
+public:
+  enum { order = -1 };
+};
+
+template <>
+class
+cxsparse_defaults<SparseMatrix>
+{
+public:
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+  enum { order = SPQR_ORDERING_DEFAULT };
+#elif defined (HAVE_CXSPARSE)
+  enum { order = 3 };
+#endif
+};
+
+template <>
+class
+cxsparse_defaults<SparseComplexMatrix>
+{
+public:
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+  enum { order = SPQR_ORDERING_DEFAULT };
+#elif defined (HAVE_CXSPARSE)
+  enum { order = 2 };
+#endif
+};
+
+template <typename SPARSE_T>
+template <typename RHS_T, typename RET_T>
+RET_T
+sparse_qr<SPARSE_T>::solve (const SPARSE_T& a, const RHS_T& b,
+                            octave_idx_type& info)
+{
+#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
+
+  info = -1;
+
+  octave_idx_type nr = a.rows ();
+  octave_idx_type nc = a.cols ();
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  int order = cxsparse_defaults<SPARSE_T>::order;
+
+  if (nr < 0 || nc < 0 || b_nc < 0 || b_nr < 0)
+    (*current_liboctave_error_handler)
+      ("matrix dimension with negative size");
+
+  if ( nr != b_nr)
+    (*current_liboctave_error_handler)
+      ("matrix dimension mismatch in solution of minimum norm problem");
+
+  info = 0;
+
+  return min2norm_solve<RHS_T, RET_T> (a, b, info, order);
 
 #elif defined (HAVE_CXSPARSE)
-        , S (nullptr), N (nullptr)
+
+  info = -1;
+
+  octave_idx_type nr = a.rows ();
+  octave_idx_type nc = a.cols ();
+
+  octave_idx_type b_nc = b.cols ();
+  octave_idx_type b_nr = b.rows ();
+
+  int order = cxsparse_defaults<SPARSE_T>::order;
+
+  if (nr < 0 || nc < 0 || nr != b_nr)
+    (*current_liboctave_error_handler)
+      ("matrix dimension mismatch in solution of minimum norm problem");
+
+  if (nr == 0 || nc == 0 || b_nc == 0)
     {
-      CXSPARSE_ZNAME () A;
-
-      A.nzmax = a.nnz ();
-      A.m = nrows;
-      A.n = ncols;
-      // Cast away const on A, with full knowledge that CSparse won't touch it
-      // Prevents the methods below making a copy of the data.
-      A.p = const_cast<suitesparse_integer *>
-              (to_suitesparse_intptr (a.cidx ()));
-      A.i = const_cast<suitesparse_integer *>
-              (to_suitesparse_intptr (a.ridx ()));
-      A.x = const_cast<cs_complex_t *>
-              (reinterpret_cast<const cs_complex_t *> (a.data ()));
-      A.nz = -1;
-
-      S = CXSPARSE_ZNAME (_sqr) (order, &A, 1);
-      N = CXSPARSE_ZNAME (_qr) (&A, S);
-
-      if (! N)
-        (*current_liboctave_error_handler)
-          ("sparse_qr: sparse matrix QR factorization filled");
-
+      info = 0;
+
+      return RET_T (nc, b_nc, 0.0);
+    }
+  else if (nr >= nc)
+    {
+      sparse_qr<SPARSE_T> q (a, order);
+
+      return q.ok () ? q.tall_solve<RHS_T, RET_T> (b, info) : RET_T ();
+    }
+  else
+    {
+      sparse_qr<SPARSE_T> q (a.hermitian (), order);
+
+      return q.ok () ? q.wide_solve<RHS_T, RET_T> (b, info) : RET_T ();
     }
 
 #else
 
-    {
-      octave_unused_parameter (order);
-
-      (*current_liboctave_error_handler)
-        ("sparse_qr: support for CXSparse was unavailable or disabled when liboctave was built");
-    }
-
-#endif
-
-    template <>
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::~sparse_qr_rep (void)
-    {
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-
-      cholmod_l_free_sparse (&m_R, &m_cc);
-      cholmod_l_free_sparse (&m_H, &m_cc);
-      cholmod_l_free_dense (&m_Htau, &m_cc);
-      free (m_E);  // FIXME: use cholmod_l_free
-      free (m_HPinv);
-      cholmod_l_finish (&m_cc);
-
-#elif defined (HAVE_CXSPARSE)
-
-      CXSPARSE_ZNAME (_sfree) (S);
-      CXSPARSE_ZNAME (_nfree) (N);
-
-#endif
-    }
-
-    template <>
-    SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::V (void) const
-    {
-#if defined (HAVE_CXSPARSE)
-      // Drop zeros from V and sort
-      // FIXME: Is the double transpose to sort necessary?
-
-      CXSPARSE_ZNAME (_dropzeros) (N->L);
-      CXSPARSE_ZNAME () *D = CXSPARSE_ZNAME (_transpose) (N->L, 1);
-      CXSPARSE_ZNAME (_spfree) (N->L);
-      N->L = CXSPARSE_ZNAME (_transpose) (D, 1);
-      CXSPARSE_ZNAME (_spfree) (D);
-
-      octave_idx_type nc = N->L->n;
-      octave_idx_type nz = N->L->nzmax;
-      SparseComplexMatrix ret (N->L->m, nc, nz);
-
-      for (octave_idx_type j = 0; j < nc+1; j++)
-        ret.xcidx (j) = N->L->p[j];
-
-      for (octave_idx_type j = 0; j < nz; j++)
-        {
-          ret.xridx (j) = N->L->i[j];
-          ret.xdata (j) = reinterpret_cast<Complex *> (N->L->x)[j];
-        }
-
-      return ret;
-
-#else
-
-      return SparseComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::R (bool econ) const
-    {
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-
-      octave_idx_type nr = from_size_t (m_R->nrow);
-      octave_idx_type nc = from_size_t (m_R->ncol);
-      octave_idx_type nz = from_size_t (m_R->nzmax);
-
-      // FIXME: Does this work if econ = true?
-      SparseComplexMatrix ret ((econ ? (nc > nr ? nr : nc) : nr), nc, nz);
-      SuiteSparse_long *Rp = reinterpret_cast<SuiteSparse_long *> (m_R->p);
-      SuiteSparse_long *Ri = reinterpret_cast<SuiteSparse_long *> (m_R->i);
-
-      for (octave_idx_type j = 0; j < nc + 1; j++)
-        ret.xcidx (j) = from_suitesparse_long (Rp[j]);
-
-      for (octave_idx_type j = 0; j < nz; j++)
-        {
-          ret.xridx (j) = from_suitesparse_long (Ri[j]);
-          ret.xdata (j) = (reinterpret_cast<Complex *> (m_R->x))[j];
-        }
-
-      return ret;
-
-#elif defined (HAVE_CXSPARSE)
-
-      // Drop zeros from R and sort
-      // FIXME: Is the double transpose to sort necessary?
-
-      CXSPARSE_ZNAME (_dropzeros) (N->U);
-      CXSPARSE_ZNAME () *D = CXSPARSE_ZNAME (_transpose) (N->U, 1);
-      CXSPARSE_ZNAME (_spfree) (N->U);
-      N->U = CXSPARSE_ZNAME (_transpose) (D, 1);
-      CXSPARSE_ZNAME (_spfree) (D);
-
-      octave_idx_type nc = N->U->n;
-      octave_idx_type nz = N->U->nzmax;
-
-      SparseComplexMatrix ret ((econ ? (nc > nrows ? nrows : nc) : nrows),
-                               nc, nz);
-
-      for (octave_idx_type j = 0; j < nc+1; j++)
-        ret.xcidx (j) = N->U->p[j];
-
-      for (octave_idx_type j = 0; j < nz; j++)
-        {
-          ret.xridx (j) = N->U->i[j];
-          ret.xdata (j) = reinterpret_cast<Complex *>(N->U->x)[j];
-        }
-
-      return ret;
-
-#else
-
-      octave_unused_parameter (econ);
-
-      return SparseComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::C
-    (const ComplexMatrix& b, bool econ)
-    {
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-
-      // FIXME: not tested
-      octave_idx_type nr = (econ
-                            ? (ncols > nrows ? nrows : ncols)
-                            : nrows);
-      octave_idx_type b_nr = b.rows ();
-      octave_idx_type b_nc = b.cols ();
-      ComplexMatrix ret (nr, b_nc);
-
-      if (nrows != b_nr)
-        (*current_liboctave_error_handler) ("matrix dimension mismatch");
-
-      if (b_nc < 0 || b_nr < 0)
-        (*current_liboctave_error_handler)
-          ("matrix dimension with negative size");
-
-      cholmod_dense *QTB;  // Q' * B
-      cholmod_dense B = cod2ccd (b);
-
-      QTB = SuiteSparseQR_qmult<Complex> (SPQR_QTX, m_H, m_Htau, m_HPinv, &B,
-                                          &m_cc);
-      spqr_error_handler (&m_cc);
-
-      // copy QTB into ret
-      Complex *QTB_x = reinterpret_cast<Complex *> (QTB->x);
-      Complex *ret_vec = reinterpret_cast<Complex *> (ret.fortran_vec ());
-      for (octave_idx_type j = 0; j < b_nc; j++)
-        for (octave_idx_type i = 0; i < nr; i++)
-          ret_vec[j * nr + i] = QTB_x[j * b_nr + i];
-
-      cholmod_l_free_dense (&QTB, &m_cc);
-
-      return ret;
-
-#elif defined (HAVE_CXSPARSE)
-
-      if (econ)
-        (*current_liboctave_error_handler)
-          ("sparse-qr: economy mode with CXSparse not supported");
-
-      octave_idx_type b_nr = b.rows ();
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type nc = N->L->n;
-      octave_idx_type nr = nrows;
-      const cs_complex_t *bvec
-        = reinterpret_cast<const cs_complex_t *> (b.data ());
-      ComplexMatrix ret (b_nr, b_nc);
-      Complex *vec = ret.fortran_vec ();
-
-      if (nr < 0 || nc < 0 || nr != b_nr)
-        (*current_liboctave_error_handler) ("matrix dimension mismatch");
-
-      if (nr == 0 || nc == 0 || b_nc == 0)
-        ret = ComplexMatrix (nc, b_nc, Complex (0.0, 0.0));
-      else
-        {
-          OCTAVE_LOCAL_BUFFER (Complex, buf, S->m2);
-
-          for (volatile octave_idx_type j = 0, idx = 0;
-               j < b_nc;
-               j++, idx += b_nr)
-            {
-              octave_quit ();
-
-              volatile octave_idx_type nm = (nr < nc ? nr : nc);
-
-              CXSPARSE_ZNAME (_ipvec) (S->pinv, bvec + idx,
-                                       reinterpret_cast<cs_complex_t *> (buf),
-                                       b_nr);
-
-              for (volatile octave_idx_type i = 0; i < nm; i++)
-                {
-                  octave_quit ();
-
-                  CXSPARSE_ZNAME (_happly) (N->L, i, N->B[i],
-                                            reinterpret_cast<cs_complex_t *> (buf));
-                }
-
-              for (octave_idx_type i = 0; i < b_nr; i++)
-                vec[i+idx] = buf[i];
-            }
-        }
-
-      return ret;
-
-#else
-
-      octave_unused_parameter (b);
-      octave_unused_parameter (econ);
-
-      return ComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::Q (bool econ)
-    {
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-
-      octave_idx_type nc = (econ
-                            ? (ncols > nrows ? nrows : ncols)
-                            : nrows);
-      ComplexMatrix ret (nrows, nc);
-      cholmod_dense *q;
-
-      // I is nrows x nrows identity matrix
-      cholmod_dense *I
-        = reinterpret_cast<cholmod_dense *>
-            (cholmod_l_allocate_dense (nrows, nrows, nrows, CHOLMOD_COMPLEX, &m_cc));
-
-      for (octave_idx_type i = 0; i < nrows * nrows; i++)
-        (reinterpret_cast<Complex *> (I->x))[i] = 0.0;
-
-      for (octave_idx_type i = 0; i < nrows; i++)
-        (reinterpret_cast<Complex *> (I->x))[i * nrows + i] = 1.0;
-
-      q = SuiteSparseQR_qmult<Complex> (SPQR_QX, m_H, m_Htau, m_HPinv, I,
-                                        &m_cc);
-      spqr_error_handler (&m_cc);
-
-      Complex *q_x = reinterpret_cast<Complex *> (q->x);
-      Complex *ret_vec = const_cast<Complex *> (ret.fortran_vec ());
-
-      for (octave_idx_type j = 0; j < nc; j++)
-        for (octave_idx_type i = 0; i < nrows; i++)
-          ret_vec[j * nrows + i] = q_x[j * nrows + i];
-
-      cholmod_l_free_dense (&q, &m_cc);
-      cholmod_l_free_dense (&I, &m_cc);
-
-      return ret;
-
-#elif defined (HAVE_CXSPARSE)
-
-      if (econ)
-        (*current_liboctave_error_handler)
-          ("sparse-qr: economy mode with CXSparse not supported");
-
-      octave_idx_type nc = N->L->n;
-      octave_idx_type nr = nrows;
-      ComplexMatrix ret (nr, nr);
-      Complex *vec = ret.fortran_vec ();
-
-      if (nr < 0 || nc < 0)
-        (*current_liboctave_error_handler) ("matrix dimension mismatch");
-
-      if (nr == 0 || nc == 0)
-        ret = ComplexMatrix (nc, nr, Complex (0.0, 0.0));
-      else
-        {
-          OCTAVE_LOCAL_BUFFER (cs_complex_t, bvec, nr);
-
-          for (octave_idx_type i = 0; i < nr; i++)
-            bvec[i] = cs_complex_t (0.0, 0.0);
-
-          OCTAVE_LOCAL_BUFFER (Complex, buf, S->m2);
-
-          for (volatile octave_idx_type j = 0, idx = 0; j < nr; j++, idx+=nr)
-            {
-              octave_quit ();
-
-              bvec[j] = cs_complex_t (1.0, 0.0);
-
-              volatile octave_idx_type nm = (nr < nc ? nr : nc);
-
-              CXSPARSE_ZNAME (_ipvec) (S->pinv, bvec,
-                                       reinterpret_cast<cs_complex_t *> (buf),
-                                       nr);
-
-              for (volatile octave_idx_type i = 0; i < nm; i++)
-                {
-                  octave_quit ();
-
-                  CXSPARSE_ZNAME (_happly) (N->L, i, N->B[i],
-                                            reinterpret_cast<cs_complex_t *> (buf));
-                }
-
-              for (octave_idx_type i = 0; i < nr; i++)
-                vec[i+idx] = buf[i];
-
-              bvec[j] = cs_complex_t (0.0, 0.0);
-            }
-        }
-
-      return ret.hermitian ();
-
-#else
-
-      octave_unused_parameter (econ);
-
-      return ComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    template <>
-    SparseComplexMatrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::tall_solve<SparseComplexMatrix,
-                                                       SparseComplexMatrix>
-      (const SparseComplexMatrix& b, octave_idx_type& info)
-    {
-      info = -1;
-
-#if defined (HAVE_CXSPARSE)
-
-      octave_idx_type nr = nrows;
-      octave_idx_type nc = ncols;
-
-      octave_idx_type b_nr = b.rows ();
-      octave_idx_type b_nc = b.cols ();
-
-      SparseComplexMatrix x (nc, b_nc, b.nnz ());
-      x.xcidx (0) = 0;
-
-      volatile octave_idx_type x_nz = b.nnz ();
-      volatile octave_idx_type ii = 0;
-
-      OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (double, Xz, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (double, buf, S->m2);
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = 0; j < b_nr; j++)
-            {
-              Complex c = b.xelem (j, i);
-              Xx[j] = c.real ();
-              Xz[j] = c.imag ();
-            }
-
-          for (octave_idx_type j = nr; j < S->m2; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_ipvec) (S->pinv, Xx, buf, nr);
-
-          for (volatile octave_idx_type j = 0; j < nc; j++)
-            {
-              octave_quit ();
-
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_DNAME (_usolve) (N->U, buf);
-          CXSPARSE_DNAME (_ipvec) (S->q, buf, Xx, nc);
-
-          for (octave_idx_type j = nr; j < S->m2; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_ipvec) (S->pinv, Xz, buf, nr);
-
-          for (volatile octave_idx_type j = 0; j < nc; j++)
-            {
-              octave_quit ();
-
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_DNAME (_usolve) (N->U, buf);
-          CXSPARSE_DNAME (_ipvec) (S->q, buf, Xz, nc);
-
-          for (octave_idx_type j = 0; j < nc; j++)
-            {
-              Complex tmp = Complex (Xx[j], Xz[j]);
-
-              if (tmp != 0.0)
-                {
-                  if (ii == x_nz)
-                    {
-                      // Resize the sparse matrix
-                      octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
-                      sz = (sz > 10 ? sz : 10) + x_nz;
-                      x.change_capacity (sz);
-                      x_nz = sz;
-                    }
-
-                  x.xdata (ii) = tmp;
-                  x.xridx (ii++) = j;
-                }
-            }
-
-          x.xcidx (i+1) = ii;
-        }
-
-      info = 0;
-
-      return x;
-
-#else
-
-      octave_unused_parameter (b);
-
-      return SparseComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    template <>
-    SparseComplexMatrix
-    sparse_qr<SparseMatrix>::sparse_qr_rep::wide_solve<SparseComplexMatrix,
-                                                       SparseComplexMatrix>
-      (const SparseComplexMatrix& b, octave_idx_type& info) const
-    {
-      info = -1;
-
-#if defined (HAVE_CXSPARSE)
-
-      // These are swapped because the original matrix was transposed in
-      // sparse_qr<SparseMatrix>::solve.
-
-      octave_idx_type nr = ncols;
-      octave_idx_type nc = nrows;
-
-      octave_idx_type b_nr = b.rows ();
-      octave_idx_type b_nc = b.cols ();
-
-      SparseComplexMatrix x (nc, b_nc, b.nnz ());
-      x.xcidx (0) = 0;
-
-      volatile octave_idx_type x_nz = b.nnz ();
-      volatile octave_idx_type ii = 0;
-      volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
-
-      OCTAVE_LOCAL_BUFFER (double, Xx, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (double, Xz, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (double, buf, nbuf);
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = 0; j < b_nr; j++)
-            {
-              Complex c = b.xelem (j, i);
-              Xx[j] = c.real ();
-              Xz[j] = c.imag ();
-            }
-
-          for (octave_idx_type j = nr; j < nbuf; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_pvec) (S->q, Xx, buf, nr);
-          CXSPARSE_DNAME (_utsolve) (N->U, buf);
-
-          for (volatile octave_idx_type j = nr-1; j >= 0; j--)
-            {
-              octave_quit ();
-
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_DNAME (_pvec) (S->pinv, buf, Xx, nc);
-
-          for (octave_idx_type j = nr; j < nbuf; j++)
-            buf[j] = 0.;
-
-          CXSPARSE_DNAME (_pvec) (S->q, Xz, buf, nr);
-          CXSPARSE_DNAME (_utsolve) (N->U, buf);
-
-          for (volatile octave_idx_type j = nr-1; j >= 0; j--)
-            {
-              octave_quit ();
-
-              CXSPARSE_DNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_DNAME (_pvec) (S->pinv, buf, Xz, nc);
-
-          for (octave_idx_type j = 0; j < nc; j++)
-            {
-              Complex tmp = Complex (Xx[j], Xz[j]);
-
-              if (tmp != 0.0)
-                {
-                  if (ii == x_nz)
-                    {
-                      // Resize the sparse matrix
-                      octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
-                      sz = (sz > 10 ? sz : 10) + x_nz;
-                      x.change_capacity (sz);
-                      x_nz = sz;
-                    }
-
-                  x.xdata (ii) = tmp;
-                  x.xridx (ii++) = j;
-                }
-            }
-
-          x.xcidx (i+1) = ii;
-        }
-
-      info = 0;
-
-      x.maybe_compress ();
-
-      return x;
-
-#else
-
-      octave_unused_parameter (b);
-
-      return SparseComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    template <>
-    ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::tall_solve<MArray<double>,
-                                                              ComplexMatrix>
-      (const MArray<double>& b, octave_idx_type& info)
-    {
-      info = -1;
-
-#if defined (HAVE_CXSPARSE)
-
-      octave_idx_type nr = nrows;
-      octave_idx_type nc = ncols;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      ComplexMatrix x (nc, b_nc);
-      cs_complex_t *vec = reinterpret_cast<cs_complex_t *> (x.fortran_vec ());
-
-      OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, S->m2);
-      OCTAVE_LOCAL_BUFFER (Complex, Xx, b_nr);
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = 0; j < b_nr; j++)
-            Xx[j] = b.xelem (j, i);
-
-          for (octave_idx_type j = nr; j < S->m2; j++)
-            buf[j] = cs_complex_t (0.0, 0.0);
-
-          CXSPARSE_ZNAME (_ipvec) (S->pinv,
-                                   reinterpret_cast<cs_complex_t *>(Xx),
-                                   buf, nr);
-
-          for (volatile octave_idx_type j = 0; j < nc; j++)
-            {
-              octave_quit ();
-
-              CXSPARSE_ZNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_ZNAME (_usolve) (N->U, buf);
-          CXSPARSE_ZNAME (_ipvec) (S->q, buf, vec + idx, nc);
-        }
-
-      info = 0;
-
-      return x;
-
-#else
-
-      octave_unused_parameter (b);
-
-      return ComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    template <>
-    ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::wide_solve<MArray<double>,
-                                                              ComplexMatrix>
-      (const MArray<double>& b, octave_idx_type& info) const
-    {
-      info = -1;
-
-#if defined (HAVE_CXSPARSE)
-
-      // These are swapped because the original matrix was transposed in
-      // sparse_qr<SparseComplexMatrix>::solve.
-
-      octave_idx_type nr = ncols;
-      octave_idx_type nc = nrows;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      ComplexMatrix x (nc, b_nc);
-      cs_complex_t *vec = reinterpret_cast<cs_complex_t *> (x.fortran_vec ());
-
-      volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
-
-      OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, nbuf);
-      OCTAVE_LOCAL_BUFFER (Complex, Xx, b_nr);
-      OCTAVE_LOCAL_BUFFER (double, B, nr);
-
-      for (octave_idx_type i = 0; i < nr; i++)
-        B[i] = N->B[i];
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = 0; j < b_nr; j++)
-            Xx[j] = b.xelem (j, i);
-
-          for (octave_idx_type j = nr; j < nbuf; j++)
-            buf[j] = cs_complex_t (0.0, 0.0);
-
-          CXSPARSE_ZNAME (_pvec) (S->q, reinterpret_cast<cs_complex_t *> (Xx),
-                                  buf, nr);
-          CXSPARSE_ZNAME (_utsolve) (N->U, buf);
-
-          for (volatile octave_idx_type j = nr-1; j >= 0; j--)
-            {
-              octave_quit ();
-
-              CXSPARSE_ZNAME (_happly) (N->L, j, B[j], buf);
-            }
-
-          CXSPARSE_ZNAME (_pvec) (S->pinv, buf, vec + idx, nc);
-        }
-
-      info = 0;
-
-      return x;
-
-#else
-
-      octave_unused_parameter (b);
-
-      return ComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    template <>
-    SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::tall_solve<SparseMatrix,
-                                                              SparseComplexMatrix>
-      (const SparseMatrix& b, octave_idx_type& info)
-    {
-      info = -1;
-
-#if defined (HAVE_CXSPARSE)
-
-      octave_idx_type nr = nrows;
-      octave_idx_type nc = ncols;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      SparseComplexMatrix x (nc, b_nc, b.nnz ());
-      x.xcidx (0) = 0;
-
-      volatile octave_idx_type x_nz = b.nnz ();
-      volatile octave_idx_type ii = 0;
-
-      OCTAVE_LOCAL_BUFFER (Complex, Xx, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, S->m2);
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = 0; j < b_nr; j++)
-            Xx[j] = b.xelem (j, i);
-
-          for (octave_idx_type j = nr; j < S->m2; j++)
-            buf[j] = cs_complex_t (0.0, 0.0);
-
-          CXSPARSE_ZNAME (_ipvec) (S->pinv,
-                                   reinterpret_cast<cs_complex_t *> (Xx),
-                                   buf, nr);
-
-          for (volatile octave_idx_type j = 0; j < nc; j++)
-            {
-              octave_quit ();
-
-              CXSPARSE_ZNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_ZNAME (_usolve) (N->U, buf);
-          CXSPARSE_ZNAME (_ipvec) (S->q, buf,
-                                   reinterpret_cast<cs_complex_t *> (Xx),
-                                   nc);
-
-          for (octave_idx_type j = 0; j < nc; j++)
-            {
-              Complex tmp = Xx[j];
-
-              if (tmp != 0.0)
-                {
-                  if (ii == x_nz)
-                    {
-                      // Resize the sparse matrix
-                      octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
-                      sz = (sz > 10 ? sz : 10) + x_nz;
-                      x.change_capacity (sz);
-                      x_nz = sz;
-                    }
-
-                  x.xdata (ii) = tmp;
-                  x.xridx (ii++) = j;
-                }
-            }
-
-          x.xcidx (i+1) = ii;
-        }
-
-      info = 0;
-
-      x.maybe_compress ();
-
-      return x;
-
-#else
-
-      octave_unused_parameter (b);
-
-      return SparseComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    template <>
-    SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::wide_solve<SparseMatrix,
-                                                              SparseComplexMatrix>
-      (const SparseMatrix& b, octave_idx_type& info) const
-    {
-      info = -1;
-
-#if defined (HAVE_CXSPARSE)
-
-      // These are swapped because the original matrix was transposed in
-      // sparse_qr<SparseComplexMatrix>::solve.
-
-      octave_idx_type nr = ncols;
-      octave_idx_type nc = nrows;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      SparseComplexMatrix x (nc, b_nc, b.nnz ());
-      x.xcidx (0) = 0;
-
-      volatile octave_idx_type x_nz = b.nnz ();
-      volatile octave_idx_type ii = 0;
-      volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
-
-      OCTAVE_LOCAL_BUFFER (Complex, Xx, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, nbuf);
-      OCTAVE_LOCAL_BUFFER (double, B, nr);
-
-      for (octave_idx_type i = 0; i < nr; i++)
-        B[i] = N->B[i];
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = 0; j < b_nr; j++)
-            Xx[j] = b.xelem (j, i);
-
-          for (octave_idx_type j = nr; j < nbuf; j++)
-            buf[j] = cs_complex_t (0.0, 0.0);
-
-          CXSPARSE_ZNAME (_pvec) (S->q,
-                                  reinterpret_cast<cs_complex_t *> (Xx),
-                                  buf, nr);
-          CXSPARSE_ZNAME (_utsolve) (N->U, buf);
-
-          for (volatile octave_idx_type j = nr-1; j >= 0; j--)
-            {
-              octave_quit ();
-
-              CXSPARSE_ZNAME (_happly) (N->L, j, B[j], buf);
-            }
-
-          CXSPARSE_ZNAME (_pvec) (S->pinv, buf,
-                                  reinterpret_cast<cs_complex_t *> (Xx),
-                                  nc);
-
-          for (octave_idx_type j = 0; j < nc; j++)
-            {
-              Complex tmp = Xx[j];
-
-              if (tmp != 0.0)
-                {
-                  if (ii == x_nz)
-                    {
-                      // Resize the sparse matrix
-                      octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
-                      sz = (sz > 10 ? sz : 10) + x_nz;
-                      x.change_capacity (sz);
-                      x_nz = sz;
-                    }
-
-                  x.xdata (ii) = tmp;
-                  x.xridx (ii++) = j;
-                }
-            }
-
-          x.xcidx (i+1) = ii;
-        }
-
-      info = 0;
-
-      x.maybe_compress ();
-
-      return x;
-
-#else
-
-      octave_unused_parameter (b);
-
-      return SparseComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    template <>
-    ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::tall_solve<MArray<Complex>,
-                                                              ComplexMatrix>
-      (const MArray<Complex>& b, octave_idx_type& info)
-    {
-      info = -1;
-
-#if defined (HAVE_CXSPARSE)
-
-      octave_idx_type nr = nrows;
-      octave_idx_type nc = ncols;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      const cs_complex_t *bvec = reinterpret_cast<const cs_complex_t *>
-                                 (b.data ());
-
-      ComplexMatrix x (nc, b_nc);
-      cs_complex_t *vec = reinterpret_cast<cs_complex_t *>
-                          (x.fortran_vec ());
-
-      OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, S->m2);
-
-      for (volatile octave_idx_type i = 0, idx = 0, bidx = 0; i < b_nc;
-           i++, idx+=nc, bidx+=b_nr)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = nr; j < S->m2; j++)
-            buf[j] = cs_complex_t (0.0, 0.0);
-
-          CXSPARSE_ZNAME (_ipvec) (S->pinv, bvec + bidx, buf, nr);
-
-          for (volatile octave_idx_type j = 0; j < nc; j++)
-            {
-              octave_quit ();
-
-              CXSPARSE_ZNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_ZNAME (_usolve) (N->U, buf);
-          CXSPARSE_ZNAME (_ipvec) (S->q, buf, vec + idx, nc);
-        }
-
-      info = 0;
-
-      return x;
-
-#else
-
-      octave_unused_parameter (b);
-
-      return ComplexMatrix ();
+  octave_unused_parameter (a);
+  octave_unused_parameter (b);
+  octave_unused_parameter (info);
+
+  return RET_T ();
 
 #endif
-    }
-
-    template <>
-    template <>
-    ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::wide_solve<MArray<Complex>,
-                                                              ComplexMatrix>
-      (const MArray<Complex>& b, octave_idx_type& info) const
-    {
-      info = -1;
-
-#if defined (HAVE_CXSPARSE)
-
-      // These are swapped because the original matrix was transposed in
-      // sparse_qr<SparseComplexMatrix>::solve.
-
-      octave_idx_type nr = ncols;
-      octave_idx_type nc = nrows;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      const cs_complex_t *bvec = reinterpret_cast<const cs_complex_t *>
-                                 (b.data ());
-
-      ComplexMatrix x (nc, b_nc);
-      cs_complex_t *vec = reinterpret_cast<cs_complex_t *> (x.fortran_vec ());
-
-      volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
-
-      OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, nbuf);
-      OCTAVE_LOCAL_BUFFER (double, B, nr);
-
-      for (octave_idx_type i = 0; i < nr; i++)
-        B[i] = N->B[i];
-
-      for (volatile octave_idx_type i = 0, idx = 0, bidx = 0; i < b_nc;
-           i++, idx+=nc, bidx+=b_nr)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = nr; j < nbuf; j++)
-            buf[j] = cs_complex_t (0.0, 0.0);
-
-          CXSPARSE_ZNAME (_pvec) (S->q, bvec + bidx, buf, nr);
-          CXSPARSE_ZNAME (_utsolve) (N->U, buf);
-
-          for (volatile octave_idx_type j = nr-1; j >= 0; j--)
-            {
-              octave_quit ();
-
-              CXSPARSE_ZNAME (_happly) (N->L, j, B[j], buf);
-            }
-
-          CXSPARSE_ZNAME (_pvec) (S->pinv, buf, vec + idx, nc);
-        }
-
-      info = 0;
-
-      return x;
-
-#else
-
-      octave_unused_parameter (b);
-
-      return ComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    template <>
-    SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::tall_solve<SparseComplexMatrix,
-                                                              SparseComplexMatrix>
-      (const SparseComplexMatrix& b, octave_idx_type& info)
-    {
-      info = -1;
-
-#if defined (HAVE_CXSPARSE)
-
-      octave_idx_type nr = nrows;
-      octave_idx_type nc = ncols;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      SparseComplexMatrix x (nc, b_nc, b.nnz ());
-      x.xcidx (0) = 0;
-
-      volatile octave_idx_type x_nz = b.nnz ();
-      volatile octave_idx_type ii = 0;
-
-      OCTAVE_LOCAL_BUFFER (Complex, Xx, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, S->m2);
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = 0; j < b_nr; j++)
-            Xx[j] = b.xelem (j, i);
-
-          for (octave_idx_type j = nr; j < S->m2; j++)
-            buf[j] = cs_complex_t (0.0, 0.0);
-
-          CXSPARSE_ZNAME (_ipvec) (S->pinv,
-                                   reinterpret_cast<cs_complex_t *> (Xx),
-                                   buf, nr);
-
-          for (volatile octave_idx_type j = 0; j < nc; j++)
-            {
-              octave_quit ();
-
-              CXSPARSE_ZNAME (_happly) (N->L, j, N->B[j], buf);
-            }
-
-          CXSPARSE_ZNAME (_usolve) (N->U, buf);
-          CXSPARSE_ZNAME (_ipvec) (S->q, buf,
-                                   reinterpret_cast<cs_complex_t *> (Xx),
-                                   nc);
-
-          for (octave_idx_type j = 0; j < nc; j++)
-            {
-              Complex tmp = Xx[j];
-
-              if (tmp != 0.0)
-                {
-                  if (ii == x_nz)
-                    {
-                      // Resize the sparse matrix
-                      octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
-                      sz = (sz > 10 ? sz : 10) + x_nz;
-                      x.change_capacity (sz);
-                      x_nz = sz;
-                    }
-
-                  x.xdata (ii) = tmp;
-                  x.xridx (ii++) = j;
-                }
-            }
-
-          x.xcidx (i+1) = ii;
-        }
-
-      info = 0;
-
-      x.maybe_compress ();
-
-      return x;
-
-#else
-
-      octave_unused_parameter (b);
-
-      return SparseComplexMatrix ();
-
-#endif
-    }
-
-    template <>
-    template <>
-    SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::sparse_qr_rep::wide_solve<SparseComplexMatrix,
-                                                              SparseComplexMatrix>
-      (const SparseComplexMatrix& b, octave_idx_type& info) const
-    {
-      info = -1;
-
-#if defined (HAVE_CXSPARSE)
-
-      // These are swapped because the original matrix was transposed in
-      // sparse_qr<SparseComplexMatrix>::solve.
-
-      octave_idx_type nr = ncols;
-      octave_idx_type nc = nrows;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      SparseComplexMatrix x (nc, b_nc, b.nnz ());
-      x.xcidx (0) = 0;
-
-      volatile octave_idx_type x_nz = b.nnz ();
-      volatile octave_idx_type ii = 0;
-      volatile octave_idx_type nbuf = (nc > S->m2 ? nc : S->m2);
-
-      OCTAVE_LOCAL_BUFFER (Complex, Xx, (b_nr > nc ? b_nr : nc));
-      OCTAVE_LOCAL_BUFFER (cs_complex_t, buf, nbuf);
-      OCTAVE_LOCAL_BUFFER (double, B, nr);
-
-      for (octave_idx_type i = 0; i < nr; i++)
-        B[i] = N->B[i];
-
-      for (volatile octave_idx_type i = 0, idx = 0; i < b_nc; i++, idx+=nc)
-        {
-          octave_quit ();
-
-          for (octave_idx_type j = 0; j < b_nr; j++)
-            Xx[j] = b.xelem (j, i);
-
-          for (octave_idx_type j = nr; j < nbuf; j++)
-            buf[j] = cs_complex_t (0.0, 0.0);
-
-          CXSPARSE_ZNAME (_pvec) (S->q, reinterpret_cast<cs_complex_t *>(Xx),
-                                  buf, nr);
-          CXSPARSE_ZNAME (_utsolve) (N->U, buf);
-
-          for (volatile octave_idx_type j = nr-1; j >= 0; j--)
-            {
-              octave_quit ();
-
-              CXSPARSE_ZNAME (_happly) (N->L, j, B[j], buf);
-            }
-
-          CXSPARSE_ZNAME (_pvec) (S->pinv, buf,
-                                  reinterpret_cast<cs_complex_t *>(Xx), nc);
-
-          for (octave_idx_type j = 0; j < nc; j++)
-            {
-              Complex tmp = Xx[j];
-
-              if (tmp != 0.0)
-                {
-                  if (ii == x_nz)
-                    {
-                      // Resize the sparse matrix
-                      octave_idx_type sz = x_nz * (b_nc - i) / b_nc;
-                      sz = (sz > 10 ? sz : 10) + x_nz;
-                      x.change_capacity (sz);
-                      x_nz = sz;
-                    }
-
-                  x.xdata (ii) = tmp;
-                  x.xridx (ii++) = j;
-                }
-            }
-
-          x.xcidx (i+1) = ii;
-        }
-
-      info = 0;
-
-      x.maybe_compress ();
-
-      return x;
-
-#else
-
-      octave_unused_parameter (b);
-
-      return SparseComplexMatrix ();
-
-#endif
-    }
-
-    template <typename SPARSE_T>
-    sparse_qr<SPARSE_T>::sparse_qr (void)
-      : m_rep (new sparse_qr_rep (SPARSE_T (), 0))
-    { }
-
-    template <typename SPARSE_T>
-    sparse_qr<SPARSE_T>::sparse_qr (const SPARSE_T& a, int order)
-      : m_rep (new sparse_qr_rep (a, order))
-    { }
-
-    template <typename SPARSE_T>
-    bool
-    sparse_qr<SPARSE_T>::ok (void) const
-    {
-      return m_rep->ok ();
-    }
-
-    template <typename SPARSE_T>
-    SPARSE_T
-    sparse_qr<SPARSE_T>::V (void) const
-    {
-      return m_rep->V ();
-    }
-
-    template <typename SPARSE_T>
-    ColumnVector
-    sparse_qr<SPARSE_T>::Pinv (void) const
-    {
-      return m_rep->P ();
-    }
-
-    template <typename SPARSE_T>
-    ColumnVector
-    sparse_qr<SPARSE_T>::P (void) const
-    {
-      return m_rep->P ();
-    }
-
-    template <typename SPARSE_T>
-    ColumnVector
-    sparse_qr<SPARSE_T>::E (void) const
-    {
-      return m_rep->E();
-    }
-
-
-    template <typename SPARSE_T>
-    SparseMatrix
-    sparse_qr<SPARSE_T>::E_MAT (void) const
-    {
-      ColumnVector perm = m_rep->E ();
-      octave_idx_type nrows = perm.rows ();
-      SparseMatrix ret (nrows, nrows, nrows);
-      for (octave_idx_type i = 0; i < nrows; i++)
-        ret(perm(i) - 1, i) = 1.0;
-      return ret;
-    }
-
-
-    template <typename SPARSE_T>
-    SPARSE_T
-    sparse_qr<SPARSE_T>::R (bool econ) const
-    {
-      return m_rep->R (econ);
-    }
-
-    template <typename SPARSE_T>
-    typename SPARSE_T::dense_matrix_type
-    sparse_qr<SPARSE_T>::C (const typename SPARSE_T::dense_matrix_type& b,
-                            bool econ) const
-    {
-      return m_rep->C (b, econ);
-    }
-
-    template <typename SPARSE_T>
-    typename SPARSE_T::dense_matrix_type
-    sparse_qr<SPARSE_T>::Q (bool econ) const
-    {
-      return m_rep->Q (econ);
-    }
-
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-    //specializations of function min2norm_solve
-    template <>
-    template <>
-    OCTAVE_API Matrix
-    sparse_qr<SparseMatrix>::min2norm_solve<MArray<double>, Matrix>
-    (const SparseMatrix& a, const MArray<double>& b,
-     octave_idx_type& info, int order)
-    {
-      info = -1;
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type nc = a.cols ();
-      Matrix x (nc, b_nc);
-      cholmod_common cc;
-
-      cholmod_l_start (&cc);
-      cholmod_sparse A = ros2rcs (a);
-      cholmod_dense B = rod2rcd (b);
-      cholmod_dense *X;
-
-      X = SuiteSparseQR_min2norm<double> (order, SPQR_DEFAULT_TOL, &A, &B, &cc);
-      spqr_error_handler (&cc);
-
-      double *vec = x.fortran_vec ();
-      for (volatile octave_idx_type i = 0; i < nc * b_nc; i++)
-        vec[i] = reinterpret_cast<double *> (X->x)[i];
-
-      info = 0;
-      if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
-        {
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
-        }
-      cholmod_l_finish (&cc);
-
-      return x;
-
-    }
-
-    template <>
-    template <>
-    OCTAVE_API SparseMatrix
-    sparse_qr<SparseMatrix>::min2norm_solve<SparseMatrix, SparseMatrix>
-    (const SparseMatrix& a, const SparseMatrix& b, octave_idx_type& info,
-     int order)
-    {
-      info = -1;
-      SparseMatrix x;
-      cholmod_common cc;
-
-      cholmod_l_start (&cc);
-      cholmod_sparse A = ros2rcs (a);
-      cholmod_sparse B = ros2rcs (b);
-      cholmod_sparse *X;
-
-      X = SuiteSparseQR_min2norm<double> (order, SPQR_DEFAULT_TOL, &A, &B, &cc);
-      spqr_error_handler (&cc);
-
-      if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
-        {
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
-          delete [] reinterpret_cast<SuiteSparse_long *> (B.p);
-          delete [] reinterpret_cast<SuiteSparse_long *> (B.i);
-        }
-
-      x = rcs2ros (X);
-      cholmod_l_finish (&cc);
-      info = 0;
-
-      return x;
-
-    }
-
-    template <>
-    template <>
-    OCTAVE_API ComplexMatrix
-    sparse_qr<SparseMatrix>::min2norm_solve<MArray<Complex>, ComplexMatrix>
-    (const SparseMatrix& a, const MArray<Complex>& b,
-     octave_idx_type& info, int order)
-    {
-      info = -1;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type nc = a.cols ();
-
-      ComplexMatrix x (nc, b_nc);
-
-      cholmod_common cc;
-
-      cholmod_l_start (&cc);
-
-      cholmod_sparse *A = ros2ccs (a, &cc);
-      cholmod_dense B = cod2ccd (b);
-      cholmod_dense *X;
-
-      X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, A, &B, &cc);
-      spqr_error_handler (&cc);
-
-      Complex *vec = x.fortran_vec ();
-      for (volatile octave_idx_type i = 0; i < nc * b_nc; i++)
-        vec[i] = reinterpret_cast<Complex *> (X->x)[i];
-
-      cholmod_l_free_sparse (&A, &cc);
-      cholmod_l_finish (&cc);
-
-      info = 0;
-
-      return x;
-
-    }
-
-    template <>
-    template <>
-    OCTAVE_API SparseComplexMatrix
-    sparse_qr<SparseMatrix>::min2norm_solve<SparseComplexMatrix,
-                                            SparseComplexMatrix>
-    (const SparseMatrix& a, const SparseComplexMatrix& b,
-     octave_idx_type& info, int order)
-    {
-      info = -1;
-
-      cholmod_common cc;
-
-      cholmod_l_start (&cc);
-
-      cholmod_sparse *A = ros2ccs (a, &cc);
-      cholmod_sparse B = cos2ccs (b);
-      cholmod_sparse *X;
-
-      X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, A, &B, &cc);
-      spqr_error_handler (&cc);
-
-      cholmod_l_free_sparse (&A, &cc);
-      if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
-        {
-          delete [] reinterpret_cast<SuiteSparse_long *> (B.p);
-          delete [] reinterpret_cast<SuiteSparse_long *> (B.i);
-        }
-      cholmod_l_finish (&cc);
-
-      SparseComplexMatrix ret = ccs2cos(X);
-
-      info = 0;
-
-      return ret;
-
-    }
-
-    template <>
-    template <>
-    OCTAVE_API ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::min2norm_solve<MArray<Complex>,
-                                                   ComplexMatrix>
-    (const SparseComplexMatrix& a, const MArray<Complex>& b,
-     octave_idx_type& info,int order)
-    {
-      info = -1;
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type nc = a.cols ();
-      ComplexMatrix x (nc, b_nc);
-
-      cholmod_common cc;
-
-      cholmod_l_start (&cc);
-
-      cholmod_sparse A = cos2ccs (a);
-      cholmod_dense B = cod2ccd (b);
-      cholmod_dense *X;
-
-      X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, &A, &B, &cc);
-      spqr_error_handler (&cc);
-
-      Complex *vec = x.fortran_vec ();
-      for (volatile octave_idx_type i = 0; i < nc * b_nc; i++)
-        vec[i] = reinterpret_cast<Complex *> (X->x)[i];
-
-      if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
-        {
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
-        }
-      cholmod_l_finish (&cc);
-
-      info = 0;
-
-      return x;
-
-    }
-
-    template <>
-    template <>
-    OCTAVE_API ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::min2norm_solve<MArray<double>,
-                                                   ComplexMatrix>
-    (const SparseComplexMatrix& a, const MArray<double>& b,
-     octave_idx_type& info, int order)
-    {
-      info = -1;
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type nc = a.cols ();
-      ComplexMatrix x (nc, b_nc);
-
-      cholmod_common cc;
-
-      cholmod_l_start (&cc);
-
-      cholmod_sparse A = cos2ccs (a);
-      cholmod_dense *B = rod2ccd (b, &cc);
-      cholmod_dense *X;
-
-      X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, &A, B, &cc);
-      spqr_error_handler (&cc);
-
-      Complex *vec = x.fortran_vec ();
-
-      for (volatile octave_idx_type i = 0; i < nc * b_nc; i++)
-        vec[i] = reinterpret_cast<Complex *> (X->x)[i];
-
-      if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
-        {
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
-        }
-      cholmod_l_free_dense (&B, &cc);
-      cholmod_l_finish (&cc);
-
-      info = 0;
-
-      return x;
-
-    }
-
-    template <>
-    template <>
-    OCTAVE_API SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::min2norm_solve<SparseComplexMatrix,
-                                                   SparseComplexMatrix>
-    (const SparseComplexMatrix& a, const SparseComplexMatrix& b,
-     octave_idx_type& info, int order)
-    {
-      info = -1;
-
-      cholmod_common cc;
-
-      cholmod_l_start (&cc);
-
-      cholmod_sparse A = cos2ccs (a);
-      cholmod_sparse B = cos2ccs (b);
-      cholmod_sparse *X;
-
-      X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, &A, &B, &cc);
-      spqr_error_handler (&cc);
-
-      if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
-        {
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
-          delete [] reinterpret_cast<SuiteSparse_long *> (B.p);
-          delete [] reinterpret_cast<SuiteSparse_long *> (B.i);
-        }
-      cholmod_l_finish (&cc);
-
-      info = 0;
-
-      return ccs2cos (X);
-
-    }
-
-    template <>
-    template <>
-    OCTAVE_API SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::min2norm_solve<SparseMatrix,
-                                                   SparseComplexMatrix>
-    (const SparseComplexMatrix& a, const SparseMatrix& b,
-     octave_idx_type& info,int order)
-    {
-      info = -1;
-
-      cholmod_common cc;
-
-      cholmod_l_start (&cc);
-
-      cholmod_sparse A = cos2ccs (a);
-      cholmod_sparse *B = ros2ccs (b, &cc);
-      cholmod_sparse *X;
-
-      X = SuiteSparseQR_min2norm<Complex> (order, SPQR_DEFAULT_TOL, &A, B, &cc);
-      spqr_error_handler (&cc);
-
-      SparseComplexMatrix ret = ccs2cos(X);
-
-      if (sizeof (octave_idx_type) != sizeof (SuiteSparse_long))
-        {
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.p);
-          delete [] reinterpret_cast<SuiteSparse_long *> (A.i);
-        }
-      cholmod_l_free_sparse (&B, &cc);
-      cholmod_l_finish (&cc);
-
-      info = 0;
-
-      return ret;
-
-    }
-#endif
-
-    // FIXME: Why is the "order" of the QR calculation as used in the
-    // CXSparse function sqr 3 for real matrices and 2 for complex?  These
-    // values seem to be required but there was no explanation in David
-    // Bateman's original code.
-
-    template <typename SPARSE_T>
-    class
-    cxsparse_defaults
-    {
-    public:
-      enum { order = -1 };
-    };
-
-    template <>
-    class
-    cxsparse_defaults<SparseMatrix>
-    {
-    public:
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-      enum { order = SPQR_ORDERING_DEFAULT };
-#elif defined (HAVE_CXSPARSE)
-      enum { order = 3 };
-#endif
-    };
-
-    template <>
-    class
-    cxsparse_defaults<SparseComplexMatrix>
-    {
-    public:
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-      enum { order = SPQR_ORDERING_DEFAULT };
-#elif defined (HAVE_CXSPARSE)
-      enum { order = 2 };
-#endif
-    };
-
-    template <typename SPARSE_T>
-    template <typename RHS_T, typename RET_T>
-    RET_T
-    sparse_qr<SPARSE_T>::solve (const SPARSE_T& a, const RHS_T& b,
-                                octave_idx_type& info)
-    {
-#if (defined (HAVE_SPQR) && defined (HAVE_CHOLMOD))
-
-      info = -1;
-
-      octave_idx_type nr = a.rows ();
-      octave_idx_type nc = a.cols ();
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      int order = cxsparse_defaults<SPARSE_T>::order;
-
-      if (nr < 0 || nc < 0 || b_nc < 0 || b_nr < 0)
-        (*current_liboctave_error_handler)
-          ("matrix dimension with negative size");
-
-      if ( nr != b_nr)
-        (*current_liboctave_error_handler)
-          ("matrix dimension mismatch in solution of minimum norm problem");
-
-      info = 0;
-
-      return min2norm_solve<RHS_T, RET_T> (a, b, info, order);
-
-#elif defined (HAVE_CXSPARSE)
-
-      info = -1;
-
-      octave_idx_type nr = a.rows ();
-      octave_idx_type nc = a.cols ();
-
-      octave_idx_type b_nc = b.cols ();
-      octave_idx_type b_nr = b.rows ();
-
-      int order = cxsparse_defaults<SPARSE_T>::order;
-
-      if (nr < 0 || nc < 0 || nr != b_nr)
-        (*current_liboctave_error_handler)
-          ("matrix dimension mismatch in solution of minimum norm problem");
-
-      if (nr == 0 || nc == 0 || b_nc == 0)
-        {
-          info = 0;
-
-          return RET_T (nc, b_nc, 0.0);
-        }
-      else if (nr >= nc)
-        {
-          sparse_qr<SPARSE_T> q (a, order);
-
-          return q.ok () ? q.tall_solve<RHS_T, RET_T> (b, info) : RET_T ();
-        }
-      else
-        {
-          sparse_qr<SPARSE_T> q (a.hermitian (), order);
-
-          return q.ok () ? q.wide_solve<RHS_T, RET_T> (b, info) : RET_T ();
-        }
-
-#else
-
-      octave_unused_parameter (a);
-      octave_unused_parameter (b);
-      octave_unused_parameter (info);
-
-      return RET_T ();
-
-#endif
-    }
-
-    //explicit instantiations of static member function solve
-    template
-    OCTAVE_API Matrix
-    sparse_qr<SparseMatrix>::solve<MArray<double>, Matrix>
-    (const SparseMatrix& a, const MArray<double>& b, octave_idx_type& info);
-
-    template
-    OCTAVE_API SparseMatrix
-    sparse_qr<SparseMatrix>::solve<SparseMatrix, SparseMatrix>
-    (const SparseMatrix& a, const SparseMatrix& b, octave_idx_type& info);
-
-    template
-    OCTAVE_API ComplexMatrix
-    sparse_qr<SparseMatrix>::solve<MArray<Complex>, ComplexMatrix>
-    (const SparseMatrix& a, const MArray<Complex>& b, octave_idx_type& info);
-
-    template
-    OCTAVE_API SparseComplexMatrix
-    sparse_qr<SparseMatrix>::solve<SparseComplexMatrix, SparseComplexMatrix>
-    (const SparseMatrix& a, const SparseComplexMatrix& b,
-     octave_idx_type& info);
-
-    template
-    OCTAVE_API ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::solve<MArray<Complex>, ComplexMatrix>
-    (const SparseComplexMatrix& a, const MArray<Complex>& b,
-     octave_idx_type& info);
-
-    template
-    OCTAVE_API SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::solve<
-    SparseComplexMatrix, SparseComplexMatrix>
-    (const SparseComplexMatrix& a, const SparseComplexMatrix& b,
-     octave_idx_type& info);
-
-    template
-    OCTAVE_API ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::solve<MArray<double>, ComplexMatrix>
-    (const SparseComplexMatrix& a, const MArray<double>& b,
-     octave_idx_type& info);
-
-    template
-    OCTAVE_API SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::solve<SparseMatrix, SparseComplexMatrix>
-    (const SparseComplexMatrix& a, const SparseMatrix& b,
-     octave_idx_type& info);
-
-    //explicit instantiations of member function E_MAT
-    template
-    OCTAVE_API SparseMatrix
-    sparse_qr<SparseMatrix>::E_MAT (void) const;
-
-    template
-    OCTAVE_API SparseMatrix
-    sparse_qr<SparseComplexMatrix>::E_MAT (void) const;
-
-    template <typename SPARSE_T>
-    template <typename RHS_T, typename RET_T>
-    RET_T
-    sparse_qr<SPARSE_T>::tall_solve (const RHS_T& b, octave_idx_type& info) const
-    {
-      return m_rep->template tall_solve<RHS_T, RET_T> (b, info);
-    }
-
-    template <typename SPARSE_T>
-    template <typename RHS_T, typename RET_T>
-    RET_T
-    sparse_qr<SPARSE_T>::wide_solve (const RHS_T& b, octave_idx_type& info) const
-    {
-      return m_rep->template wide_solve<RHS_T, RET_T> (b, info);
-    }
-
-    // Explicitly instantiate all member functions
-
-    template OCTAVE_API sparse_qr<SparseMatrix>::sparse_qr (void);
-    template OCTAVE_API
-    sparse_qr<SparseMatrix>::sparse_qr (const SparseMatrix& a, int order);
-    template OCTAVE_API bool sparse_qr<SparseMatrix>::ok (void) const;
-    template OCTAVE_API ColumnVector sparse_qr<SparseMatrix>::E (void) const;
-    template OCTAVE_API SparseMatrix sparse_qr<SparseMatrix>::V (void) const;
-    template OCTAVE_API ColumnVector sparse_qr<SparseMatrix>::Pinv (void) const;
-    template OCTAVE_API ColumnVector sparse_qr<SparseMatrix>::P (void) const;
-    template OCTAVE_API SparseMatrix
-    sparse_qr<SparseMatrix>::R (bool econ) const;
-    template OCTAVE_API Matrix
-    sparse_qr<SparseMatrix>::C (const Matrix& b, bool econ) const;
-    template OCTAVE_API Matrix sparse_qr<SparseMatrix>::Q (bool econ) const;
-
-    template OCTAVE_API sparse_qr<SparseComplexMatrix>::sparse_qr (void);
-    template OCTAVE_API
-    sparse_qr<SparseComplexMatrix>::sparse_qr
-    (const SparseComplexMatrix& a, int order);
-    template OCTAVE_API bool sparse_qr<SparseComplexMatrix>::ok (void) const;
-    template OCTAVE_API ColumnVector
-    sparse_qr<SparseComplexMatrix>::E (void) const;
-    template OCTAVE_API SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::V (void) const;
-    template OCTAVE_API ColumnVector
-    sparse_qr<SparseComplexMatrix>::Pinv (void) const;
-    template OCTAVE_API ColumnVector
-    sparse_qr<SparseComplexMatrix>::P (void) const;
-    template OCTAVE_API SparseComplexMatrix
-    sparse_qr<SparseComplexMatrix>::R (bool econ) const;
-    template OCTAVE_API ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::C (const ComplexMatrix& b, bool econ) const;
-    template OCTAVE_API ComplexMatrix
-    sparse_qr<SparseComplexMatrix>::Q (bool econ) const;
-
-    Matrix
-    qrsolve (const SparseMatrix& a, const MArray<double>& b,
-             octave_idx_type& info)
-    {
-      return sparse_qr<SparseMatrix>::solve<MArray<double>, Matrix> (a, b,
-                                                                     info);
-    }
-
-    SparseMatrix
-    qrsolve (const SparseMatrix& a, const SparseMatrix& b,
-             octave_idx_type& info)
-    {
-      return sparse_qr<SparseMatrix>::solve<SparseMatrix, SparseMatrix> (a, b,
-                                                                         info);
-    }
-
-    ComplexMatrix
-    qrsolve (const SparseMatrix& a, const MArray<Complex>& b,
-             octave_idx_type& info)
-    {
-      return sparse_qr<SparseMatrix>::solve<MArray<Complex>,
-                                            ComplexMatrix> (a, b, info);
-    }
-
-    SparseComplexMatrix
-    qrsolve (const SparseMatrix& a, const SparseComplexMatrix& b,
-             octave_idx_type& info)
-    {
-      return sparse_qr<SparseMatrix>::solve<SparseComplexMatrix,
-                                            SparseComplexMatrix> (a, b, info);
-    }
-
-    ComplexMatrix
-    qrsolve (const SparseComplexMatrix& a, const MArray<double>& b,
-             octave_idx_type& info)
-    {
-      return sparse_qr<SparseComplexMatrix>::solve<MArray<double>,
-                                                   ComplexMatrix> (a, b, info);
-    }
-
-    SparseComplexMatrix
-    qrsolve (const SparseComplexMatrix& a, const SparseMatrix& b,
-             octave_idx_type& info)
-    {
-      return sparse_qr<SparseComplexMatrix>::solve<SparseMatrix,
-                                                   SparseComplexMatrix>
-                                             (a, b, info);
-    }
-
-    ComplexMatrix
-    qrsolve (const SparseComplexMatrix& a, const MArray<Complex>& b,
-             octave_idx_type& info)
-    {
-      return sparse_qr<SparseComplexMatrix>::solve<MArray<Complex>,
-                                                   ComplexMatrix> (a, b, info);
-    }
-
-    SparseComplexMatrix
-    qrsolve (const SparseComplexMatrix& a, const SparseComplexMatrix& b,
-             octave_idx_type& info)
-    {
-      return sparse_qr<SparseComplexMatrix>::solve<SparseComplexMatrix,
-                                                   SparseComplexMatrix>
-                                             (a, b, info);
-    }
+}
+
+//explicit instantiations of static member function solve
+template
+OCTAVE_API Matrix
+sparse_qr<SparseMatrix>::solve<MArray<double>, Matrix>
+(const SparseMatrix& a, const MArray<double>& b, octave_idx_type& info);
+
+template
+OCTAVE_API SparseMatrix
+sparse_qr<SparseMatrix>::solve<SparseMatrix, SparseMatrix>
+(const SparseMatrix& a, const SparseMatrix& b, octave_idx_type& info);
+
+template
+OCTAVE_API ComplexMatrix
+sparse_qr<SparseMatrix>::solve<MArray<Complex>, ComplexMatrix>
+(const SparseMatrix& a, const MArray<Complex>& b, octave_idx_type& info);
+
+template
+OCTAVE_API SparseComplexMatrix
+sparse_qr<SparseMatrix>::solve<SparseComplexMatrix, SparseComplexMatrix>
+(const SparseMatrix& a, const SparseComplexMatrix& b,
+ octave_idx_type& info);
+
+template
+OCTAVE_API ComplexMatrix
+sparse_qr<SparseComplexMatrix>::solve<MArray<Complex>, ComplexMatrix>
+(const SparseComplexMatrix& a, const MArray<Complex>& b,
+ octave_idx_type& info);
+
+template
+OCTAVE_API SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::solve<
+SparseComplexMatrix, SparseComplexMatrix>
+(const SparseComplexMatrix& a, const SparseComplexMatrix& b,
+ octave_idx_type& info);
+
+template
+OCTAVE_API ComplexMatrix
+sparse_qr<SparseComplexMatrix>::solve<MArray<double>, ComplexMatrix>
+(const SparseComplexMatrix& a, const MArray<double>& b,
+ octave_idx_type& info);
+
+template
+OCTAVE_API SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::solve<SparseMatrix, SparseComplexMatrix>
+(const SparseComplexMatrix& a, const SparseMatrix& b,
+ octave_idx_type& info);
+
+//explicit instantiations of member function E_MAT
+template
+OCTAVE_API SparseMatrix
+sparse_qr<SparseMatrix>::E_MAT (void) const;
+
+template
+OCTAVE_API SparseMatrix
+sparse_qr<SparseComplexMatrix>::E_MAT (void) const;
+
+template <typename SPARSE_T>
+template <typename RHS_T, typename RET_T>
+RET_T
+sparse_qr<SPARSE_T>::tall_solve (const RHS_T& b, octave_idx_type& info) const
+{
+  return m_rep->template tall_solve<RHS_T, RET_T> (b, info);
+}
+
+template <typename SPARSE_T>
+template <typename RHS_T, typename RET_T>
+RET_T
+sparse_qr<SPARSE_T>::wide_solve (const RHS_T& b, octave_idx_type& info) const
+{
+  return m_rep->template wide_solve<RHS_T, RET_T> (b, info);
+}
+
+// Explicitly instantiate all member functions
+
+template OCTAVE_API sparse_qr<SparseMatrix>::sparse_qr (void);
+template OCTAVE_API
+sparse_qr<SparseMatrix>::sparse_qr (const SparseMatrix& a, int order);
+template OCTAVE_API bool sparse_qr<SparseMatrix>::ok (void) const;
+template OCTAVE_API ColumnVector sparse_qr<SparseMatrix>::E (void) const;
+template OCTAVE_API SparseMatrix sparse_qr<SparseMatrix>::V (void) const;
+template OCTAVE_API ColumnVector sparse_qr<SparseMatrix>::Pinv (void) const;
+template OCTAVE_API ColumnVector sparse_qr<SparseMatrix>::P (void) const;
+template OCTAVE_API SparseMatrix
+sparse_qr<SparseMatrix>::R (bool econ) const;
+template OCTAVE_API Matrix
+sparse_qr<SparseMatrix>::C (const Matrix& b, bool econ) const;
+template OCTAVE_API Matrix sparse_qr<SparseMatrix>::Q (bool econ) const;
+
+template OCTAVE_API sparse_qr<SparseComplexMatrix>::sparse_qr (void);
+template OCTAVE_API
+sparse_qr<SparseComplexMatrix>::sparse_qr
+(const SparseComplexMatrix& a, int order);
+template OCTAVE_API bool sparse_qr<SparseComplexMatrix>::ok (void) const;
+template OCTAVE_API ColumnVector
+sparse_qr<SparseComplexMatrix>::E (void) const;
+template OCTAVE_API SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::V (void) const;
+template OCTAVE_API ColumnVector
+sparse_qr<SparseComplexMatrix>::Pinv (void) const;
+template OCTAVE_API ColumnVector
+sparse_qr<SparseComplexMatrix>::P (void) const;
+template OCTAVE_API SparseComplexMatrix
+sparse_qr<SparseComplexMatrix>::R (bool econ) const;
+template OCTAVE_API ComplexMatrix
+sparse_qr<SparseComplexMatrix>::C (const ComplexMatrix& b, bool econ) const;
+template OCTAVE_API ComplexMatrix
+sparse_qr<SparseComplexMatrix>::Q (bool econ) const;
+
+Matrix
+qrsolve (const SparseMatrix& a, const MArray<double>& b,
+         octave_idx_type& info)
+{
+  return sparse_qr<SparseMatrix>::solve<MArray<double>, Matrix> (a, b,
+         info);
+}
+
+SparseMatrix
+qrsolve (const SparseMatrix& a, const SparseMatrix& b,
+         octave_idx_type& info)
+{
+  return sparse_qr<SparseMatrix>::solve<SparseMatrix, SparseMatrix> (a, b,
+         info);
+}
+
+ComplexMatrix
+qrsolve (const SparseMatrix& a, const MArray<Complex>& b,
+         octave_idx_type& info)
+{
+  return sparse_qr<SparseMatrix>::solve<MArray<Complex>,
+         ComplexMatrix> (a, b, info);
+}
+
+SparseComplexMatrix
+qrsolve (const SparseMatrix& a, const SparseComplexMatrix& b,
+         octave_idx_type& info)
+{
+  return sparse_qr<SparseMatrix>::solve<SparseComplexMatrix,
+         SparseComplexMatrix> (a, b, info);
+}
+
+ComplexMatrix
+qrsolve (const SparseComplexMatrix& a, const MArray<double>& b,
+         octave_idx_type& info)
+{
+  return sparse_qr<SparseComplexMatrix>::solve<MArray<double>,
+         ComplexMatrix> (a, b, info);
+}
+
+SparseComplexMatrix
+qrsolve (const SparseComplexMatrix& a, const SparseMatrix& b,
+         octave_idx_type& info)
+{
+  return sparse_qr<SparseComplexMatrix>::solve<SparseMatrix,
+         SparseComplexMatrix>
+         (a, b, info);
+}
+
+ComplexMatrix
+qrsolve (const SparseComplexMatrix& a, const MArray<Complex>& b,
+         octave_idx_type& info)
+{
+  return sparse_qr<SparseComplexMatrix>::solve<MArray<Complex>,
+         ComplexMatrix> (a, b, info);
+}
+
+SparseComplexMatrix
+qrsolve (const SparseComplexMatrix& a, const SparseComplexMatrix& b,
+         octave_idx_type& info)
+{
+  return sparse_qr<SparseComplexMatrix>::solve<SparseComplexMatrix,
+         SparseComplexMatrix>
+         (a, b, info);
+}
 
 OCTAVE_END_NAMESPACE(math)
 OCTAVE_END_NAMESPACE(octave)