diff src/corefcn/qz.cc @ 15039:e753177cde93

maint: Move non-dynamically linked functions from DLD-FUNCTIONS/ to corefcn/ directory * __contourc__.cc, __dispatch__.cc, __lin_interpn__.cc, __pchip_deriv__.cc, __qp__.cc, balance.cc, besselj.cc, betainc.cc, bsxfun.cc, cellfun.cc, colloc.cc, conv2.cc, daspk.cc, dasrt.cc, dassl.cc, det.cc, dlmread.cc, dot.cc, eig.cc, fft.cc, fft2.cc, fftn.cc, filter.cc, find.cc, gammainc.cc, gcd.cc, getgrent.cc, getpwent.cc, getrusage.cc, givens.cc, hess.cc, hex2num.cc, inv.cc, kron.cc, lookup.cc, lsode.cc, lu.cc, luinc.cc, matrix_type.cc, max.cc, md5sum.cc, mgorth.cc, nproc.cc, pinv.cc, quad.cc, quadcc.cc, qz.cc, rand.cc, rcond.cc, regexp.cc, schur.cc, spparms.cc, sqrtm.cc, str2double.cc, strfind.cc, sub2ind.cc, svd.cc, syl.cc, time.cc, tril.cc, typecast.cc: Move functions from DLD-FUNCTIONS/ to corefcn/ directory. Include "defun.h", not "defun-dld.h". Change docstring to refer to these as "Built-in Functions". * build-aux/mk-opts.pl: Generate options code with '#include "defun.h"'. Change option docstrings to refer to these as "Built-in Functions". * corefcn/module.mk: List of functions to build in corefcn/ dir. * DLD-FUNCTIONS/config-module.awk: Update to new build system. * DLD-FUNCTIONS/module-files: Remove functions which are now in corefcn/ directory. * src/Makefile.am: Update to build "convenience library" in corefcn/. Octave program now links against all other libraries + corefcn libary. * src/find-defun-files.sh: Strip $srcdir from filename. * src/link-deps.mk: Add REGEX and FFTW link dependencies for liboctinterp. * type.m, which.m: Change failing tests to use 'amd', still a dynamic function, rather than 'dot', which isn't.
author Rik <rik@octave.org>
date Fri, 27 Jul 2012 15:35:00 -0700
parents src/DLD-FUNCTIONS/qz.cc@54a386f2ac4e
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/corefcn/qz.cc	Fri Jul 27 15:35:00 2012 -0700
@@ -0,0 +1,1278 @@
+/*
+
+Copyright (C) 1998-2012 A. S. Hodel
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+// Generalized eigenvalue balancing via LAPACK
+
+// Author: A. S. Hodel <scotte@eng.auburn.edu>
+
+#undef DEBUG
+#undef DEBUG_SORT
+#undef DEBUG_EIG
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <cfloat>
+
+#include <iostream>
+#include <iomanip>
+
+#include "CmplxQRP.h"
+#include "CmplxQR.h"
+#include "dbleQR.h"
+#include "f77-fcn.h"
+#include "lo-math.h"
+#include "quit.h"
+
+#include "defun.h"
+#include "error.h"
+#include "gripes.h"
+#include "oct-obj.h"
+#include "oct-map.h"
+#include "ov.h"
+#include "pager.h"
+#if defined (DEBUG) || defined (DEBUG_SORT)
+#include "pr-output.h"
+#endif
+#include "symtab.h"
+#include "utils.h"
+#include "variables.h"
+
+typedef octave_idx_type (*sort_function) (const octave_idx_type& LSIZE, const double& ALPHA,
+                              const double& BETA, const double& S,
+                              const double& P);
+
+extern "C"
+{
+  F77_RET_T
+  F77_FUNC (dggbal, DGGBAL) (F77_CONST_CHAR_ARG_DECL,
+                             const octave_idx_type& N, double* A,
+                             const octave_idx_type& LDA, double* B,
+                             const octave_idx_type& LDB, octave_idx_type& ILO,
+                             octave_idx_type& IHI, double* LSCALE,
+                             double* RSCALE, double* WORK,
+                             octave_idx_type& INFO
+                             F77_CHAR_ARG_LEN_DECL);
+
+F77_RET_T
+  F77_FUNC (zggbal, ZGGBAL) (F77_CONST_CHAR_ARG_DECL,
+                             const octave_idx_type& N, Complex* A,
+                             const octave_idx_type& LDA, Complex* B,
+                             const octave_idx_type& LDB, octave_idx_type& ILO,
+                             octave_idx_type& IHI, double* LSCALE,
+                             double* RSCALE, double* WORK,
+                             octave_idx_type& INFO
+                             F77_CHAR_ARG_LEN_DECL);
+
+  F77_RET_T
+  F77_FUNC (dggbak, DGGBAK) (F77_CONST_CHAR_ARG_DECL,
+                             F77_CONST_CHAR_ARG_DECL,
+                             const octave_idx_type& N,
+                             const octave_idx_type& ILO,
+                             const octave_idx_type& IHI,
+                             const double* LSCALE, const double* RSCALE,
+                             octave_idx_type& M, double* V,
+                             const octave_idx_type& LDV, octave_idx_type& INFO
+                             F77_CHAR_ARG_LEN_DECL
+                             F77_CHAR_ARG_LEN_DECL);
+
+F77_RET_T
+  F77_FUNC (zggbak, ZGGBAK) (F77_CONST_CHAR_ARG_DECL,
+                             F77_CONST_CHAR_ARG_DECL,
+                             const octave_idx_type& N,
+                             const octave_idx_type& ILO,
+                             const octave_idx_type& IHI,
+                             const double* LSCALE, const double* RSCALE,
+                             octave_idx_type& M, Complex* V,
+                             const octave_idx_type& LDV, octave_idx_type& INFO
+                             F77_CHAR_ARG_LEN_DECL
+                             F77_CHAR_ARG_LEN_DECL);
+
+  F77_RET_T
+  F77_FUNC (dgghrd, DGGHRD) (F77_CONST_CHAR_ARG_DECL,
+                             F77_CONST_CHAR_ARG_DECL,
+                             const octave_idx_type& N,
+                             const octave_idx_type& ILO,
+                             const octave_idx_type& IHI, double* A,
+                             const octave_idx_type& LDA, double* B,
+                             const octave_idx_type& LDB, double* Q,
+                             const octave_idx_type& LDQ, double* Z,
+                             const octave_idx_type& LDZ, octave_idx_type& INFO
+                             F77_CHAR_ARG_LEN_DECL
+                             F77_CHAR_ARG_LEN_DECL);
+
+ F77_RET_T
+  F77_FUNC (zgghrd, ZGGHRD) (F77_CONST_CHAR_ARG_DECL,
+                             F77_CONST_CHAR_ARG_DECL,
+                             const octave_idx_type& N,
+                             const octave_idx_type& ILO,
+                             const octave_idx_type& IHI, Complex* A,
+                             const octave_idx_type& LDA, Complex* B,
+                             const octave_idx_type& LDB, Complex* Q,
+                             const octave_idx_type& LDQ, Complex* Z,
+                             const octave_idx_type& LDZ, octave_idx_type& INFO
+                             F77_CHAR_ARG_LEN_DECL
+                             F77_CHAR_ARG_LEN_DECL);
+
+  F77_RET_T
+  F77_FUNC (dhgeqz, DHGEQZ) (F77_CONST_CHAR_ARG_DECL,
+                             F77_CONST_CHAR_ARG_DECL,
+                             F77_CONST_CHAR_ARG_DECL,
+                             const octave_idx_type& N,
+                             const octave_idx_type& ILO,
+                             const octave_idx_type& IHI,
+                             double* A, const octave_idx_type& LDA, double* B,
+                             const octave_idx_type& LDB, double* ALPHAR,
+                             double* ALPHAI, double* BETA, double* Q,
+                             const octave_idx_type& LDQ, double* Z,
+                             const octave_idx_type& LDZ, double* WORK,
+                             const octave_idx_type& LWORK,
+                             octave_idx_type& INFO
+                             F77_CHAR_ARG_LEN_DECL
+                             F77_CHAR_ARG_LEN_DECL
+                             F77_CHAR_ARG_LEN_DECL);
+
+F77_RET_T
+  F77_FUNC (zhgeqz, ZHGEQZ) (F77_CONST_CHAR_ARG_DECL,
+                             F77_CONST_CHAR_ARG_DECL,
+                             F77_CONST_CHAR_ARG_DECL,
+                             const octave_idx_type& N,
+                             const octave_idx_type& ILO,
+                             const octave_idx_type& IHI,
+                             Complex* A, const octave_idx_type& LDA,
+                             Complex* B, const octave_idx_type& LDB,
+                             Complex* ALPHA, Complex* BETA, Complex* CQ,
+                             const octave_idx_type& LDQ,
+                             Complex* CZ, const octave_idx_type& LDZ,
+                             Complex* WORK, const octave_idx_type& LWORK,
+                             double* RWORK, octave_idx_type& INFO
+                             F77_CHAR_ARG_LEN_DECL
+                             F77_CHAR_ARG_LEN_DECL
+                             F77_CHAR_ARG_LEN_DECL);
+
+  F77_RET_T
+  F77_FUNC (dlag2, DLAG2) (const double* A, const octave_idx_type& LDA,
+                           const double* B, const octave_idx_type& LDB,
+                           const double& SAFMIN, double& SCALE1,
+                           double& SCALE2, double& WR1, double& WR2,
+                           double& WI);
+
+  // Van Dooren's code (netlib.org: toms/590) for reordering
+  // GEP.  Only processes Z, not Q.
+  F77_RET_T
+  F77_FUNC (dsubsp, DSUBSP) (const octave_idx_type& NMAX,
+                             const octave_idx_type& N, double* A,
+                             double* B, double* Z, sort_function,
+                             const double& EPS, octave_idx_type& NDIM,
+                             octave_idx_type& FAIL, octave_idx_type* IND);
+
+  // Documentation for DTGEVC incorrectly states that VR, VL are
+  // complex*16; they are declared in DTGEVC as double precision
+  // (probably a cut and paste problem fro ZTGEVC).
+  F77_RET_T
+  F77_FUNC (dtgevc, DTGEVC) (F77_CONST_CHAR_ARG_DECL,
+                             F77_CONST_CHAR_ARG_DECL,
+                             octave_idx_type* SELECT,
+                             const octave_idx_type& N, double* A,
+                             const octave_idx_type& LDA, double* B,
+                             const octave_idx_type& LDB, double* VL,
+                             const octave_idx_type& LDVL, double* VR,
+                             const octave_idx_type& LDVR,
+                             const octave_idx_type& MM, octave_idx_type& M,
+                             double* WORK, octave_idx_type& INFO
+                             F77_CHAR_ARG_LEN_DECL
+                             F77_CHAR_ARG_LEN_DECL);
+
+F77_RET_T
+  F77_FUNC (ztgevc, ZTGEVC) (F77_CONST_CHAR_ARG_DECL,
+                             F77_CONST_CHAR_ARG_DECL,
+                             octave_idx_type* SELECT,
+                             const octave_idx_type& N, const Complex* A,
+                             const octave_idx_type& LDA,const Complex* B,
+                             const octave_idx_type& LDB, Complex* xVL,
+                             const octave_idx_type& LDVL, Complex* xVR,
+                             const octave_idx_type& LDVR,
+                             const octave_idx_type& MM, octave_idx_type& M,
+                             Complex* CWORK, double* RWORK,
+                             octave_idx_type& INFO
+                             F77_CHAR_ARG_LEN_DECL
+                             F77_CHAR_ARG_LEN_DECL);
+
+  F77_RET_T
+  F77_FUNC (xdlamch, XDLAMCH) (F77_CONST_CHAR_ARG_DECL,
+                               double& retval
+                               F77_CHAR_ARG_LEN_DECL);
+
+  F77_RET_T
+  F77_FUNC (xdlange, XDLANGE) (F77_CONST_CHAR_ARG_DECL,
+                               const octave_idx_type&,
+                               const octave_idx_type&, const double*,
+                               const octave_idx_type&, double*, double&
+                               F77_CHAR_ARG_LEN_DECL);
+}
+
+// fcrhp, fin, fout, folhp:
+// routines for ordering of generalized eigenvalues
+// return 1 if  test is passed, 0 otherwise
+//    fin: |lambda| < 1
+//    fout: |lambda| >= 1
+//    fcrhp: real(lambda) >= 0
+//    folhp: real(lambda) < 0
+
+static octave_idx_type
+fcrhp (const octave_idx_type& lsize, const double& alpha,
+       const double& beta, const double& s, const double&)
+{
+  if (lsize == 1)
+    return (alpha * beta >= 0 ? 1 : -1);
+  else
+    return (s >= 0 ? 1 : -1);
+}
+
+static octave_idx_type
+fin (const octave_idx_type& lsize, const double& alpha,
+     const double& beta, const double&, const double& p)
+{
+  octave_idx_type retval;
+
+  if (lsize == 1)
+    retval = (fabs (alpha) < fabs (beta) ? 1 : -1);
+  else
+    retval = (fabs (p) < 1 ? 1 : -1);
+
+#ifdef DEBUG
+  std::cout << "qz: fin: retval=" << retval << std::endl;
+#endif
+
+  return retval;
+}
+
+static octave_idx_type
+folhp (const octave_idx_type& lsize, const double& alpha,
+       const double& beta, const double& s, const double&)
+{
+  if (lsize == 1)
+    return (alpha * beta < 0 ? 1 : -1);
+  else
+    return (s < 0 ? 1 : -1);
+}
+
+static octave_idx_type
+fout (const octave_idx_type& lsize, const double& alpha,
+      const double& beta, const double&, const double& p)
+{
+  if (lsize == 1)
+    return (fabs (alpha) >= fabs (beta) ? 1 : -1);
+  else
+    return (fabs (p) >= 1 ? 1 : -1);
+}
+
+
+//FIXME: Matlab does not produce lambda as the first output argument.
+//       Compatibility problem?
+DEFUN (qz, args, nargout,
+  "-*- texinfo -*-\n\
+@deftypefn  {Built-in Function} {@var{lambda} =} qz (@var{A}, @var{B})\n\
+@deftypefnx {Built-in Function} {@var{lambda} =} qz (@var{A}, @var{B}, @var{opt})\n\
+QZ@tie{}decomposition of the generalized eigenvalue problem\n\
+(@math{A x = s B x}).  There are three ways to call this function:\n\
+@enumerate\n\
+@item @code{@var{lambda} = qz (@var{A}, @var{B})}\n\
+\n\
+Computes the generalized eigenvalues\n\
+@tex\n\
+$\\lambda$\n\
+@end tex\n\
+@ifnottex\n\
+@var{lambda}\n\
+@end ifnottex\n\
+of @math{(A - s B)}.\n\
+\n\
+@item @code{[AA, BB, Q, Z, V, W, @var{lambda}] = qz (@var{A}, @var{B})}\n\
+\n\
+Computes QZ@tie{}decomposition, generalized eigenvectors, and\n\
+generalized eigenvalues of @math{(A - s B)}\n\
+@tex\n\
+$$ AV = BV{ \\rm diag }(\\lambda) $$\n\
+$$ W^T A = { \\rm diag }(\\lambda)W^T B $$\n\
+$$ AA = Q^T AZ, BB = Q^T BZ $$\n\
+@end tex\n\
+@ifnottex\n\
+\n\
+@example\n\
+@group\n\
+\n\
+A * V = B * V * diag (@var{lambda})\n\
+W' * A = diag (@var{lambda}) * W' * B\n\
+AA = Q * A * Z, BB = Q * B * Z\n\
+\n\
+@end group\n\
+@end example\n\
+\n\
+@end ifnottex\n\
+with @var{Q} and @var{Z} orthogonal (unitary)= @var{I}\n\
+\n\
+@item @code{[AA,BB,Z@{, @var{lambda}@}] = qz (@var{A}, @var{B}, @var{opt})}\n\
+\n\
+As in form [2], but allows ordering of generalized eigenpairs\n\
+for (e.g.) solution of discrete time algebraic Riccati equations.\n\
+Form 3 is not available for complex matrices, and does not compute\n\
+the generalized eigenvectors @var{V}, @var{W}, nor the orthogonal matrix\n\
+@var{Q}.\n\
+\n\
+@table @var\n\
+@item opt\n\
+for ordering eigenvalues of the GEP pencil.  The leading block\n\
+of the revised pencil contains all eigenvalues that satisfy:\n\
+\n\
+@table @asis\n\
+@item \"N\"\n\
+= unordered (default)\n\
+\n\
+@item \"S\"\n\
+= small: leading block has all |lambda| @leq{} 1\n\
+\n\
+@item \"B\"\n\
+= big: leading block has all |lambda| @geq{} 1\n\
+\n\
+@item \"-\"\n\
+= negative real part: leading block has all eigenvalues\n\
+in the open left half-plane\n\
+\n\
+@item \"+\"\n\
+= non-negative real part: leading block has all eigenvalues\n\
+in the closed right half-plane\n\
+@end table\n\
+@end table\n\
+@end enumerate\n\
+\n\
+Note: @code{qz} performs permutation balancing, but not scaling\n\
+(@pxref{doc-balance}).  The order of output arguments was selected for\n\
+compatibility with @sc{matlab}.\n\
+@seealso{balance, eig, schur}\n\
+@end deftypefn")
+{
+  octave_value_list retval;
+  int nargin = args.length ();
+
+#ifdef DEBUG
+  std::cout << "qz: nargin = " << nargin << ", nargout = " << nargout << std::endl;
+#endif
+
+  if (nargin < 2 || nargin > 3 || nargout > 7)
+    {
+      print_usage ();
+      return retval;
+    }
+  else if (nargin == 3 && (nargout < 3 || nargout > 4))
+    {
+      error ("qz: invalid number of output arguments for form [3] call");
+      return retval;
+    }
+
+#ifdef DEBUG
+  std::cout << "qz: determine ordering option" << std::endl;
+#endif
+
+  // Determine ordering option.
+  volatile char ord_job = 0;
+  static double safmin;
+
+  if (nargin == 2)
+    ord_job = 'N';
+  else if (!args(2).is_string ())
+    {
+      error ("qz: OPT must be a string");
+      return retval;
+    }
+  else
+    {
+      std::string tmp = args(2).string_value ();
+
+      if (! tmp.empty ())
+        ord_job = tmp[0];
+
+      if (! (ord_job == 'N' || ord_job == 'n'
+             || ord_job == 'S' || ord_job == 's'
+             || ord_job == 'B' || ord_job == 'b'
+             || ord_job == '+' || ord_job == '-'))
+        {
+          error ("qz: invalid order option");
+          return retval;
+        }
+
+      // overflow constant required by dlag2
+      F77_FUNC (xdlamch, XDLAMCH) (F77_CONST_CHAR_ARG2 ("S", 1),
+                                   safmin
+                                   F77_CHAR_ARG_LEN (1));
+
+#ifdef DEBUG_EIG
+      std::cout << "qz: initial value of safmin=" << setiosflags (std::ios::scientific)
+           << safmin << std::endl;
+#endif
+
+      // Some machines (e.g., DEC alpha) get safmin = 0;
+      // for these, use eps instead to avoid problems in dlag2.
+      if (safmin == 0)
+        {
+#ifdef DEBUG_EIG
+          std::cout << "qz: DANGER WILL ROBINSON: safmin is 0!" << std::endl;
+#endif
+
+          F77_FUNC (xdlamch, XDLAMCH) (F77_CONST_CHAR_ARG2 ("E", 1),
+                                       safmin
+                                       F77_CHAR_ARG_LEN (1));
+
+#ifdef DEBUG_EIG
+          std::cout << "qz: safmin set to " << setiosflags (std::ios::scientific)
+               << safmin << std::endl;
+#endif
+        }
+    }
+
+#ifdef DEBUG
+  std::cout << "qz: check argument 1" << std::endl;
+#endif
+
+  // Argument 1: check if it's o.k. dimensioned.
+  octave_idx_type nn = args(0).rows ();
+
+#ifdef DEBUG
+  std::cout << "argument 1 dimensions: (" << nn << "," << args(0).columns () << ")"
+       << std::endl;
+#endif
+
+  int arg_is_empty = empty_arg ("qz", nn, args(0).columns ());
+
+  if (arg_is_empty < 0)
+    {
+      gripe_empty_arg ("qz: parameter 1", 0);
+      return retval;
+    }
+  else if (arg_is_empty > 0)
+    {
+      gripe_empty_arg ("qz: parameter 1; continuing", 0);
+      return octave_value_list (2, Matrix ());
+    }
+  else if (args(0).columns () != nn)
+    {
+      gripe_square_matrix_required ("qz");
+      return retval;
+    }
+
+  // Argument 1: dimensions look good; get the value.
+  Matrix aa;
+  ComplexMatrix caa;
+
+  if (args(0).is_complex_type ())
+    caa = args(0).complex_matrix_value ();
+  else
+    aa = args(0).matrix_value ();
+
+  if (error_state)
+    return retval;
+
+#ifdef DEBUG
+  std::cout << "qz: check argument 2" << std::endl;
+#endif
+
+  // Extract argument 2 (bb, or cbb if complex).
+  if ((nn != args(1).columns ()) || (nn != args(1).rows ()))
+    {
+      gripe_nonconformant ();
+      return retval;
+    }
+
+  Matrix bb;
+  ComplexMatrix cbb;
+
+  if (args(1).is_complex_type ())
+    cbb = args(1).complex_matrix_value ();
+  else
+    bb = args(1).matrix_value ();
+
+  if (error_state)
+    return retval;
+
+  // Both matrices loaded, now let's check what kind of arithmetic:
+  // declared volatile to avoid compiler warnings about long jumps,
+  // vforks.
+
+  volatile int complex_case
+    = (args(0).is_complex_type () || args(1).is_complex_type ());
+
+  if (nargin == 3 && complex_case)
+    {
+      error ("qz: cannot re-order complex qz decomposition");
+      return retval;
+    }
+
+  // First, declare variables used in both the real and complex case.
+  Matrix QQ(nn,nn), ZZ(nn,nn), VR(nn,nn), VL(nn,nn);
+  RowVector alphar(nn), alphai(nn), betar(nn);
+  ComplexRowVector xalpha(nn), xbeta(nn);
+  ComplexMatrix CQ(nn,nn), CZ(nn,nn), CVR(nn,nn), CVL(nn,nn);
+  octave_idx_type ilo, ihi, info;
+  char compq = (nargout >= 3 ? 'V' : 'N');
+  char compz = ((nargout >= 4 || nargin == 3)? 'V' : 'N');
+
+  // Initialize Q, Z to identity if we need either of them.
+  if (compq == 'V' || compz == 'V')
+    for (octave_idx_type ii = 0; ii < nn; ii++)
+      for (octave_idx_type jj = 0; jj < nn; jj++)
+        {
+          OCTAVE_QUIT;
+          QQ(ii,jj) = ZZ(ii,jj) = (ii == jj ? 1.0 : 0.0);
+        }
+
+  // Always perform permutation balancing.
+  const char bal_job = 'P';
+  RowVector lscale (nn), rscale (nn), work (6 * nn), rwork (nn);
+
+  if (complex_case)
+    {
+#ifdef DEBUG
+      if (compq == 'V')
+        std::cout << "qz: performing balancing; CQ=" << std::endl << CQ << std::endl;
+#endif
+      if (args(0).is_real_type ())
+        caa = ComplexMatrix (aa);
+
+      if (args(1).is_real_type ())
+        cbb = ComplexMatrix (bb);
+
+      if (compq == 'V')
+        CQ = ComplexMatrix (QQ);
+
+      if (compz == 'V')
+        CZ = ComplexMatrix (ZZ);
+
+      F77_XFCN (zggbal, ZGGBAL,
+                (F77_CONST_CHAR_ARG2 (&bal_job, 1),
+                 nn, caa.fortran_vec (), nn, cbb.fortran_vec (),
+                 nn, ilo, ihi, lscale.fortran_vec (),
+                 rscale.fortran_vec (), work.fortran_vec (), info
+                 F77_CHAR_ARG_LEN (1)));
+    }
+  else
+    {
+#ifdef DEBUG
+      if (compq == 'V')
+        std::cout << "qz: performing balancing; QQ=" << std::endl << QQ << std::endl;
+#endif
+
+      F77_XFCN (dggbal, DGGBAL,
+                (F77_CONST_CHAR_ARG2 (&bal_job, 1),
+                 nn, aa.fortran_vec (), nn, bb.fortran_vec (),
+                 nn, ilo, ihi, lscale.fortran_vec (),
+                 rscale.fortran_vec (), work.fortran_vec (), info
+                 F77_CHAR_ARG_LEN (1)));
+    }
+
+  // Since we just want the balancing matrices, we can use dggbal
+  // for both the real and complex cases; left first
+
+#if 0
+  if (compq == 'V')
+    {
+      F77_XFCN (dggbak, DGGBAK,
+                (F77_CONST_CHAR_ARG2 (&bal_job, 1),
+                 F77_CONST_CHAR_ARG2 ("L", 1),
+                 nn, ilo, ihi, lscale.data (), rscale.data (),
+                 nn, QQ.fortran_vec (), nn, info
+                 F77_CHAR_ARG_LEN (1)
+                 F77_CHAR_ARG_LEN (1)));
+
+#ifdef DEBUG
+      if (compq == 'V')
+        std::cout << "qz: balancing done; QQ=" << std::endl << QQ << std::endl;
+#endif
+  }
+
+  // then right
+  if (compz == 'V')
+    {
+      F77_XFCN (dggbak, DGGBAK,
+                (F77_CONST_CHAR_ARG2 (&bal_job, 1),
+                 F77_CONST_CHAR_ARG2 ("R", 1),
+                 nn, ilo, ihi, lscale.data (), rscale.data (),
+                 nn, ZZ.fortran_vec (), nn, info
+                 F77_CHAR_ARG_LEN (1)
+                 F77_CHAR_ARG_LEN (1)));
+
+#ifdef DEBUG
+      if (compz == 'V')
+        std::cout << "qz: balancing done; ZZ=" << std::endl << ZZ << std::endl;
+#endif
+    }
+#endif
+
+  static char qz_job;
+  qz_job = (nargout < 2 ? 'E' : 'S');
+
+  if (complex_case)
+    {
+      // Complex case.
+
+      // The QR decomposition of cbb.
+      ComplexQR cbqr (cbb);
+      // The R matrix of QR decomposition for cbb.
+      cbb = cbqr.R ();
+      // (Q*)caa for following work.
+      caa = (cbqr.Q ().hermitian ()) * caa;
+      CQ = CQ * cbqr.Q ();
+
+      F77_XFCN (zgghrd, ZGGHRD,
+                (F77_CONST_CHAR_ARG2 (&compq, 1),
+                 F77_CONST_CHAR_ARG2 (&compz, 1),
+                 nn, ilo, ihi, caa.fortran_vec (),
+                 nn, cbb.fortran_vec (), nn, CQ.fortran_vec (), nn,
+                 CZ.fortran_vec (), nn, info
+                 F77_CHAR_ARG_LEN (1)
+                 F77_CHAR_ARG_LEN (1)));
+
+      ComplexRowVector cwork (1 * nn);
+
+      F77_XFCN (zhgeqz, ZHGEQZ,
+                (F77_CONST_CHAR_ARG2 (&qz_job, 1),
+                 F77_CONST_CHAR_ARG2 (&compq, 1),
+                 F77_CONST_CHAR_ARG2 (&compz, 1),
+                 nn, ilo, ihi,
+                 caa.fortran_vec (), nn,
+                 cbb.fortran_vec (),nn,
+                 xalpha.fortran_vec (), xbeta.fortran_vec (),
+                 CQ.fortran_vec (), nn,
+                 CZ.fortran_vec (), nn,
+                 cwork.fortran_vec (), nn, rwork.fortran_vec (), info
+                 F77_CHAR_ARG_LEN (1)
+                 F77_CHAR_ARG_LEN (1)
+                 F77_CHAR_ARG_LEN (1)));
+
+      if (compq == 'V')
+        {
+          // Left eigenvector.
+          F77_XFCN (zggbak, ZGGBAK,
+                    (F77_CONST_CHAR_ARG2 (&bal_job, 1),
+                     F77_CONST_CHAR_ARG2 ("L", 1),
+                     nn, ilo, ihi, lscale.data (), rscale.data (),
+                     nn, CQ.fortran_vec (), nn, info
+                     F77_CHAR_ARG_LEN (1)
+                     F77_CHAR_ARG_LEN (1)));
+        }
+
+      // Right eigenvector.
+      if (compz == 'V')
+        {
+          F77_XFCN (zggbak, ZGGBAK,
+                    (F77_CONST_CHAR_ARG2 (&bal_job, 1),
+                     F77_CONST_CHAR_ARG2 ("R", 1),
+                     nn, ilo, ihi, lscale.data (), rscale.data (),
+                     nn, CZ.fortran_vec (), nn, info
+                     F77_CHAR_ARG_LEN (1)
+                     F77_CHAR_ARG_LEN (1)));
+        }
+
+    }
+  else
+    {
+#ifdef DEBUG
+      std::cout << "qz: peforming qr decomposition of bb" << std::endl;
+#endif
+
+      // Compute the QR factorization of bb.
+      QR bqr (bb);
+
+#ifdef DEBUG
+      std::cout << "qz: qr (bb) done; now peforming qz decomposition" << std::endl;
+#endif
+
+      bb = bqr.R ();
+
+#ifdef DEBUG
+      std::cout << "qz: extracted bb" << std::endl;
+#endif
+
+      aa = (bqr.Q ()).transpose () * aa;
+
+#ifdef DEBUG
+      std::cout << "qz: updated aa " << std::endl;
+      std::cout << "bqr.Q () = " << std::endl << bqr.Q () << std::endl;
+
+      if (compq == 'V')
+        std::cout << "QQ =" << QQ << std::endl;
+#endif
+
+      if (compq == 'V')
+        QQ = QQ * bqr.Q ();
+
+#ifdef DEBUG
+      std::cout << "qz: precursors done..." << std::endl;
+#endif
+
+#ifdef DEBUG
+      std::cout << "qz: compq = " << compq << ", compz = " << compz << std::endl;
+#endif
+
+      // Reduce  to generalized hessenberg form.
+      F77_XFCN (dgghrd, DGGHRD,
+                (F77_CONST_CHAR_ARG2 (&compq, 1),
+                 F77_CONST_CHAR_ARG2 (&compz, 1),
+                 nn, ilo, ihi, aa.fortran_vec (),
+                 nn, bb.fortran_vec (), nn, QQ.fortran_vec (), nn,
+                 ZZ.fortran_vec (), nn, info
+                 F77_CHAR_ARG_LEN (1)
+                 F77_CHAR_ARG_LEN (1)));
+
+      // Check if just computing generalized eigenvalues or if we're
+      // actually computing the decomposition.
+
+      // Reduce to generalized Schur form.
+      F77_XFCN (dhgeqz, DHGEQZ,
+                (F77_CONST_CHAR_ARG2 (&qz_job, 1),
+                 F77_CONST_CHAR_ARG2 (&compq, 1),
+                 F77_CONST_CHAR_ARG2 (&compz, 1),
+                 nn, ilo, ihi, aa.fortran_vec (), nn, bb.fortran_vec (),
+                 nn, alphar.fortran_vec (), alphai.fortran_vec (),
+                 betar.fortran_vec (), QQ.fortran_vec (), nn,
+                 ZZ.fortran_vec (), nn, work.fortran_vec (), nn, info
+                 F77_CHAR_ARG_LEN (1)
+                 F77_CHAR_ARG_LEN (1)
+                 F77_CHAR_ARG_LEN (1)));
+
+      if (compq == 'V')
+        {
+          F77_XFCN (dggbak, DGGBAK,
+                    (F77_CONST_CHAR_ARG2 (&bal_job, 1),
+                     F77_CONST_CHAR_ARG2 ("L", 1),
+                     nn, ilo, ihi, lscale.data (), rscale.data (),
+                     nn, QQ.fortran_vec (), nn, info
+                     F77_CHAR_ARG_LEN (1)
+                     F77_CHAR_ARG_LEN (1)));
+
+#ifdef DEBUG
+          if (compq == 'V')
+            std::cout << "qz: balancing done; QQ=" << std::endl << QQ << std::endl;
+#endif
+        }
+
+  // then right
+      if (compz == 'V')
+        {
+           F77_XFCN (dggbak, DGGBAK,
+                     (F77_CONST_CHAR_ARG2 (&bal_job, 1),
+                      F77_CONST_CHAR_ARG2 ("R", 1),
+                      nn, ilo, ihi, lscale.data (), rscale.data (),
+                      nn, ZZ.fortran_vec (), nn, info
+                      F77_CHAR_ARG_LEN (1)
+                      F77_CHAR_ARG_LEN (1)));
+
+#ifdef DEBUG
+           if (compz == 'V')
+             std::cout << "qz: balancing done; ZZ=" << std::endl << ZZ << std::endl;
+#endif
+        }
+
+    }
+
+  // Order the QZ decomposition?
+  if (! (ord_job == 'N' || ord_job == 'n'))
+    {
+      if (complex_case)
+        {
+          // Probably not needed, but better be safe.
+          error ("qz: cannot re-order complex qz decomposition");
+          return retval;
+        }
+      else
+        {
+#ifdef DEBUG_SORT
+          std::cout << "qz: ordering eigenvalues: ord_job = "
+                    << ord_job << std::endl;
+#endif
+
+          // Declared static to avoid vfork/long jump compiler complaints.
+          static sort_function sort_test;
+          sort_test = 0;
+
+          switch (ord_job)
+            {
+            case 'S':
+            case 's':
+              sort_test = &fin;
+              break;
+
+            case 'B':
+            case 'b':
+              sort_test = &fout;
+              break;
+
+            case '+':
+              sort_test = &fcrhp;
+              break;
+
+            case '-':
+              sort_test = &folhp;
+              break;
+
+            default:
+              // Invalid order option (should never happen, since we
+              // checked the options at the top).
+              panic_impossible ();
+              break;
+            }
+
+          octave_idx_type ndim, fail;
+          double inf_norm;
+
+          F77_XFCN (xdlange, XDLANGE,
+                    (F77_CONST_CHAR_ARG2 ("I", 1),
+                     nn, nn, aa.data (), nn, work.fortran_vec (), inf_norm
+                     F77_CHAR_ARG_LEN (1)));
+
+          double eps = DBL_EPSILON * inf_norm * nn;
+
+#ifdef DEBUG_SORT
+          std::cout << "qz: calling dsubsp: aa=" << std::endl;
+          octave_print_internal (std::cout, aa, 0);
+          std::cout << std::endl << "bb="  << std::endl;
+          octave_print_internal (std::cout, bb, 0);
+          if (compz == 'V')
+            {
+              std::cout << std::endl << "ZZ="  << std::endl;
+              octave_print_internal (std::cout, ZZ, 0);
+            }
+          std::cout << std::endl;
+          std::cout << "alphar = " << std::endl;
+          octave_print_internal (std::cout, (Matrix) alphar, 0);
+          std::cout << std::endl << "alphai = " << std::endl;
+          octave_print_internal (std::cout, (Matrix) alphai, 0);
+          std::cout << std::endl << "beta = " << std::endl;
+          octave_print_internal (std::cout, (Matrix) betar, 0);
+          std::cout << std::endl;
+#endif
+
+          Array<octave_idx_type> ind (dim_vector (nn, 1));
+
+          F77_XFCN (dsubsp, DSUBSP,
+                    (nn, nn, aa.fortran_vec (), bb.fortran_vec (),
+                     ZZ.fortran_vec (), sort_test, eps, ndim, fail,
+                     ind.fortran_vec ()));
+
+#ifdef DEBUG
+          std::cout << "qz: back from dsubsp: aa=" << std::endl;
+          octave_print_internal (std::cout, aa, 0);
+          std::cout << std::endl << "bb="  << std::endl;
+          octave_print_internal (std::cout, bb, 0);
+          if (compz == 'V')
+            {
+              std::cout << std::endl << "ZZ="  << std::endl;
+              octave_print_internal (std::cout, ZZ, 0);
+            }
+          std::cout << std::endl;
+#endif
+
+          // Manually update alphar, alphai, betar.
+          static int jj;
+
+          jj = 0;
+          while (jj < nn)
+            {
+#ifdef DEBUG_EIG
+              std::cout << "computing gen eig #" << jj << std::endl;
+#endif
+
+              // Number of zeros in this block.
+              static int zcnt;
+
+              if (jj == (nn-1))
+                zcnt = 1;
+              else if (aa(jj+1,jj) == 0)
+                zcnt = 1;
+              else zcnt = 2;
+
+              if (zcnt == 1)
+                {
+                  // Real zero.
+#ifdef DEBUG_EIG
+                  std::cout << "  single gen eig:" << std::endl;
+                  std::cout << "  alphar(" << jj << ") = " << aa(jj,jj) << std::endl;
+                  std::cout << "  betar( " << jj << ") = " << bb(jj,jj) << std::endl;
+                  std::cout << "  alphai(" << jj << ") = 0" << std::endl;
+#endif
+
+                  alphar(jj) = aa(jj,jj);
+                  alphai(jj) = 0;
+                  betar(jj) = bb(jj,jj);
+                }
+              else
+                {
+                  // Complex conjugate pair.
+#ifdef DEBUG_EIG
+                  std::cout << "qz: calling dlag2:" << std::endl;
+                  std::cout << "safmin="
+                       << setiosflags (std::ios::scientific) << safmin << std::endl;
+
+                  for (int idr = jj; idr <= jj+1; idr++)
+                    {
+                      for (int idc = jj; idc <= jj+1; idc++)
+                        {
+                          std::cout << "aa(" << idr << "," << idc << ")="
+                               << aa(idr,idc) << std::endl;
+                          std::cout << "bb(" << idr << "," << idc << ")="
+                               << bb(idr,idc) << std::endl;
+                        }
+                    }
+#endif
+
+                  // FIXME -- probably should be using
+                  // fortran_vec instead of &aa(jj,jj) here.
+
+                  double scale1, scale2, wr1, wr2, wi;
+                  const double *aa_ptr = aa.data () + jj * nn + jj;
+                  const double *bb_ptr = bb.data () + jj * nn + jj;
+                  F77_XFCN (dlag2, DLAG2,
+                            (aa_ptr, nn, bb_ptr, nn, safmin,
+                             scale1, scale2, wr1, wr2, wi));
+
+#ifdef DEBUG_EIG
+                  std::cout << "dlag2 returns: scale1=" << scale1
+                       << "\tscale2=" << scale2 << std::endl
+                       << "\twr1=" << wr1 << "\twr2=" << wr2
+                       << "\twi=" << wi << std::endl;
+#endif
+
+                  // Just to be safe, check if it's a real pair.
+                  if (wi == 0)
+                    {
+                      alphar(jj) = wr1;
+                      alphai(jj) = 0;
+                      betar(jj) = scale1;
+                      alphar(jj+1) = wr2;
+                      alphai(jj+1) = 0;
+                      betar(jj+1) = scale2;
+                    }
+                  else
+                    {
+                      alphar(jj) = alphar(jj+1) = wr1;
+                      alphai(jj) = -(alphai(jj+1) = wi);
+                      betar(jj)  = betar(jj+1) = scale1;
+                    }
+                }
+
+              // Advance past this block.
+              jj += zcnt;
+            }
+
+#ifdef DEBUG_SORT
+          std::cout << "qz: back from dsubsp: aa=" << std::endl;
+          octave_print_internal (std::cout, aa, 0);
+          std::cout << std::endl << "bb="  << std::endl;
+          octave_print_internal (std::cout, bb, 0);
+
+          if (compz == 'V')
+            {
+              std::cout << std::endl << "ZZ="  << std::endl;
+              octave_print_internal (std::cout, ZZ, 0);
+            }
+          std::cout << std::endl << "qz: ndim=" << ndim << std::endl
+               << "fail=" << fail << std::endl;
+          std::cout << "alphar = " << std::endl;
+          octave_print_internal (std::cout, (Matrix) alphar, 0);
+          std::cout << std::endl << "alphai = " << std::endl;
+          octave_print_internal (std::cout, (Matrix) alphai, 0);
+          std::cout << std::endl << "beta = " << std::endl;
+          octave_print_internal (std::cout, (Matrix) betar, 0);
+          std::cout << std::endl;
+#endif
+        }
+    }
+
+  // Compute generalized eigenvalues?
+  ComplexColumnVector gev;
+
+  if (nargout < 2 || nargout == 7 || (nargin == 3 && nargout == 4))
+    {
+      if (complex_case)
+        {
+          int cnt = 0;
+
+          for (int ii = 0; ii < nn; ii++)
+            cnt++;
+
+          ComplexColumnVector tmp (cnt);
+
+          cnt = 0;
+          for (int ii = 0; ii < nn; ii++)
+            tmp(cnt++) = xalpha(ii) / xbeta(ii);
+
+          gev = tmp;
+        }
+      else
+        {
+#ifdef DEBUG
+          std::cout << "qz: computing generalized eigenvalues" << std::endl;
+#endif
+
+          // Return finite generalized eigenvalues.
+          int cnt = 0;
+
+          for (int ii = 0; ii < nn; ii++)
+            if (betar(ii) != 0)
+              cnt++;
+
+          ComplexColumnVector tmp (cnt);
+
+          cnt = 0;
+          for (int ii = 0; ii < nn; ii++)
+            if (betar(ii) != 0)
+              tmp(cnt++) = Complex(alphar(ii), alphai(ii))/betar(ii);
+
+          gev = tmp;
+        }
+    }
+
+  // Right, left eigenvector matrices.
+  if (nargout >= 5)
+    {
+      // Which side to compute?
+      char side = (nargout == 5 ? 'R' : 'B');
+      // Compute all of them and backtransform
+      char howmny = 'B';
+      // Dummy pointer; select is not used.
+      octave_idx_type *select = 0;
+
+      if (complex_case)
+        {
+          CVL = CQ;
+          CVR = CZ;
+          ComplexRowVector cwork2 (2 * nn);
+          RowVector rwork2 (8 * nn);
+          octave_idx_type m;
+
+          F77_XFCN (ztgevc, ZTGEVC,
+                    (F77_CONST_CHAR_ARG2 (&side, 1),
+                     F77_CONST_CHAR_ARG2 (&howmny, 1),
+                     select, nn, caa.fortran_vec (), nn, cbb.fortran_vec (),
+                     nn, CVL.fortran_vec (), nn, CVR.fortran_vec (), nn, nn,
+                     m, cwork2.fortran_vec (), rwork2.fortran_vec (), info
+                     F77_CHAR_ARG_LEN (1)
+                     F77_CHAR_ARG_LEN (1)));
+        }
+      else
+        {
+#ifdef DEBUG
+          std::cout << "qz: computing  generalized eigenvectors" << std::endl;
+#endif
+
+          VL = QQ;
+          VR = ZZ;
+          octave_idx_type m;
+
+          F77_XFCN (dtgevc, DTGEVC,
+                    (F77_CONST_CHAR_ARG2 (&side, 1),
+                     F77_CONST_CHAR_ARG2 (&howmny, 1),
+                     select, nn, aa.fortran_vec (), nn, bb.fortran_vec (),
+                     nn, VL.fortran_vec (), nn, VR.fortran_vec (), nn, nn,
+                     m, work.fortran_vec (), info
+                     F77_CHAR_ARG_LEN (1)
+                     F77_CHAR_ARG_LEN (1)));
+
+          // Now construct the complex form of VV, WW.
+          int jj = 0;
+
+          while (jj < nn)
+            {
+              OCTAVE_QUIT;
+
+              // See if real or complex eigenvalue.
+
+              // Column increment; assume complex eigenvalue.
+              int cinc = 2;
+
+              if (jj == (nn-1))
+                // Single column.
+                cinc = 1;
+              else if (aa(jj+1,jj) == 0)
+                cinc = 1;
+
+              // Now copy the eigenvector (s) to CVR, CVL.
+              if (cinc == 1)
+                {
+                  for (int ii = 0; ii < nn; ii++)
+                    CVR(ii,jj) = VR(ii,jj);
+
+                  if (side == 'B')
+                    for (int ii = 0; ii < nn; ii++)
+                      CVL(ii,jj) = VL(ii,jj);
+                }
+              else
+                {
+                  // Double column; complex vector.
+
+                  for (int ii = 0; ii < nn; ii++)
+                    {
+                      CVR(ii,jj) = Complex (VR(ii,jj), VR(ii,jj+1));
+                      CVR(ii,jj+1) = Complex (VR(ii,jj), -VR(ii,jj+1));
+                    }
+
+                  if (side == 'B')
+                    for (int ii = 0; ii < nn; ii++)
+                      {
+                        CVL(ii,jj) = Complex (VL(ii,jj), VL(ii,jj+1));
+                        CVL(ii,jj+1) = Complex (VL(ii,jj), -VL(ii,jj+1));
+                      }
+                }
+
+              // Advance to next eigenvectors (if any).
+              jj += cinc;
+            }
+        }
+    }
+
+  switch (nargout)
+    {
+    case 7:
+      retval(6) = gev;
+
+    case 6:
+      // Return eigenvectors.
+      retval(5) = CVL;
+
+    case 5:
+      // Return eigenvectors.
+      retval(4) = CVR;
+
+    case 4:
+      if (nargin == 3)
+        {
+#ifdef DEBUG
+          std::cout << "qz: sort: retval(3) = gev = " << std::endl;
+          octave_print_internal (std::cout, gev);
+          std::cout << std::endl;
+#endif
+          retval(3) = gev;
+        }
+      else
+        {
+          if (complex_case)
+            retval(3) = CZ;
+          else
+            retval(3) = ZZ;
+        }
+
+    case 3:
+      if (nargin == 3)
+        {
+          if (complex_case)
+            retval(2) = CZ;
+          else
+            retval(2) = ZZ;
+        }
+      else
+        {
+          if (complex_case)
+            retval(2) = CQ.hermitian ();
+          else
+            retval(2) = QQ.transpose ();
+        }
+
+    case 2:
+      {
+        if (complex_case)
+          {
+#ifdef DEBUG
+            std::cout << "qz: retval(1) = cbb = " << std::endl;
+            octave_print_internal (std::cout, cbb, 0);
+            std::cout << std::endl << "qz: retval(0) = caa = " <<std::endl;
+            octave_print_internal (std::cout, caa, 0);
+            std::cout << std::endl;
+#endif
+            retval(1) = cbb;
+            retval(0) = caa;
+          }
+      else
+        {
+#ifdef DEBUG
+          std::cout << "qz: retval(1) = bb = " << std::endl;
+          octave_print_internal (std::cout, bb, 0);
+          std::cout << std::endl << "qz: retval(0) = aa = " <<std::endl;
+          octave_print_internal (std::cout, aa, 0);
+          std::cout << std::endl;
+#endif
+          retval(1) = bb;
+          retval(0) = aa;
+        }
+      }
+      break;
+
+
+    case 1:
+    case 0:
+#ifdef DEBUG
+      std::cout << "qz: retval(0) = gev = " << gev << std::endl;
+#endif
+      retval(0) = gev;
+      break;
+
+    default:
+      error ("qz: too many return arguments");
+      break;
+  }
+
+#ifdef DEBUG
+  std::cout << "qz: exiting (at long last)" << std::endl;
+#endif
+
+  return retval;
+}
+
+/*
+%!shared a, b, c
+%! a = [1 2; 0 3];
+%! b = [1 0; 0 0];
+%! c = [0 1; 0 0];
+%!assert (qz (a,b), 1)
+%!assert (isempty (qz (a,c)))
+
+## Exaple 7.7.3 in Golub & Van Loan
+%!test
+%! a = [ 10  1  2;
+%!        1  2 -1;
+%!        1  1  2];
+%! b = reshape (1:9,3,3);
+%! [aa, bb, q, z, v, w, lambda] = qz (a, b);
+%! sz = length (lambda);
+%! observed = (b * v * diag ([lambda;0])) (:, 1:sz);
+%! assert ( (a*v) (:, 1:sz), observed, norm (observed) * 1e-14);
+%! observed = (diag ([lambda;0]) * w' * b) (1:sz, :);
+%! assert ( (w'*a) (1:sz, :) , observed, norm (observed) * 1e-13);
+%! assert (q * a * z, aa, norm (aa) * 1e-14);
+%! assert (q * b * z, bb, norm (bb) * 1e-14);
+
+%!test
+%! A = [0, 0, -1, 0; 1, 0, 0, 0; -1, 0, -2, -1; 0, -1, 1, 0];
+%! B = [0, 0, 0, 0; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1];
+%! [AA, BB, Q, Z1] = qz (A, B);
+%! [AA, BB, Z2] = qz (A, B, '-');
+%! assert (Z1, Z2);
+*/