[cvxopt] 24/64: Imported Upstream version 1.1

Andreas Tille tille at debian.org
Wed Jul 20 11:23:50 UTC 2016


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository cvxopt.

commit c5f59dfcaa1a93c9fafa7f16526de0bd9933c817
Author: Andreas Tille <tille at debian.org>
Date:   Wed Jul 20 08:26:56 2016 +0200

    Imported Upstream version 1.1
---
 INSTALL                                            |    2 +-
 LICENSE                                            |    4 +-
 doc/Makefile                                       |    2 +-
 doc/base.tex                                       |  748 -------
 doc/base_sparse.tex                                |  689 -------
 doc/blas.tex                                       |   13 +-
 doc/c-api.tex                                      |    2 +-
 doc/coneprog.tex                                   |  199 +-
 doc/copyright.tex                                  |    4 +-
 doc/cvxopt.tex                                     |    7 +-
 doc/fftw.tex                                       |   96 +-
 doc/figures/floorplan.pdf                          |  Bin 17885 -> 19279 bytes
 doc/figures/normappr.pdf                           |  Bin 21648 -> 23044 bytes
 doc/figures/portfolio1.pdf                         |  Bin 21164 -> 22566 bytes
 doc/figures/portfolio2.pdf                         |  Bin 22820 -> 24206 bytes
 doc/intro.tex                                      |   32 +-
 doc/lapack.tex                                     |  207 +-
 doc/matrices.tex                                   | 1363 ++++++++++++
 doc/modeling.tex                                   |   22 +-
 doc/printing.tex                                   |   39 +-
 doc/solvers.tex                                    |  222 +-
 doc/spsolvers.tex                                  |   27 +-
 examples/book/README                               |    2 +-
 examples/book/chap4/portfolio                      |    2 +-
 examples/book/chap4/rls                            |    3 +-
 examples/book/chap6/basispursuit                   |    2 +-
 examples/book/chap6/consumerpref                   |    3 +-
 examples/book/chap6/cvxfit                         |    3 +-
 examples/book/chap6/huber                          |    3 +-
 examples/book/chap6/inputdesign                    |    3 +-
 examples/book/chap6/penalties                      |   17 +-
 examples/book/chap6/polapprox                      |    3 +-
 examples/book/chap6/regsel                         |    3 +-
 examples/book/chap6/robls                          |   16 +-
 examples/book/chap6/smoothrec                      |    5 +-
 examples/book/chap6/tv                             |    6 +-
 examples/book/chap7/chernoff                       |    5 +-
 examples/book/chap7/expdesign                      |    4 +-
 examples/book/chap7/logreg                         |    3 +-
 examples/book/chap7/maxent                         |    3 +-
 examples/book/chap7/probbounds                     |    3 +-
 examples/book/chap8/centers                        |    2 +-
 examples/book/chap8/ellipsoids                     |    3 +-
 examples/book/chap8/floorplan                      |    3 +-
 examples/book/chap8/linsep                         |    3 +-
 examples/book/chap8/placement                      |   11 +-
 examples/doc/chap10/l1svc                          |    4 +-
 examples/doc/chap10/lp                             |    2 +-
 examples/doc/chap10/normappr                       |    4 +-
 examples/doc/chap10/roblp                          |    4 +-
 examples/doc/chap4/acent                           |    3 +-
 examples/doc/chap7/covsel                          |    3 +-
 examples/doc/chap8/conelp                          |    3 +-
 examples/doc/chap8/coneqp                          |    3 +-
 examples/doc/chap8/l1                              |    8 +-
 examples/doc/chap8/l1regls                         |   12 +-
 examples/doc/chap8/lp                              |    3 +-
 examples/doc/chap8/mcsdp                           |    5 +-
 examples/doc/chap8/portfolio                       |    2 +-
 examples/doc/chap8/qcl1                            |    7 +-
 examples/doc/chap8/sdp                             |    3 +-
 examples/doc/chap8/socp                            |    3 +-
 examples/doc/chap9/acent                           |   13 +-
 examples/doc/chap9/acent2                          |    2 +-
 examples/doc/chap9/floorplan                       |    3 +-
 examples/doc/chap9/gp                              |    3 +-
 examples/doc/chap9/l2ac                            |   13 +-
 examples/doc/chap9/robls                           |   10 +-
 examples/filterdemo/filterdemo_cli                 |    2 +-
 examples/filterdemo/filterdemo_gui                 |    3 +-
 src/C/SuiteSparse/CHOLMOD/Check/cholmod_check.c    |  113 +-
 src/C/SuiteSparse/CHOLMOD/Check/cholmod_write.c    |    6 +-
 .../SuiteSparse/CHOLMOD/Cholesky/cholmod_analyze.c |   66 +-
 .../CHOLMOD/Cholesky/cholmod_postorder.c           |    4 +-
 src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_solve.c |   14 +-
 src/C/SuiteSparse/CHOLMOD/Core/cholmod_common.c    |   15 +
 src/C/SuiteSparse/CHOLMOD/Core/cholmod_complex.c   |    3 +
 src/C/SuiteSparse/CHOLMOD/Core/cholmod_error.c     |    4 +-
 src/C/SuiteSparse/CHOLMOD/Core/cholmod_memory.c    |   10 +-
 src/C/SuiteSparse/CHOLMOD/Doc/ChangeLog            |   17 +
 src/C/SuiteSparse/CHOLMOD/Doc/UserGuide.tex        |    4 +-
 src/C/SuiteSparse/CHOLMOD/Include/cholmod_blas.h   |  122 +-
 src/C/SuiteSparse/CHOLMOD/Include/cholmod_check.h  |   43 +-
 .../SuiteSparse/CHOLMOD/Include/cholmod_cholesky.h |   19 +
 src/C/SuiteSparse/CHOLMOD/Include/cholmod_core.h   |   52 +-
 .../SuiteSparse/CHOLMOD/Include/cholmod_internal.h |   43 +-
 .../CHOLMOD/Include/cholmod_supernodal.h           |   25 +
 src/C/SuiteSparse/CHOLMOD/Makefile                 |    2 +-
 .../CHOLMOD/Supernodal/cholmod_super_solve.c       |   21 +-
 .../CHOLMOD/Supernodal/cholmod_super_symbolic.c    |  192 +-
 .../CHOLMOD/Supernodal/t_cholmod_super_numeric.c   |    6 +-
 .../CHOLMOD/Supernodal/t_cholmod_super_solve.c     |   15 +-
 src/C/SuiteSparse/Contents.m                       |   10 +-
 src/C/SuiteSparse/Makefile                         |   14 +-
 src/C/SuiteSparse/README.txt                       |   45 +-
 src/C/SuiteSparse/README_cvxopt                    |    7 +-
 src/C/SuiteSparse/UFconfig/README.txt              |    1 +
 src/C/SuiteSparse/UFconfig/UFconfig.h              |    9 +-
 src/C/SuiteSparse/UFconfig/UFconfig.mk             |   57 +-
 src/C/SuiteSparse/UMFPACK/Doc/ChangeLog            |    4 +
 src/C/SuiteSparse/UMFPACK/Doc/License              |    2 +-
 src/C/SuiteSparse/UMFPACK/Doc/QuickStart.tex       |    2 +-
 src/C/SuiteSparse/UMFPACK/Doc/UserGuide.stex       |    3 +-
 src/C/amd.c                                        |    2 +-
 src/C/base.c                                       |  743 ++++++-
 src/C/blas.c                                       |  176 +-
 src/C/blas_redefines.h                             |    5 +
 src/C/cholmod.c                                    |    2 +-
 src/C/cvxopt.h                                     |    2 +-
 src/C/dense.c                                      |  197 +-
 src/C/dsdp.c                                       |   45 +-
 src/C/fftw.c                                       |  836 ++++++--
 src/C/glpk.c                                       |    3 +-
 src/C/gsl.c                                        |    2 +-
 src/C/lapack.c                                     |  739 ++++++-
 src/C/misc.h                                       |    3 +-
 src/C/misc_solvers.c                               |    2 +-
 src/C/sparse.c                                     |   82 +-
 src/C/umfpack.c                                    |    2 +-
 src/python/__init__.py                             |  166 +-
 src/python/coneprog.py                             | 2173 +++++++++++++++++---
 src/python/cvxprog.py                              |  301 ++-
 src/python/info.py                                 |    6 +-
 src/python/misc.py                                 |    2 +-
 src/python/modeling.py                             |    4 +-
 src/python/mosek.py                                |   39 +-
 src/python/printing.py                             |    4 +-
 src/python/solvers.py                              |    2 +-
 src/setup.py                                       |    2 +-
 129 files changed, 7568 insertions(+), 2829 deletions(-)

diff --git a/INSTALL b/INSTALL
index 33b1616..3151188 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,4 +1,4 @@
-Installation instructions for CVXOPT Version 1.0.
+Installation instructions for CVXOPT Version 1.1.
 
 The package requires version 2.5 or newer of Python, and is built from 
 source, so the header files and libraries for Python must be installed, 
diff --git a/LICENSE b/LICENSE
index f49560f..84eaa3e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-CVXOPT version 1.0.  Copyright (c) 2004-2008 J. Dahl and L. Vandenberghe.
+CVXOPT version 1.1.  Copyright (c) 2004-2008 J. Dahl and L. Vandenberghe.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@ SuiteSparse suite of sparse matrix algorithms, including:
 
 - AMD Version 2.2.0. Copyright (c) 2007 by Timothy A. Davis, Patrick R. 
   Amestoy, and Iain S. Duff.
-- CHOLMOD Version 1.6.0 Copyright (c) 2005-2007 by University of Florida,
+- CHOLMOD Version 1.7.0 Copyright (c) 2005-2007 by University of Florida,
   Timothy A. Davis and W. Hager.
 - COLAMD version 2.7.0. Copyright (c) 1998-2007 by Timothy A. Davis.
 - UMFPACK Version 5.2.0. Copyright (c) 1995-2006 by Timothy A. Davis.
diff --git a/doc/Makefile b/doc/Makefile
old mode 100644
new mode 100755
index 9792ae7..fa24b80
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -1,4 +1,4 @@
-SOURCES	= cvxopt.tex intro.tex base.tex blas.tex lapack.tex \
+SOURCES	= cvxopt.tex intro.tex matrices.tex blas.tex lapack.tex fftw.tex \
 	spsolvers.tex modeling.tex solvers.tex coneprog.tex c-api.tex 
 
 all: html 
diff --git a/doc/base.tex b/doc/base.tex
deleted file mode 100644
index 6439b25..0000000
--- a/doc/base.tex
+++ /dev/null
@@ -1,748 +0,0 @@
-\chapter{Dense Matrices (\module{cvxopt.base})}\label{chap:matrix}
-
-The \module{cvxopt.base} module defines two new Python types: 
-\mtrx\ objects, used for dense matrix computations, and 
-\spmtrx\ objects, used for sparse matrix computations.
-In this chapter we describe the dense \mtrx\ object.
-Sparse matrices are discussed in chapter~\ref{chap:spmatrix}.
-
-\section{Creating Matrices}\label{s-creating-matrices}
-A \mtrx\ object is created by calling the function \function{matrix()}. 
-The arguments specify the values of the coefficients, the
-dimensions, and the type (integer, double or complex) of the matrix.
-
-\begin{funcdesc}{matrix}{x\optional{, size\optional{, tc}}}
-\var{size} is a tuple of length two with the matrix dimensions.
-The number of rows and/or the number of columns can be zero.
-
-\var{tc} stands for typecode. The possible values are \itc, \dtc\ and 
-\ztc, for integer, real (double) and complex matrices, respectively.  
-
-\var{x} can be a number, a sequence of numbers, a dense or sparse 
-matrix, a one- or two-dimensional \program{NumPy} array, or a list of
-lists of matrices and numbers.  
-\BIT
-\item If \var{x} is a number (Python \intgr, \flt\ or \cmplx), a matrix
-is created with the dimensions specified by \var{size} and with all the 
-coefficients equal to \var{x}.  
-The default value of \var{size} is \tm{(1,1)}, and the default value
-of \var{tc} is the type of \var{x}.
-If necessary, the type of \var{x} is converted (from integer to double
-when used to create a matrix of type \dtc, and from integer or
-double to complex when used to create a matrix of type \ztc).
-
-\begin{verbatim}
->>> from cvxopt.base import matrix
->>> A = matrix(1, (1,4))   
->>> print A
-[ 1  1  1  1]
->>> A = matrix(1.0, (1,4))   
->>> print A
-[ 1.00e+00  1.00e+00  1.00e+00  1.00e+00]
->>> A = matrix(1+1j)     
->>> print A
-[ 1.00e+00+j1.00e+00]
-\end{verbatim}
-
-\item If \var{x} is a sequence of numbers (list, tuple, \module{array}
-array, xrange object, one-dimensional \program{NumPy} array, \ldots),
-then the numbers are interpreted as the coefficients of a matrix in 
-column-major order.  The length of \var{x} must be equal to the product 
-of \code{size[0]} and \code{size[1]}.
-If \var{size} is not specified, a matrix with one column is created. 
-If \var{tc} is not specified, it is determined from the elements of 
-\var{x} (and if that is impossible, for example because \var{x} is
-an empty list, a value \itc\ is used).  
-Type conversion takes place as for scalar \var{x}.
-
-The following example shows several ways to define the same integer 
-matrix.
-\begin{verbatim}
->>> A = matrix([0, 1, 2, 3], (2,2))  
->>> A = matrix((0, 1, 2, 3), (2,2))  
->>> A = matrix(xrange(4), (2,2))
->>> from array import array
->>> A = matrix(array('i', [0,1,2,3]), (2,2))
->>> print A
-[ 0  2]
-[ 1  3]
-\end{verbatim}
-
-\item If \var{x} is a dense or sparse matrix (a \mtrx\ or a \spmtrx\ 
-object), or a two-dimensional \program{NumPy} array of type \itc, 
-\dtc\ or \ztc, then the  coefficients of \var{x} are copied, in 
-column-major order, to a new matrix of the given size.  
-The total number of elements in the new matrix (the product of 
-\code{size[0]} and \code{size[1]}) must be the same as the product of  
-the dimensions of \var{x}.  If \var{size} is not specified, the 
-dimensions of \var{x} are used.  
-The default value of \var{tc} is the type of \var{x}. 
-Type conversion takes place when the type of \var{x} differs from 
-\var{tc}, in a similar way as for scalar \var{x}.  
-
-\begin{verbatim}
->>> A = matrix([1., 2., 3., 4., 5., 6.], (2,3))  
->>> print A
-[ 1.00e+00  3.00e+00  5.00e+00]
-[ 2.00e+00  4.00e+00  6.00e+00]
->>> B = matrix(A, (3,2))  
->>> print B
-[ 1.00e+00  4.00e+00]
-[ 2.00e+00  5.00e+00]
-[ 3.00e+00  6.00e+00]
->>> C = matrix(B, tc='z')      
->>> print C
-[ 1.00e+00-j0.00e+00  4.00e+00-j0.00e+00]
-[ 2.00e+00-j0.00e+00  5.00e+00-j0.00e+00]
-[ 3.00e+00-j0.00e+00  6.00e+00-j0.00e+00]
->>> from numpy import array
->>> x = array([[1., 2., 3.], [4., 5., 6.]])
->>> x
-array([[ 1.  2.  3.]
-       [ 4.  5.  6.]])
->>> print matrix(x)
-[ 1.00e+00  2.00e+00  3.00e+00]
-[ 4.00e+00  5.00e+00  6.00e+00]
-\end{verbatim}
-
-\item If \var{x} is a list of lists of matrices 
-(\mtrx\ or \spmtrx\ objects) or numbers (Python \intgr, \flt\ or 
-\cmplx), then each element of \var{x} is interpreted as a 
-block-column stored in column-major order. 
-If \var{size} is not specified, the block-columns are juxtaposed
-to obtain a matrix with \code{len(\var{x})} block-columns.
-If \var{size} is specified, then the matrix with \code{len(\var{x})}
-block-columns is resized by copying its elements in column-major order 
-into a matrix of the dimensions given by \var{size}.  
-If \var{tc} is not specified, it is determined from the elements of 
-\var{x} (and if that is impossible, for example because \var{x} is
-a list of empty lists, a value \itc\ is used).  
-The same rules for type conversion apply as for scalar \var{x}.
-
-\begin{verbatim}
->>> print matrix([[1., 2.], [3., 4.], [5., 6.]])
-[ 1.00e+00  3.00e+00  5.00e+00]
-[ 2.00e+00  4.00e+00  6.00e+00]
->>> A1 = matrix([1, 2], (2,1))
->>> B1 = matrix([6, 7, 8, 9, 10, 11], (2,3))
->>> B2 = matrix([12, 13, 14, 15, 16, 17], (2,3))
->>> B3 = matrix([18, 19, 20], (1,3))
->>> C = matrix([[A1, 3.0, 4.0, 5.0], [B1, B2, B3]])
->>> print C
-[ 1.00e+00  6.00e+00  8.00e+00  1.00e+01]
-[ 2.00e+00  7.00e+00  9.00e+00  1.10e+01]
-[ 3.00e+00  1.20e+01  1.40e+01  1.60e+01]
-[ 4.00e+00  1.30e+01  1.50e+01  1.70e+01]
-[ 5.00e+00  1.80e+01  1.90e+01  2.00e+01]
-\end{verbatim}
-
-A matrix with a single block-column can be represented by a single 
-list (\ie, when the length of \var{x} is one, it can be replaced with
-\code{\var{x}[0]}).
-\begin{verbatim}
->>> D = matrix([B1, B2, B3])
->>> print D
-[  6   8  10]
-[  7   9  11]
-[ 12  14  16]
-[ 13  15  17]
-[ 18  19  20]
-\end{verbatim}
-\EIT
-\end{funcdesc}
-
-\section{Attributes and Methods} 
-A \mtrx\ has the following attributes.
-
-\begin{memberdesc}{size}
-A tuple with the dimensions of the matrix. The size of the matrix
-can be changed by altering this attribute, as long as the number
-of elements in the matrix remains unchanged.  
-\end{memberdesc} 
-
-\begin{memberdesc}{typecode}
-A \ctype{char}, either \itc, \dtc, or \ztc, for integer, real
-and complex matrices, respectively.  A read-only attribute.
-\end{memberdesc} 
-
-\begin{methoddesc}{trans}
-Returns the transpose of the matrix as a new matrix.
-One can also use \code{A.T} instead of \code{A.trans()}.
-\end{methoddesc}
-
-\begin{methoddesc}{ctrans}
-Returns the conjugate transpose of the matrix as a new matrix.
-One can also use \code{A.H} instead of \code{A.ctrans()}.
-\end{methoddesc}
-
-\begin{methoddesc}{real}
-For complex matrices, returns the real part as a real matrix.
-For integer and real matrices, returns a copy of the matrix.
-\end{methoddesc}
-
-\begin{methoddesc}{imag}
-For complex matrices, returns the imaginary part as a real matrix.
-For integer and real matrices, returns an integer or real zero matrix.
-\end{methoddesc}
-
-\begin{memberdesc}{\_\_array\_struct\_\_}
-A PyCObject implementing the \program{NumPy} array interface  
-(see section~\ref{s-array-interface} for details).
-\end{memberdesc} 
-
-\begin{funcdesc}{tofile}{f}
-Writes the elements of the matrix in column-major order to a binary 
-file \var{f}. 
-\end{funcdesc}
-
-\begin{funcdesc}{fromfile}{f}
-Reads the contents of a binary file \var{f} into the matrix object.
-\end{funcdesc}
-
-The last two methods are illustrated in the following example.
-\begin{verbatim}
->>> from cvxopt.base import matrix
->>> A = matrix([[1.,2.,3.], [4.,5.,6.]])  
->>> print A
-[ 1.00e+00  4.00e+00]
-[ 2.00e+00  5.00e+00]
-[ 3.00e+00  6.00e+00]
->>> f = open('mat.bin','w')
->>> A.tofile(f)
->>> f.close()
->>> B = matrix(0.0, (2,3))
->>> f = open('mat.bin','r')
->>> B.fromfile(f)
->>> f.close()
->>> print B
-[ 1.00e+00  3.00e+00  5.00e+00]
-[ 2.00e+00  4.00e+00  6.00e+00]
-\end{verbatim}
-
-Matrices can also be written to or read from files using the 
-\function{dump()} and \function{load()} functions in the 
-\module{pickle} module. 
-
-
-\section{Arithmetic Operations} \label{s-arithmetic}
-The following table lists the arithmetic operations defined for 
-dense matrices.  In this table \var{A} and \var{B} are dense matrices 
-with compatible dimensions, \var{c} is a scalar (a Python number 
-or a dense 1 by 1 matrix), and \var{d} is a Python number.
-
-\begin{center}
-\begin{tabular}{l|l}
-\hline
-Unary plus/minus & \code{+\var{A}}, \code{-\var{A}} \\ \hline 
-Addition & \code{\var{A}+\var{B}}, \code{\var{A}+\var{c}}, 
-    \code{\var{c}+\var{A}}\\ \hline 
-Subtraction & \code{\var{A}-\var{B}}, \code{\var{A}-\var{c}}, 
-    \code{\var{c}-\var{A}}\\ \hline 
-Matrix multiplication & \code{{\var{A}*\var{B}}} \\ \hline
-Scalar multiplication and division & \code{\var{c}*\var{A}}, 
-    \code{\var{A}*\var{c}}, \code{\var{A}/\var{c}} \\ \hline 
-Remainder after division & \code{\var{A}\%\var{c}} \\ \hline 
-Elementwise exponentiation  & \code{\var{A}**\var{d}} \\ \hline 
-\end{tabular}
-\end{center}
-
-If \var{c} in the expressions \code{\var{A}+\var{c}}, 
-\code{\var{c}+\var{A}}, \code{\var{A}-\var{c}}, 
-\code{\var{c}-\var{A}} is a number, then it is interpreted as a 
-matrix with the same dimensions as \var{A}, type given by the type 
-of \var{c}, and all entries equal to \var{c}.
-If \var{c}  is a 1 by 1 matrix and \var{A} is not 1 by 1, then \var{c} 
-is interpreted as a matrix with the same size of \var{A} and all 
-entries equal to \code{\var{c}[0]}.
-
-Postmultiplying a matrix with a number \var{c} means the same as 
-premultiplying, \ie, scalar multiplication.  Dividing a matrix by 
-\var{c} means dividing all entries by \var{c}.  
-If \var{c} is a 1 by 1 matrix and the product
-\code{\var{c}*\var{A}} or \code{\var{A}*\var{c}} cannot be 
-interpreted as a matrix-matrix product, then it is interpreted as 
-\code{\var{c}[0]*\var{A}}. 
-The division \code{\var{A}/\var{c}} and remainder 
-\code{\var{A}\%\var{c}} with \var{c} a 1 by 1 matrix are always 
-interpreted as \code{\var{A}/\var{c}[0]}, resp.,
-\code{\var{A}\%\var{c}[0]}.
-
-If one of the operands in the arithmetic operations is integer 
-(a scalar \intgr\ or a matrix of type \itc) and the other operand 
-is double (a scalar \flt\ or a matrix of type \dtc), then the 
-integer operand is converted to double, and the result is a matrix of 
-type \dtc.
-If one of the operands is integer or double, and the other operand is 
-complex (a scalar \cmplx\ or a matrix of type \ztc), 
-then the first operand is converted to complex, and the result is 
-a matrix of type \ztc.  
-
-The result of \code{\var{A}**\var{d}} is a complex matrix
-if \var{A} or \var{d} are complex, and real otherwise.
-
-Note that Python rounds the result of an integer division towards minus 
-infinity.
-
-The following in-place operations are also defined, but only if 
-they do not change the type of the matrix \var{A}: 
-\begin{center}\begin{tabular}{l|l} \hline
- In-place addition & 
-    \code{\var{A}+=\var{B}}, \code{\var{A}+=\var{c}} \\ \hline
- In-place subtraction & 
-    \code{\var{A}-=\var{B}}, \code{\var{A}-=\var{c}} \\ \hline   
- In-place scalar multiplication and division & 
-    \code{\var{A}*=\var{c}}, \code{\var{A}/=\var{c}} \\ \hline
- In-place remainder & \code{\var{A}\%=\var{c}} \\ \hline
-\end{tabular}\end{center}
-
-For example, if \var{A} has type \itc, then \code{\var{A}+=\var{B}} 
-is allowed if \var{B} has type \itc.
-It is not allowed if \var{B} has type \dtc or \ztc because the 
-addition \code{\var{A}+\var{B}} results in a matrix of 
-type \dtc or \ztc and therefore cannot be assigned to \var{A} 
-without changing its type.
-
-In-place matrix-matrix products are not allowed.  (Except when
-\var{c} is a 1 by 1 matrix, in which case \code{\var{A}*=\var{c}} is 
-interpreted as the scalar product \code{\var{A}*=\var{c}[0]}.)
-
-It is important to know when a matrix operation creates
-a new object.  The following rules apply.
-\begin{itemize}
-\item A simple assignment (\samp{A = B}) is given the standard 
-Python interpretation, \ie, it assigns to the variable \var{A} a 
-reference (or pointer) to the object referenced by \var{B}.
-\begin{verbatim}
->>> B = matrix([[1.,2.], [3.,4.]])  
->>> print B
-[ 1.00e+00  3.00e+00]
-[ 2.00e+00  4.00e+00]
->>> A = B
->>> A[0,0] = -1 
->>> print B   # modifying A[0,0] also modified B[0,0]
-[-1.00e+00  3.00e+00]
-[ 2.00e+00  4.00e+00]
-\end{verbatim}
-
-\item The regular (\ie, not in-place) arithmetic operations always 
-return new objects.   Hence \samp{A = +B} is equivalent to 
-\samp{A = matrix(B)}.
-\begin{verbatim}
->>> B = matrix([[1.,2.], [3.,4.]])  
->>> A = +B
->>> A[0,0] = -1 
->>> print B   # modifying A[0,0] does not modify B[0,0]
-[ 1.00e+00  3.00e+00]
-[ 2.00e+00  4.00e+00]
-\end{verbatim}
-
-\item  The in-place operations directly modify the coefficients of the 
- existing matrix object and do not create a new object. 
-\begin{verbatim}
->>> B = matrix([[1.,2.], [3.,4.]])  
->>> A = B
->>> A *= 2
->>> print B   # in-place operation also changed B
-[ 2.00e+00  6.00e+00]
-[ 4.00e+00  8.00e+00]
->>> A = 2*A
->>> print B   # regular operation creates a new A, so does not change B
-[ 2.00e+00  6.00e+00]
-[ 4.00e+00  8.00e+00]
-\end{verbatim}
-\end{itemize}
-
-%The restrictions on in-place operations follow the principle that once 
-%a matrix object is created, its size and type cannot be modified.  
-%The only matrix attributes that can be changed are the values of the 
-%elements.  The values can be changed by in-place operations or by an 
-%indexed assignment, as explained in the next section. 
-
-\section{Indexing and Slicing} \label{s-indexing}
-
-Matrices can be indexed using one or two arguments.  In single-argument
-indexing of a matrix \var{A}, the index runs from 
-\code{-len(\var{A})} to \code{len(\var{A})-1}, and is interpreted as an
-index in the one-dimensional array of coefficients of \var{A} 
-in column-major order.   Negative indices have the standard Python 
-interpretation: for negative \var{k}, \code{\var{A}[\var{k}]} is the 
-same element as  \code{\var{A}[len(\var{A})+\var{k}]}.
-
-Four different types of one-argument indexing are implemented.
-\begin{enumerate}
-\item The index can be a single integer.  This returns a 
-number, \eg, \code{\var{A}[0]} is the first element of \var{A}.
-
-\item The index can be an integer matrix.  This returns a 
-column matrix: the command \samp{A[matrix([0,1,2,3])]} 
-returns the 4 by 1 matrix consisting of the first four elements of
-\var{A}.   The size of the index matrix is ignored: 
-\samp{A[matrix([0,1,2,3], (2,2))]} returns the same 4 by 1 matrix.
-
-\item The index can be a list of integers.  This returns a column 
-matrix, \eg, \code{\var{A}[[0,1,2,3]]} is the 4 by 1 matrix consisting 
-of elements 0, 1, 2, 3 of \var{A}.   
-
-\item The index can be a Python slice.  This returns a matrix with one 
-column (possibly 0 by 1, or 1 by 1).  For example, \code{\var{A}[::2]} 
-is the column matrix defined by taking every other element of \var{A}, 
-stored in column-major order.  
-\code{\var{A}[0:0]} is a matrix with size (0,1).
-\end{enumerate}
-Thus, single-argument indexing returns a scalar (if the index is an 
-integer), or a matrix with one column.  This is consistent with the 
-interpretation that single-argument indexing accesses the matrix in 
-column-major order.
-
-Note that an index list or an index matrix are equivalent,
-but they are both useful, especially when we perform operations on 
-index sets.  For example, if \var{I} and \var{J} are lists then 
-\code{\var{I}+\var{J}} is the concatenated list, and \code{2*\var{I}} 
-is \var{I} repeated twice.  If they are matrices, these operations are 
-interpreted as arithmetic operations.
-For large index sets, indexing with integer matrices is also faster 
-than indexing with lists. 
-
-The following example illustrates one-argument indexing.
-\begin{verbatim}
->>> from cvxopt.base import matrix 
->>> A = matrix(range(16), (4,4), 'd')
->>> print A
-[ 0.00e+00  4.00e+00  8.00e+00  1.20e+01]
-[ 1.00e+00  5.00e+00  9.00e+00  1.30e+01]
-[ 2.00e+00  6.00e+00  1.00e+01  1.40e+01]
-[ 3.00e+00  7.00e+00  1.10e+01  1.50e+01]
->>> A[4]
-4.0
->>> I = matrix([0, 5, 10, 15])
->>> print A[I]      # the diagonal
-[ 0.00e+00]
-[ 5.00e+00]
-[ 1.00e+01]
-[ 1.50e+01]
->>> I = [0,2];  J = [1,3]
->>> print A[2*I+J]  # duplicate I and append J
-[ 0.00e+00]
-[ 2.00e+00]
-[ 0.00e+00]
-[ 2.00e+00]
-[ 1.00e+00]
-[ 3.00e+00]
->>> I = matrix([0, 2]);  J =  matrix([1, 3])
->>> print A[2*I+J]  # multiply I by 2 and add J
-[ 1.00e+00]
-[ 7.00e+00]
->>> print A[4::4]   # get every fourth element skipping the first four  
-[ 4.00e+00]
-[ 8.00e+00]
-[ 1.20e+01]
-\end{verbatim}
-
-In two-argument indexing the arguments can be any combinations of the
-four types listed above.  The first argument indexes the rows of 
-the matrix and the second argument indexes the columns.  If both 
-indices are scalars, then a scalar is returned.  In all other cases, 
-a matrix is returned.  We continue the example.
-\begin{verbatim}
->>> print A[:,1]
-[ 4.00e+00]
-[ 5.00e+00]
-[ 6.00e+00]
-[ 7.00e+00]
->>> J = matrix([0, 2])
->>> print A[J,J]
-[ 0.00e+00  8.00e+00]
-[ 2.00e+00  1.00e+01]
->>> print A[:2, -2:]   
-[ 8.00e+00  1.20e+01]
-[ 9.00e+00  1.30e+01]
-\end{verbatim}
-
-Expressions of the form \code{\var{A}[\var{I}]} or 
-\code{\var{A}[\var{I},\var{J}]} can also appear on the lefthand side 
-of an assignment.   
-The righthand side must be a scalar (\ie, a number or a 1 by 1 dense
-matrix), a sequence of numbers, or a dense or sparse matrix. 
-If the righthand side is a scalar, it is interpreted as a 
-matrix with identical entries and the dimensions of the lefthand side.
-If the righthand side is a sequence of numbers (list, tuple, 
-\module{array} array, xrange object, \ldots) its values are 
-interpreted as the coefficients of the lefthand side in column-major 
-order.  If the righthand side is a matrix (\mtrx\ or \spmtrx), it must 
-have the same size as the lefthand side.  Sparse matrices are 
-converted to dense in the assignment.
-
-Indexed assignments are only allowed if they do not change the type of 
-the matrix.  For example, if \var{A} is a matrix with type \dtc, then 
-\code{\var{A}[\var{I}] = \var{B}} is only permitted if \var{B} is 
-an \intgr, a \flt, or a matrix of type \itc\ or \dtc.
-If \var{A} is an integer matrix, then \code{\var{A}[\var{I}] = \var{B}} 
-is only permitted if \var{B} is an \intgr\  or an integer matrix.
-
-The following example illlustrates indexed assignment.
-\begin{verbatim}
->>> A = matrix(range(16), (4,4))
->>> A[::2,::2] = matrix([[-1, -2], [-3, -4]])
->>> print A
-[ -1   4  -3  12]
-[  1   5   9  13]
-[ -2   6  -4  14]
-[  3   7  11  15]
->>> A[::5] += 1
->>> print A
-[  0   4  -3  12]
-[  1   6   9  13]
-[ -2   6  -3  14]
-[  3   7  11  16]
->>> A[0,:] = -1, 1, -1, 1
->>> print A
-[ -1   1  -1   1]
-[  1   6   9  13]
-[ -2   6  -3  14]
-[  3   7  11  16]
->>> A[2:,2:] = xrange(4)
->>> print A
-[ -1   1  -1   1]
-[  1   6   9  13]
-[ -2   6   0   2]
-[  3   7   1   3]
-\end{verbatim}
-
-\section{Built-in Functions} \label{s-builtinfuncs}
-Many Python built-in functions and operations can be used with matrix 
-arguments.  We list some useful examples.
-
-\begin{funcdesc}{len}{x}
-Returns the product of the number of rows and the number of columns. 
-\end{funcdesc}
-
-\begin{funcdesc}{bool}{\optional{x}}
-Returns \False\ if \var{x} is empty (\ie, \code{len(\var{x})} is zero) 
-and \True\ otherwise.
-\end{funcdesc}
-
-\begin{funcdesc}{max}{x}
-Returns the maximum element of \var{x}.
-\end{funcdesc}
-
-\begin{funcdesc}{min}{x}
-Returns the minimum element of \var{x}.
-\end{funcdesc}
-
-\begin{funcdesc}{abs}{x}
-Returns a matrix with the absolute values of the elements of \var{x}.
-\end{funcdesc}
-
-\begin{funcdesc}{sum}{x\optional{, start=0.0}}
-Returns the sum of \var{start} and the elements of \var{x}.
-\end{funcdesc}
-
-Matrices can be used as  arguments to the \function{list()}, 
-\function{tuple()}, \function{zip()}, \function{map()}, and 
-\function{filter()} functions described in section 2.1 of the Python 
-Library Reference.  \code{list(\var{A})} and \code{tuple(\var{A})} 
-construct a list, respectively a tuple, from the elements of \var{A}.
-\code{zip(\var{A},\var{B},\ldots)} returns a list of tuples, 
-with the {\it i}th tuple containing the {\it i}th elements of \var{A}, 
-\var{B}, \ldots.
-
-\begin{verbatim}
->>> from cvxopt.base import matrix
->>> A = matrix([[-11., -5., -20.], [-6., -0., 7.]])
->>> B = matrix(range(6), (3,2))
->>> list(A)
-[-11.0, -5.0, -20.0, -6.0, 0.0, 7.0]
->>> tuple(B)
-(0, 1, 2, 3, 4, 5)
->>> zip(A,B)
-[(-11.0, 0), (-5.0, 1), (-20.0, 2), (-6.0, 3), (0.0, 4), (7.0, 5)]
-\end{verbatim}
-
-\code{map(\var{f},\var{A})}, where \var{f} is a function and \var{A} 
-is a matrix, returns a list constructed by applying \var{f} to each 
-element of \var{A}.  Multiple arguments can be provided, for example, 
-as in \code{map(\var{f},\var{A},\var{B})}, if \var{f} is a function 
-with two arguments.
-\begin{verbatim}
->>> A = matrix([[5, -4, 10, -7], [-1, -5, -6, 2], [6, 1, 5, 2],  [-1, 2, -3, -7]])
->>> B = matrix([[4,-15, 9, -14], [-4, -12, 1, -22], [-10, -9, 9, 12], [-9, -7,-11, -6]])
->>> print matrix(map(max, A, B), (4,4))   # takes componentwise maximum
-[  5  -1   6  -1]
-[ -4  -5   1   2]
-[ 10   1   9  -3]
-[ -7   2  12  -6]
-\end{verbatim}
-
-\code{filter(\var{f},\var{A})}, where \var{f} is a function and 
-\var{A} is a matrix, returns a list containing the elements of \var{A} 
-for which \var{f} is true.
-\begin{verbatim}
->>> filter(lambda x: x%2, A)         # list of odd elements in A
-[5, -7, -1, -5, 1, 5, -1, -3, -7]
->>> filter(lambda x: -2 < x < 3, A)  # list of elements between -2 and 3
-[-1, 2, 1, 2, -1, 2]
-\end{verbatim}
-
-It is also possible to iterate over matrix elements, as illustrated in
-the following example.
-\begin{verbatim}
->>> A = matrix([[5, -3], [9, 11]])
->>> for x in A: print max(x,0)
-...
-5
-0
-9
-11
->>> [max(x,0) for x in A]
-[5, 0, 9, 11]
-\end{verbatim}
-
-The expression \samp{\var{x} in \var{A}} returns \True\ if an element 
-of \var{A} is equal to \var{x} and \False\ otherwise.
-
-\section{Other Matrix Functions} \label{s-otherfuncs}
-The following functions of dense matrices can be imported from 
-\module{cvxopt.base}.
-\begin{funcdesc}{sqrt}{x}
-The elementwise square root of \var{x}.  The result is returned 
-as a real matrix if \var{x} is an integer or real matrix and 
-as a complex matrix if \var{x} is a complex matrix.  Raises an 
-exception when \var{x} is an integer or real matrix with negative 
-elements.
-\end{funcdesc}
-
-\begin{funcdesc}{sin}{x}
-The sine function applied elementwise to \var{x}.  
-The result is returned as a real matrix if \var{x} is an integer
-or real matrix and as a complex matrix otherwise.  
-\end{funcdesc}
-
-\begin{funcdesc}{cos}{x}
-The cosine function applied elementwise to \var{x}.  
-The result is returned as a real matrix if \var{x} is an integer
-or real matrix and as a complex matrix otherwise.  
-\end{funcdesc}
-
-\begin{funcdesc}{exp}{x}
-The exponential function applied elementwise to \var{x}.  
-The result is returned as a real matrix if \var{x} is an integer 
-or real matrix and as a complex matrix otherwise.  
-\end{funcdesc}
-
-\begin{funcdesc}{log}{x}
-The natural logarithm applied elementwise to \var{x}.  
-The result is returned as a real matrix if \var{x} is an integer
-or real matrix and as a complex matrix otherwise.  
-Raises an exception when \var{x} is an integer or real matrix with 
-nonnegative elements, or a complex matrix with zero elements.
-\end{funcdesc}
-
-\begin{funcdesc}{mul}{x, y}
-The elementwise product of \var{x} and \var{y}.  
-The two matrices must have the same size and type.
-\end{funcdesc}
-
-\begin{funcdesc}{div}{x, y}
-The elementwise division of \var{x} by \var{y}.  
-The two matrices must have the same size and type.
-\end{funcdesc}
-
-
-\section{Randomly Generated Matrices} 
-\label{s-random}
-The \module{cvxopt.base} module provides two functions \function{normal()} 
-and \function{uniform()} for generating
-randomly distributed matrices.  The default installation relies on
-the pseudo-random number generators in the Python standard 
-library \module{random}.  Alternatively, the random number generators in 
-the \ulink{GNU Scientific Library (GSL)}{http://www.gnu.org/software/gsl}
-can be used, if this option is selected during the installation of CVXOPT.
-The random matrix functions based on GSL are faster than the default 
-functions based on the \module{random} module.
-
-\begin{funcdesc}{normal}{nrows\optional{, ncols\optional{, 
- mean\optional{, std}}}}
-Returns a type \dtc\ matrix of size \var{nrows} by 
-\var{ncols} with elements chosen from a normal distribution 
-with mean \var{mean} and standard deviation \var{std}.
-The default values for the optional arguments are 
-\var{ncols}=1, \var{mean}=0.0, \var{std}=1.0.
-\end{funcdesc}
-
-\begin{funcdesc}{uniform}{nrows\optional{, ncols\optional{, 
-  a\optional{, b}}}}
-Returns a type \dtc\ matrix of size \var{nrows} by 
-\var{ncols} matrix with elements uniformly distributed 
-between \var{a} and \var{b}.
-The default values for the optional arguments are 
-\var{ncols}=1, \var{a}=0.0, \var{b}=1.0.
-\end{funcdesc}
-
-\begin{funcdesc}{setseed}{\optional{value}}
-Sets the state of the random number generator.  \var{value} must be an 
-integer.  If \var{value} is absent or equal to zero, the value is taken 
-from the system clock.  
-If the Python random number generators are used, this is equivalent
-to \function{random.seed(value)}.
-\end{funcdesc}
-
-\begin{funcdesc}{getseed}{}
-Returns the current state of the random number generator.  
-This function is only available if the GSL random number generators are 
-installed.   
-(The state of the random number generators in the Python \module{random} 
-module can be managed via the functions \function{random.getstate()} and 
-\function{random.setstate()}.)
-\end{funcdesc}
-
-
-\section{The NumPy Array Interface} \label{s-array-interface}
-
-The CVXOPT \mtrx\ object is compatible with the \program{NumPy} Array 
-Interface, 
-which allows Python objects that represent multidimensional 
-arrays to exchange data using information stored in the 
-attribute \code{\_\_array\_struct\_\_}.  
-
-\textbf{See also:}
-\BIT
-\item \seelink{http://numpy.scipy.org/array_interface.shtml}
-{NumPy Array Interface Specification}{}
-\item \seelink{http://numpy.scipy.org}{NumPy home page}{}
-\EIT
-
-As already mentioned in section~\ref{s-creating-matrices},
-a two-dimensional array object (for example, a \program{NumPy} matrix or
-two-dimensional array) can be converted to a CVXOPT \mtrx\ object by using 
-the \function{matrix()} constructor.
-Conversely, CVXOPT matrices can be used as array-like objects
-in \program{NumPy}.  The following example illustrates the 
-compatibility of CVXOPT matrices and \program{NumPy} arrays. 
-\begin{verbatim}
->>> from cvxopt.base import matrix
->>> a = matrix(range(6), (2,3), 'd')
->>> print a
-[ 0.00e+00  2.00e+00  4.00e+00]
-[ 1.00e+00  3.00e+00  5.00e+00]
->>> from numpy import array
->>> b = array(a)
->>> b
-array([[ 0.  2.  4.]
-       [ 1.  3.  5.]])
->>> a*b
-array([[  0.   4.  16.]
-       [  1.   9.  25.]])
->>> from numpy import mat
->>> c = mat(a)
->>> c
-matrix([[ 0.  2.  4.]
-        [ 1.  3.  5.]])
->>> a.T * c 
-matrix([[  1.,   3.,   5.],
-        [  3.,  13.,  23.],
-        [  5.,  23.,  41.]])
-\end{verbatim}
-In the first product, \code{a*b} is interpreted as \program{NumPy} array 
-multiplication, \ie, componentwise multiplication.
-The second product \code{a.T*c} is interpreted as \program{NumPy} matrix 
-multiplication, \ie, standard matrix multiplication.
diff --git a/doc/base_sparse.tex b/doc/base_sparse.tex
deleted file mode 100644
index 254a4b1..0000000
--- a/doc/base_sparse.tex
+++ /dev/null
@@ -1,689 +0,0 @@
-\chapter{Sparse Matrices (\module{cvxopt.base})}\label{chap:spmatrix}
-
-In this chapter we discuss the \spmtrx\ object defined in 
-\module{cvxopt.base}.
-
-\section{Creating Sparse Matrices} \label{s-creating-spmatrix}
-A general \spmtrx\ object can be thought of as a \emph{triplet 
-description} of a sparse matrix, \ie, a list of entries of the matrix, 
-with for each entry the value, row index, and column index.  
-Entries that are not included in the list are assumed to be zero.  
-For example, the sparse matrix
-\BEQ \label{e-sparse-A}
- A = \left[ \begin{array}{rrrrr}
-  0 & 2 & 0 & 0 & 3 \\
-  2 & 0 & 0 & 0 & 0 \\
- -1 & -2 & 0 & 4 & 0 \\
-  0 & 0 & 1 & 0 & 0 \end{array} \right]
-\EEQ
-has the triplet description 
-\[
-(2,1,0), \qquad (-1,2,0), \qquad (2,0,1), \qquad (-2,2,1), \qquad
-(1,3,2), \qquad (4,2,3), \qquad (3,0,4).
-\]
-The list may include entries with a zero value, so triplet
-descriptions are not necessarily unique.
-The list
-\[
-(2,1,0), \qquad (-1,2,0), \qquad (0,3,0), \qquad (2,0,1), \qquad 
- (-2,2,1), \qquad (1,3,2), \qquad (4,2,3), \qquad (3,0,4)
-\]
-is another triplet description of the same matrix.
-
-An \spmtrx\ object corresponds to a particular triplet 
-description of a sparse matrix.  We will refer to the entries in
-the triplet description as the \emph{nonzero entries} of the object, 
-even though they may have a numerical value zero.
-
-Two functions are provided to create sparse matrices. 
-The first, \function{spmatrix()}, constructs a sparse matrix from 
-a triplet description. 
-\begin{funcdesc}{spmatrix}{x, I, J\optional{, size\optional{, tc}}}
-
-\var{I} and \var{J} are sequences of integers (lists, tuples, 
-\module{array} arrays, xrange objects, \ldots) or integer matrices 
-(\mtrx\ objects with typecode \itc), containing the row and column 
-indices of the nonzero entries.  
-The lengths of \var{I} and \var{J} must be  equal.  If they 
-are matrices, they are treated as lists of indices stored in 
-column-major order, \ie, as lists \code{list(\var{I})}, respectively, 
-\code{list(\var{J})}. 
-
-\var{size} is a tuple of nonnegative integers with the row and column 
-dimensions of the matrix.
-The \var{size} argument is only needed when creating a matrix with 
-a zero last row or last column.  If \var{size} is not specified, it 
-is determined from \var{I} and \var{J}:
-the default value for \code{\var{size}[0]} is max(\var{I})+1 
-if \var{I} is nonempty and zero otherwise.  
-The default value for \code{\var{size}[1]} is 
-max(\var{J})+1 if \var{J} is nonempty and zero otherwise.
-
-\var{tc} is the typecode, \dtc\ or \ztc, for double and complex 
-matrices, respectively.   Integer sparse matrices are not implemented.
-
-\var{x} can be a number, a sequence of numbers, or a dense matrix.  
-This argument specifies the numerical values of the nonzero entries.
-\BIT
-\item If \var{x} is a number (Python \intgr, \flt\ or \cmplx), 
-a matrix is created with the sparsity pattern defined by \var{I} and 
-\var{J}, and nonzero entries initialized to the value of \var{x}.  
-The default value of \var{tc} is \dtc\ if \var{x} is \intgr\ or \flt,
-and \ztc\ if \var{x} is \cmplx.  
-
-The following code creates a 4 by 4 sparse identity matrix.
-\begin{verbatim}
->>> from cvxopt.base import spmatrix
->>> A = spmatrix(1.0, range(4), range(4))
->>> print A  
-[ 1.00e+00     0         0         0    ]
-[    0      1.00e+00     0         0    ]
-[    0         0      1.00e+00     0    ]
-[    0         0         0      1.00e+00]
-\end{verbatim}
-
-\item If \var{x} is a sequence of numbers, a sparse matrix is created 
-with the entries of \var{x} copied to the entries indexed by \var{I} 
-and \var{J}.  The list \var{x} must have the same length as \var{I} and 
-\var{J}.
-The default value of \var{tc} is determined from the elements of 
-\var{x}: 
-\dtc\ if \var{x} contains integers and floating-point numbers or
-if \var{x} is an empty list,
-and \ztc\ if \var{x} contains at least one complex number.
-
-As an example, the matrix~(\ref{e-sparse-A}) can be created as follows.
-\begin{verbatim}
->>> A = spmatrix([2,-1,2,-2,1,4,3], [1,2,0,2,3,2,0], [0,0,1,1,2,3,4])
->>> print A 
-[    0      2.00e+00     0         0      3.00e+00]
-[ 2.00e+00     0         0         0         0    ]
-[-1.00e+00 -2.00e+00     0      4.00e+00     0    ]
-[    0         0      1.00e+00     0         0    ]
-\end{verbatim}
-
-\item If \var{x} is a dense matrix, a sparse matrix is created with 
-all the entries of \var{x} copied, in column-major order, to the 
-entries indexed by \var{I} and \var{J}.
-The matrix \var{x} must have the same length as \var{I} and \var{J}.
-The default value of \var{tc} is \dtc\ if \var{x} is an \itc\ or \dtc\
-matrix, and \ztc\ otherwise.
-\EIT
-
-If \var{I} and \var{J} contain repeated entries, the corresponding 
-values of the coefficients are added.
-\end{funcdesc}
-
-The function \function{sparse()} constructs a sparse matrix from
-a block-matrix description.
-   
-\begin{funcdesc}{sparse}{x\optional{, tc}}
-\var{tc} is the typecode, \dtc\ or \ztc, for double and complex 
-matrices, respectively.
-  
-\var{x} can be a \mtrx, \spmtrx, or a list of lists of matrices 
-(\mtrx\ or \spmtrx\ objects) and numbers (Python \intgr, \flt\ or 
-\cmplx). 
-\BIT
-\item If \var{x} is a \mtrx\ or \spmtrx\ object, then a sparse matrix 
-of the same size and the same numerical value is created. 
-Numerical zeros in \var{x} are treated as structural zeros and removed 
-from the triplet description of the new sparse matrix.
-
-\item If \var{x} is a list of lists of matrices (\mtrx\ or \spmtrx) 
-and numbers (Python \intgr, \flt\ or \cmplx) then each element of 
-\var{x} is interpreted as a (block-)column matrix stored in 
-colum-major order, and a block-matrix is constructed by juxtaposing
-the \code{len(\var{x})} block-columns
-(as in \function{matrix()}, see section~\ref{s-creating-matrices}). 
-Numerical zeros are removed from the triplet description of the new 
-matrix.  
-
-The following example shows how to construct a sparse block-matrix.
-\begin{verbatim}
->>> from cvxopt.base import matrix, spmatrix, sparse
->>> A = matrix([[1, 2, 0], [2, 1, 2], [0, 2, 1]])
->>> B = spmatrix([], [], [], (3,3))
->>> C = spmatrix([3, 4, 5], [0, 1, 2], [0, 1, 2])
->>> D = sparse([[A, B], [B, C]])
->>> print D
-[ 1.00e+00  2.00e+00     0         0         0         0    ]
-[ 2.00e+00  1.00e+00  2.00e+00     0         0         0    ]
-[    0      2.00e+00  1.00e+00     0         0         0    ]
-[    0         0         0      3.00e+00     0         0    ]
-[    0         0         0         0      4.00e+00     0    ]
-[    0         0         0         0         0      5.00e+00]
-\end{verbatim}
-
-A matrix with a single block-column can be represented by a single
-list.
-\begin{verbatim}
->>> D = sparse([A, C])
->>> print D
-[ 1.00e+00  2.00e+00     0    ]
-[ 2.00e+00  1.00e+00  2.00e+00]
-[    0      2.00e+00  1.00e+00]
-[ 3.00e+00     0         0    ]
-[    0      4.00e+00     0    ]
-[    0         0      5.00e+00]
-\end{verbatim}
-\EIT
-\end{funcdesc}
-
-
-The function \function{spdiag()} constructs a block-diagonal
-sparse matrix from a list of matrices.
-   
-\begin{funcdesc}{spdiag}{x}
-\var{x} is a matrix with a single row or column, or a list of square
-dense or sparse matrices or scalars.  
-If \var{x} is matrix, a sparse diagonal matrix is returned with 
-the entries of \var{x} on its diagonal.
-If \var{x} is list, a sparse block-diagonal matrix is returned with
-the element in the list as its diagonal blocks.
-  
-The following example shows how to construct a sparse block-diagonal matrix.
-\begin{verbatim}
->>> from cvxopt.base import matrix, spmatrix, spdiag
->>> A = 3.0
->>> B = matrix([[1,-2],[-2,1]])
->>> C = spmatrix([1,1,1,1,1],[0,1,2,0,0,],[0,0,0,1,2])
->>> D = spdiag([A, B, C])
->>> print D
-[ 3.00e+00     0         0         0         0         0    ]
-[    0      1.00e+00 -2.00e+00     0         0         0    ]
-[    0     -2.00e+00  1.00e+00     0         0         0    ]
-[    0         0         0      1.00e+00  1.00e+00  1.00e+00]
-[    0         0         0      1.00e+00     0         0    ]
-[    0         0         0      1.00e+00     0         0    ]
-\end{verbatim}
-\end{funcdesc}
-
-\section{Attributes and Methods}
-The following attributes and methods are defined for \spmtrx\ objects.
-
-\begin{memberdesc}{V}
-A single-column dense matrix containing the numerical values of the 
-nonzero entries in column-major order.  Making an assignment to 
-the attribute is an efficient way of changing the values of the sparse 
-matrix, without changing the sparsity pattern.
-
-When the attribute \member{V} is read, a \emph{copy} of \member{V} is 
-returned, as a new dense matrix. 
-(This implies, for example, that an indexed assignment 
-\samp{A.V[I] = B} does not work, or at least 
-cannot be used to modify \var{A}.  Instead the attribute \code{V}\ 
-will be read and returned as a new matrix; then the elements of this 
-new matrix are modified.)
-\end{memberdesc} 
-
-\begin{memberdesc}{I}
-A single-column integer matrix with the row indices of the entries in
-\code{V}.  A read-only attribute.
-\end{memberdesc} 
-
-\begin{memberdesc}{J}
-A single-column integer matrix with the column indices of the entries
-in \code{V}.  A read-only attribute.
-\end{memberdesc} 
-
-\begin{memberdesc}{size}
-A tuple with the dimensions of the matrix. The size of the matrix
-can be changed by altering this attribute, as long as the number
-of elements in the matrix remains unchanged.  
-\end{memberdesc} 
-
-\begin{memberdesc}{CCS}
-A triplet (colptr, rowind, values) with the compressed-column-storage 
-representation of the matrix.  A read-only attribute.  This attribute
-can be used to export sparse matrices to other packages such as MOSEK.
-\end{memberdesc} 
-
-
-\begin{methoddesc}{trans}
-Returns the transpose of a sparse matrix as a new sparse matrix.
-One can also use \code{A.T} instead of \code{A.trans()}.
-\end{methoddesc}
-
-\begin{methoddesc}{ctrans}
-Returns the complex conjugate transpose of a sparse matrix as a 
-new sparse matrix.
-One can also use \code{A.H} instead of \code{A.ctrans()}. 
-\end{methoddesc}
-
-In the following example we take the elementwise square root of 
-the matrix
-\BEQ \label{e-spA-example}
- A = \left[ \begin{array}{rrrrr}
-  0 & 2 & 0 & 0 & 3 \\
-  2 & 0 & 0 & 0 & 0 \\
-  1 & 2 & 0 & 4 & 0 \\
-  0 & 0 & 1 & 0 & 0 \end{array} \right]
-\EEQ
-\begin{verbatim}
->>> from cvxopt.base import sqrt
->>> A = spmatrix([2,1,2,2,1,3,4], [1,2,0,2,3,0,2], [0,0,1,1,2,3,3]) 
->>> B = spmatrix(sqrt(A.V), A.I, A.J)
->>> print B
-[    0      1.41e+00     0      1.73e+00]
-[ 1.41e+00     0         0         0    ]
-[ 1.00e+00  1.41e+00     0      2.00e+00]
-[    0         0      1.00e+00     0    ]
-\end{verbatim}
-
-The next example below illustrates assignments to \member{V}.
-\begin{verbatim}
->>> from cvxopt.base import spmatrix, matrix
->>> A = spmatrix(range(5), [0,1,1,2,2], [0,0,1,1,2])
->>> print A
-[ 0.00e+00     0         0    ]
-[ 1.00e+00  2.00e+00     0    ]
-[    0      3.00e+00  4.00e+00]
->>> B = spmatrix(A.V, A.J, A.I, (4,4))  # transpose and add a zero row and column
->>> print B
-[ 0.00e+00  1.00e+00     0         0    ]
-[    0      2.00e+00  3.00e+00     0    ]
-[    0         0      4.00e+00     0    ]
-[    0         0         0         0    ]
->>> print matrix(B)
-[ 0.00e+00  1.00e+00  0.00e+00  0.00e+00]
-[ 0.00e+00  2.00e+00  3.00e+00  0.00e+00]
-[ 0.00e+00  0.00e+00  4.00e+00  0.00e+00]
-[ 0.00e+00  0.00e+00  0.00e+00  0.00e+00]
->>> B.V = matrix([1., 7., 8., 6., 4.])   # assign new values to nonzero entries
->>> print B
-[ 1.00e+00  7.00e+00     0         0    ]
-[    0      8.00e+00  6.00e+00     0    ]
-[    0         0      4.00e+00     0    ]
-[    0         0         0         0    ]
->>> B.V += 1.0   # add 1 to the nonzero entries
->>> print B
-[ 2.00e+00  8.00e+00     0         0    ]
-[    0      9.00e+00  7.00e+00     0    ]
-[    0         0      5.00e+00     0    ]
-[    0         0         0         0    ]
-\end{verbatim}
-
-The \member{V}, \member{I} and \member{J}  attributes can be used for 
-reading sparse matrices from or writing them to binary files.  
-Suppose we want to write the matrix \var{A} defined above to a binary 
-file.
-\begin{verbatim}
->>> f = open('test.bin','w')
->>> A.V.tofile(f)  
->>> A.I.tofile(f) 
->>> A.J.tofile(f)
->>> f.close()
-\end{verbatim}
-A sparse matrix can be created from this file as follows.
-\begin{verbatim}
->>> f = open('test.bin','r')
->>> V = matrix(0.0, (5,1));  V.fromfile(f)  
->>> I = matrix(0, (5,1));  I.fromfile(f)  
->>> J = matrix(0, (5,1));  J.fromfile(f)  
->>> B = spmatrix(V, I, J)
->>> print B
-[ 0.00e+00     0         0    ]
-[ 1.00e+00  2.00e+00     0    ]
-[    0      3.00e+00  4.00e+00]
-\end{verbatim}
-
-Note that the  \module{pickle} module provides a convenient alternative 
-to this method.
-
-\section{Arithmetic Operations} \label{s-spmatrix-arith}
-Most of the operations defined for dense \dtc\ and \ztc\ matrices
-(section~\ref{s-arithmetic}) are also defined for sparse matrices.
-In the following table, \var{A} is a sparse matrix,
-\var{B} is sparse or dense, and \var{c} is a scalar, defined as a
-Python number or a 1 by 1 dense matrix.
-
-\begin{center}
-\begin{tabular}{l|l} \hline
- Unary plus/minus & \code{+\var{A}}, \code{-\var{A}} \\ \hline
- Addition & \code{\var{A}+\var{B}}, \code{\var{B}+\var{A}}, 
-   \code{\var{A}+\var{c}}, \code{\var{c}+\var{A}}\\ \hline
- Subtraction & \code{\var{A}-\var{B}}, \code{\var{B}-\var{A}}, 
-    \code{\var{A}-\var{c}}, \code{\var{c}-\var{A}}\\ \hline
- Matrix multiplication & \code{\var{A}*\var{B}}, 
-  \code{\var{B}*\var{A}} \\ \hline
- Scalar multiplication and division & \code{\var{c}*\var{A}}, 
-   \code{\var{A}*\var{c}}, \code{\var{A}/\var{c}} \\\hline
- \end{tabular}
-\end{center}
-
-If \var{B} is a dense matrix, then the result of 
-\code{\var{A}+\var{B}}, \code{\var{B}+\var{A}}, \code{\var{A}-\var{B}}, 
-\code{\var{B}-\var{A}} is a dense  matrix.
-The typecode of the result is \dtc\ if \var{A} has typecode \dtc\ and 
-\var{B} has typecode \itc\ or \dtc,
-and it is \ztc\ if \var{A} and/or \var{B} have typecode \ztc.
-
-If \var{B} is a sparse matrix, then the result of
-\code{\var{A}+\var{B}}, \code{\var{B}+\var{A}}, 
-\code{\var{A}-\var{B}}, \code{\var{B}-\var{A}} is a sparse 
-matrix.  The typecode of the result is \dtc\ if \var{A} and \var{B}
-have typecode \dtc,  and \ztc\ otherwise.
-
-If \var{c} in \code{\var{A}+\var{c}}, \code{\var{A}-\var{c}}, 
-\code{\var{c}+\var{A}}, \code{\var{c}-\var{A}} is a number,
-then it is interpreted as a dense matrix with the same size as 
-\var{A}, typecode given by the type of \var{c}, and all entries equal 
-to \var{c}. 
-If \var{c} is a 1 by 1 dense matrix and the size of \var{A} is not 1 
-by 1, then \var{c}
-is interpreted as a dense matrix of the same size as \var{A},
-typecode given by the typecode of \var{c}, and all entries equal to 
-\code{\var{c}[0]}.
-
-The result of a matrix-matrix product \code{\var{A}*\var{B}} or 
-\code{\var{B}*\var{A}} is a dense matrix if \var{B} is dense, and sparse
-if \var{B} is sparse.   The matrix-matrix product is not allowed if 
-\var{B} is a dense \itc\ matrix.  
-
-If \var{c} is a number (Python \intgr\, \flt\ or \cmplx), then the 
-operations \code{\var{c}*\var{A}} and \code{\var{A}*\var{c}} define 
-scalar multiplication and return a sparse matrix.
-
-If \var{c} is a 1 by 1 dense matrix, then, if possible, the products 
-\code{\var{c}*\var{A}} and \code{\var{A}*\var{c}} are interpreted as 
-matrix-matrix products and a dense matrix is returned.  
-If the product cannot be interpreted as a matrix-matrix product
-(either because the dimensions of \var{A} are incompatible or because
-\var{c} has typecode \itc), then the product is interpreted as the 
-scalar multiplication with \code{\var{c}[0]} and a sparse matrix is 
-returned.
-
-The division \code{\var{A}/\var{c}} is interpreted as scalar 
-multiplication with \code{1.0/\var{c}} if \var{c} is a number, 
-or with  \code{1.0/\var{c}[0]} if \var{c} is a 1 by 1 dense matrix.
-
-The following in-place operations are defined for a sparse matrix 
-\var{A} if they do not change the dimensions or type of \var{A}.
-\begin{center}
-  \begin{tabular}{l|l} \hline
-  In-place addition & \code{\var{A}+=\var{B}}, 
-    \code{\var{A}+=\var{c}} \\ \hline   
-  In-place subtraction & \code{\var{A}-=\var{B}}, 
-    \code{\var{A}-=\var{c}} \\ \hline   
-  In-place scalar multiplication and division & 
-    \code{\var{A}*=\var{c}}, \code{\var{A}/=\var{c}}  \\ \hline
-  \end{tabular}
-\end{center}
-
-For example, \samp{\var{A} += 1.0} is not allowed because the 
-operation \samp{\var{A} = \var{A} + 1.0} results in a dense matrix, 
-so it cannot be assigned to \var{A} without changing its type.
-
-In-place matrix-matrix products are not allowed.  (Except when
-\var{c} is a 1 by 1 dense matrix, in which case \code{\var{A}*=\var{c}} 
-is interpreted as a scalar product \code{\var{A}*=\var{c}[0]}.)
-
-As for dense operations, the in-place sparse operations do not return
-a new matrix but modify the existing object \var{A}.
-%The restrictions on in-place operations follow the principle that once 
-%a sparse matrix is created, its size and type cannot be modified.  
-%The only attributes that can be modified are the sparsity pattern and 
-%the numerical values of the nonzero elements.
-%These attributes can be modified by in-place operations or by indexed 
-%assignments.
-
-
-
-\section{Indexing and Slicing} 
-Sparse matrices can be indexed the same way as dense matrices 
-(see section~\ref{s-indexing} ).  
-
-\begin{verbatim}
->>> from cvxopt.base import spmatrix
->>> A = spmatrix([0,2,-1,2,-2,1], [0,1,2,0,2,1], [0,0,0,1,1,2]) 
->>> print A[:,[0,1]]
-[ 0.00e+00  2.00e+00]
-[ 2.00e+00     0    ]
-[-1.00e+00 -2.00e+00]
->>> B = spmatrix([0,2*1j,0,-2], [1,2,1,2], [0,0,1,1,])
->>> print B[-2:,-2:]
-[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00]
-[ 0.00e+00+j2.00e+00 -2.00e+00-j0.00e+00]
-\end{verbatim}
-
-An indexed sparse matrix \code{\var{A}[\var{I}]} or 
-\code{\var{A}[\var{I},\var{J}]} can also be the target of an 
-assignment.  The righthand side of the assignment can be a scalar
-(a Python \intgr, \flt, or \cmplx,  or a 1 by 1 dense matrix),
-a sequence of numbers, or a sparse or dense matrix of 
-compatible dimensions. 
-If the righthand side is a scalar, it is treated as a dense matrix of 
-the same size as the lefthand side and with all its entries equal to 
-the scalar. 
-If the righthand side is a sequence of numbers, they are treated as
-the elements of a dense matrix in column-major order. 
-
-We continue the example above.
-\begin{verbatim}
->>> C = spmatrix([10,-20,30], [0,2,1], [0,0,1])
->>> A[:,0] = C[:,0]
->>> print A
-[ 1.00e+01  2.00e+00     0    ]
-[    0         0      1.00e+00]
-[-2.00e+01 -2.00e+00     0    ]
->>> D = matrix(range(6), (3,2))
->>> A[:,0] = D[:,0]
->>> print A
-[ 0.00e+00  2.00e+00     0    ]
-[ 1.00e+00     0      1.00e+00]
-[ 2.00e+00 -2.00e+00     0    ]
->>> A[:,0] = 1
->>> print A
-[ 1.00e+00  2.00e+00     0    ]
-[ 1.00e+00     0      1.00e+00]
-[ 1.00e+00 -2.00e+00     0    ]
->>> A[:,0] = 0
->>> print A
-[ 0.00e+00  2.00e+00     0    ]
-[ 0.00e+00     0      1.00e+00]
-[ 0.00e+00 -2.00e+00     0    ]
-\end{verbatim}
-
-
-\section{Built-In Functions}
-
-The functions described in the table of section~\ref{s-builtinfuncs} 
-also work with sparse matrix arguments.  The difference is that for a 
-sparse matrix only the nonzero entries are considered. 
-
-\begin{funcdesc}{len}{x}
-If \var{x} is a \spmtrx, returns the number of nonzero entries in
-\var{x}.
-\end{funcdesc}
-
-\begin{funcdesc}{bool}{\optional{x}}
-If \var{x} is a \spmtrx, returns \False\ if \var{x} has at least one 
-nonzero entry; \False\ otherwise.
-\end{funcdesc}
-
-\begin{funcdesc}{max}{x}
-If \var{x} is a \spmtrx, returns the maximum nonzero entry of \var{x}.
-\end{funcdesc}
-
-\begin{funcdesc}{min}{x}
-If \var{x} is a \spmtrx, returns the minimum nonzero entry of \var{x}.
-\end{funcdesc}
-
-\begin{funcdesc}{abs}{x}
-If \var{x} is a \spmtrx, returns a sparse matrix with the absolute
-value of the elements of \var{x} and the same sparsity pattern.
-\end{funcdesc}
-
-\begin{funcdesc}{sum}{x\optional{, start=0.0}}
-If \var{x} is a \spmtrx, returns the sum of \var{start} and the 
-elements of \var{x}.
-\end{funcdesc}
-
-The functions \function{list()}, \function{tuple()}, \function{zip()}, 
-\function{map()}, \function{filter()} also take sparse matrix arguments.
-They work as for dense matrices, again with the difference that only 
-the nonzero entries are considered.
-
-In the following example we square the entries of the 
-matrix~(\ref{e-spA-example}). 
-
-\begin{verbatim}
->>> A = spmatrix([2,1,2,2,1,3,4], [1,2,0,2,3,0,2], [0,0,1,1,2,3,3]) 
->>> B = spmatrix(map(lambda x: x**2, A), A.I, A.J)
->>> print B
-[    0      4.00e+00     0      9.00e+00]
-[ 4.00e+00     0         0         0    ]
-[ 1.00e+00  4.00e+00     0      1.60e+01]
-[    0         0      1.00e+00     0    ]
-\end{verbatim}
-
-The expression \samp{\var{x} in \var{A}} returns \True\  if a nonzero
-entry of \var{A} is equal to \var{x} and \False\ otherwise.
-
-
-\section{Sparse BLAS Functions}
-The \module{cvxopt.base} module includes a few arithmetic functions 
-that extend functions from \module{cvxopt.blas} to sparse matrices.
-These functions are faster than the corresponding operations 
-implemented using the overloaded arithmetic described 
-in~section~\ref{s-spmatrix-arith}.
-They also work in-place, \ie, they modify their arguments without 
-creating new objects.
-
-
-\begin{funcdesc}{gemv}{A, x, y\optional{, trans='N'\optional{, 
-  alpha=1.0\optional{, beta=0.0}}}}
-Matrix-vector product with a general dense or sparse matrix:
-\[ 
-y := \alpha Ax + \beta y \quad (\mathrm{trans} = \mathrm{'N'}),
-  \qquad 
-y := \alpha A^T x + \beta y \quad (\mathrm{trans} = \mathrm{'T'}), 
-  \qquad
-y := \alpha A^H x + \beta y \quad (\mathrm{trans} = \mathrm{'C'}). 
-\]
-If \var{A} is a dense matrix, this is identical to 
-\function{blas.gemv()}.  If \var{A} is sparse, the result is the same 
-as when \function{blas.gemv()} is called with \code{matrix(A)} as 
-argument, however, without explicitly converting \var{A} to dense.
-\end{funcdesc}
-
-\begin{funcdesc}{symv}{A, x, y\optional{, uplo='L'\optional{, 
-alpha=1.0\optional{, beta=0.0}}}} 
-Matrix-vector product with a dense or sparse real symmetric matrix:
-\[
-   y := \alpha A x + \beta y.
-\]
-If \var{A} is a dense matrix, this is identical to 
-\function{blas.symv()}.  If \var{A} is sparse, the result is the same 
-as when \function{blas.symv()} is called with \code{matrix(A)} as 
-argument, however, without explicitly converting \var{A} to dense.
-\end{funcdesc}
-
-\begin{funcdesc}{gemm}{A, B, C\optional{, transA='N'\optional{, 
-transB='N'\optional{, alpha=1.0\optional{, beta=0.0\optional{, 
-partial=False}}}}}}
-Matrix-matrix product of two general sparse or dense matrices:
-\[
-  C := \alpha \op(A) \op(B) + \beta C 
-\]
-where
-\[
-\op(A) =  \left\{ \begin{array}{ll}
- A & \mathrm{transA} = \mathrm{'N'} \\
- A^T & \mathrm{transA} = \mathrm{'T'} \\
- A^H & \mathrm{transA} = \mathrm{'C'} \end{array} \right.
-\qquad
-\op(B) =  \left\{ \begin{array}{ll}
- B & \mathrm{transB} = \mathrm{'N'} \\
- B^T & \mathrm{transB} = \mathrm{'T'} \\
- B^H & \mathrm{transB} = \mathrm{'C'}. \end{array} \right.
-\]
-If \var{A}, \var{B} and \var{C} are dense matrices, this is 
-identical to \function{blas.gemm()}, described in section~\ref{s-blas3},
-and the argument \var{partial} is ignored.
-
-If \var{A} and/or \var{B} are sparse and \var{C} is dense, the result
-is the same as when \function{blas.gemm()} is called with
-\code{matrix(A)} and \code{matrix(B)} as arguments, without explicitly 
-converting  \var{A} and \var{B} to dense.
-The argument \var{partial} is ignored.
-
-If \var{C} is a sparse matrix, the matrix-matrix product in the
-definition of \function{blas.gemm()} is computed, but as a sparse 
-matrix.  
-If \var{partial} is \False, the result is stored in \var{C}, 
-and the sparsity pattern of \var{C} is  modified if necessary.
-If \var{partial} is \True, the operation only updates the nonzero
-elements in \var{C}, even if the sparsity pattern of \var{C} differs
-from that of the matrix product.  
-\end{funcdesc}
-
-\begin{funcdesc}{syrk}{A, C\optional{, uplo='L'\optional{, 
-trans='N'\optional{, alpha=1.0\optional{, beta=0.0\optional{, 
-partial=False}}}}}}
-Rank-\tm{k} update of a sparse or dense real or complex symmetric
-matrix:
-\[
- C := \alpha AA^T + \beta C \quad (\mathrm{trans} = \mathrm{'N'}), 
- \qquad 
- C := \alpha A^TA + \beta C \quad (\mathrm{trans} = \mathrm{'T'}), 
-\]
-If \var{A} and \var{C} are dense, this is identical to 
-\function{blas.syrk()}, described in section~\ref{s-blas3},
-and the argument \var{partial} is ignored.
-
-If \var{A} is sparse and \var{C} is dense, the result is the same as 
-when \function{blas.syrk()} is called with \code{matrix(A)} as 
-argument, without explicitly converting  \var{A} to dense.  
-The argument \var{partial} is ignored.
-
-If \var{C} is sparse, the product in the definition of 
-\function{blas.syrk()} is computed, but as a sparse matrix.  
-If \var{partial} is \False, the result is stored in \var{C}, 
-and the sparsity pattern of \var{C} is  modified if necessary.
-If \var{partial} is \True, the operation only updates the nonzero
-elements in \var{C}, even if the sparsity pattern of \var{C} differs
-from that of the matrix product.  
-\end{funcdesc}
-
-In the following example, we first compute 
-\[
-C =  A^TB, \qquad
-A = \left[ \begin{array}{ccc}
-0 & 1 & 0 \\ 1 & 0 & 1 \\ 0 & 1 & 0 \\ 1 & 0 & 0 \end{array}\right],
-\qquad
-B = \left[ \begin{array}{ccc}
-   0 & -1 & 0 \\ 2 & 0 & 2 \\ 0 & 3 & 0 \\ 2 & 0 & 0 
-   \end{array}\right].
-\]
-\begin{verbatim}
->>> from cvxopt.base import spmatrix, gemm
->>> A = spmatrix(1, [1,3,0,2,1], [0,0,1,1,2])
->>> B = spmatrix([2,2,-1,3,2], [1,3,0,2,1], [0,0,1,1,2])
->>> C = spmatrix([], [], [], size=(3,3))
->>> gemm(A, B, C, transA='T')
->>> print C
-[ 4.00e+00     0      2.00e+00]
-[    0      2.00e+00     0    ]
-[ 2.00e+00     0      2.00e+00]
-\end{verbatim}
-Now suppose we want to replace \var{C}  with
-\[
-C = A^TD, \qquad 
-D = \left[ \begin{array}{ccc}
-   0 & 1 & 0 \\ 3 & 0 & -2 \\ 0 & 1 & 0 \\ 4 & 0 & 0 
-   \end{array}\right].
-\]
-The new matrix has the same sparsity pattern as \var{C}, so we can 
-use \function{gemm()} with the \code{partial=True} option.
-This saves time in large sparse matrix multiplications when the 
-sparsity pattern of the result is known beforehand.
-\begin{verbatim}
->>> D = spmatrix([3,4,1,1,-2], [1,3,0,2,1], [0,0,1,1,2])
->>> gemm(A, D, C, transA='T', partial=True)
->>> print C
-[ 7.00e+00     0     -2.00e+00]
-[    0      2.00e+00     0    ]
-[ 3.00e+00     0     -2.00e+00]
-\end{verbatim}
diff --git a/doc/blas.tex b/doc/blas.tex
index df0bb83..dd91095 100644
--- a/doc/blas.tex
+++ b/doc/blas.tex
@@ -88,7 +88,7 @@ A complex Hermitian matrix of order \tm{n} is represented
 by a \mtrx\ of type \ztc\ and size (\tm{n}, \tm{n}), and
 a character argument \var{uplo} with the same meaning as for symmetric 
 matrices.
-A complex \mtrx\ {\var X} of size (\tm{n}, \tm{n}) can represent the 8
+A complex \mtrx\ {\var X} of size (\tm{n}, \tm{n}) can represent the 
 Hermitian  matrices
 \BEAS
 &
@@ -209,7 +209,8 @@ X[0,k]   & X[1,k]   & X[2,k]   & \cdots &  &  & \\
 \item[Hermitian  band matrix]
 A complex Hermitian band matrix of order \tm{n} with \tm{k} 
 subdiagonals is represented by a complex matrix of size 
-(\tm{k+1}, \tm{n}) and an argument \var{uplo}.  
+(\tm{k+1}, \tm{n}) and an argument \var{uplo}, with the same meaning
+as for symmetric band matrices.  
 A \mtrx\ \var{X} of size (\tm{k+1}, \tm{n}) can represent the band
 matrices 
 \BEAS
@@ -238,7 +239,7 @@ X[k,0] & X[k-1,1] & X[k-2,2] & \cdots &  &  & \\
 A triangular band matrix of order \tm{n} with \tm{k} subdiagonals or
 superdiagonals is represented by a real complex matrix of size 
 (\tm{k+1}, \tm{n}) and two character arguments \var{uplo} and 
-\var{diag}.  
+\var{diag}, with similar conventions as for symmetric band matrices. 
 A \mtrx\ \var{X} of size (\tm{k+1}, \tm{n}) can represent the band
 matrices 
 \BEAS
@@ -279,7 +280,7 @@ When discussing BLAS functions in the following sections we will
 omit several less important optional arguments that can 
 be used to select submatrices for in-place operations. 
 The complete specification is  documented in the docstrings of the 
-source code and the \program{pydoc} help program.
+source code, and can be viewed with the \program{pydoc} help program.
 
 \section{Level 1 BLAS} \label{s-blas1}
 The level 1 functions implement vector operations.  
@@ -579,7 +580,7 @@ A = \left[\begin{array}{rrrr}
 \]
 with the vector {\it x} = (1,-1,2,-2).
 \begin{verbatim}
->>> from cvxopt.base import matrix
+>>> from cvxopt import matrix
 >>> from cvxopt.blas import gbmv
 >>> A = matrix([[0., 1., 2.],  [6., -4., -3.],  [3., -1., 0.],  [1., 0., 0.]])
 >>> x = matrix([1., -1., 2., -2.])
@@ -593,7 +594,7 @@ with the vector {\it x} = (1,-1,2,-2).
 
 The following example illustrates the use of \function{tbsv()}.
 \begin{verbatim}
->>> from cvxopt.base import matrix
+>>> from cvxopt import matrix
 >>> from cvxopt.blas import tbsv
 >>> A = matrix([-6., 5., -1., 2.], (1,4))
 >>> x = matrix(1.0, (4,1))
diff --git a/doc/c-api.tex b/doc/c-api.tex
index 73d0389..8bfcdb8 100644
--- a/doc/c-api.tex
+++ b/doc/c-api.tex
@@ -2,7 +2,7 @@
 The API can be used to extend CVXOPT with interfaces to 
 external C routines and libraries. 
 A C program that creates or manipulates the dense or sparse matrix
-objects defined in \module{cvxopt.base} must include the 
+objects defined in CVXOPT must include the 
 \file{cvxopt.h} header file in the \file{src} directory of the 
 distribution.
 
diff --git a/doc/coneprog.tex b/doc/coneprog.tex
index 25b2477..6e4acd9 100644
--- a/doc/coneprog.tex
+++ b/doc/coneprog.tex
@@ -11,12 +11,13 @@ In this chapter we consider convex optimization problems of the form
 The linear inequality is a generalized inequality with respect to a 
 proper convex cone.  It may include componentwise vector inequalities, 
 second-order cone inequalities, and linear matrix inequalities.  
+
 The main solvers are \function{conelp()} and \function{coneqp()},
 described in sections~\ref{s-conelp} and~\ref{s-coneqp}.
 The function \function{conelp()} is restricted to problems with
-linear cost functions, but can detect primal and dual infeasibility.
+linear cost functions, and can detect primal and dual infeasibility.
 The function \function{coneqp()} solves the general quadratic problem, 
-but requires the problem to be primal and dual feasible.
+but requires the problem to be strictly primal and dual feasible.
 For convenience (and backward compatibility), simpler interfaces to these
 function are also provided that handle pure linear programs, 
 quadratic programs, second-order cone programs, and semidefinite 
@@ -32,7 +33,7 @@ and the algorithm parameters that control the cone programming solvers.
 \begin{funcdesc}{conelp}{c, G, h\optional{, dims\optional{, A, b\optional{,
 primalstart\optional{, dualstart\optional{, kktsolver}}}}}}
 Solves a pair of primal and dual cone programs
-\[ %\label{e-conelp}
+\[ 
  \begin{array}[t]{ll}
  \mbox{minimize} & c^T x \\
  \mbox{subject to} & G x + s = h \\ & Ax = b \\ & s \succeq 0
@@ -44,7 +45,7 @@ Solves a pair of primal and dual cone programs
    & z \succeq 0.
  \end{array}
 \]
-The primal variables are \tm{x} and the slack variable \tm{s}.  
+The primal variables are \tm{x} and \tm{s}.  
 The dual variables are \tm{y} and \tm{z}.  The inequalities are 
 interpreted as $s \in C$, $z\in C$, where \tm{C} is a cone defined as a 
 Cartesian product of a nonnegative orthant, a number of second-order 
@@ -119,12 +120,14 @@ to the cone \tm{C}.
 The role of the optional argument \var{kktsolver} is explained in 
 section~\ref{s-conelp-struct}.  
 
-\function{conelp()} returns a dictionary with keys \code{'status'}, 
+\function{conelp()} returns a dictionary that contains the result and 
+information about the accuracy of the solution.
+The most important fields have keys \code{'status'}, 
 \code{'x'}, \code{'s'}, \code{'y'}, \code{'z'}.  
 The \code{'status'} field  is a string with possible values
 \code{'optimal'}, \code{'primal infeasible'}, \code{'dual infeasible'}
-and \code{'unknown'}.  The meaning of the other fields depends on the 
-value of \code{'status'}.
+and \code{'unknown'}.  The meaning of the \code{'x'}, \code{'s'}, 
+\code{'y'}, \code{'z'} fields depends on the value of \code{'status'}.
 \begin{description}
 \item[\code{'optimal'}] In this case the \code{'x'}, \code{'s'}, 
 \code{'y'} and \code{'z'} entries contain the primal and dual solutions,
@@ -133,6 +136,25 @@ which approximately satisfy
  Gx + s = h, \qquad Ax=b, \qquad G^T z  + A^T y + c = 0, \qquad 
  s \succeq 0, \qquad z \succeq 0,  \qquad s^T z =0.
 \]
+The other entries in the output dictionary summarize the accuracy
+with which these optimality conditions are satisfied.
+The fields \code{'primal objective'}, \code{'dual objective'}, and
+\code{'gap'} give the primal objective \tm{c^Tx}, dual objective
+\tm{-h^Tz - b^Ty}, and the gap \tm{s^Tz}.  The field \code{'relative gap'}
+is the relative gap, defined as
+\[
+ \frac{s^Tz}{\max\{-c^Tx, -h^Tz-b^Ty\}} \quad \mbox{if} \quad
+ \max\{-c^Tx, -h^Tz-b^Ty\} > 0
+\]
+and \None\ otherwise.  The fields \code{'primal infeasibility'} 
+and \code{'dual infeasibility'} are the residuals in the primal and dual
+equality constraints, defined as
+\[
+ \max\{ \frac{\|Gx+s-h\|}{\max\{1, \|h\|\}}, 
+  \frac{\|Ax-b\|}{\max\{1,\|b\|\}} \}, \qquad
+ \frac{\|G^Tz + A^Ty + c\|}{\max\{1, \|c\|\}}, 
+\]
+respectively.
 
 \item[\code{'primal infeasible'}]
 The \code{'x'} and \code{'s'} entries are \None, and the \code{'y'}, 
@@ -141,6 +163,11 @@ infeasibility, \ie, vectors that approximately satisfy
 \[
  G^T z + A^T y = 0, \qquad h^T z + b^T y = -1, \qquad z \succeq 0.
 \]
+The field \code{'residual as primal infeasibility certificate'} gives
+the residual 
+\[
+ \frac{\|G^Tz + A^Ty\|}{\max\{1, \|c\|\}}.
+\]
 
 \item[\code{'dual infeasible'}]  
 The \code{'y'} and \code{'z'} entries are \None, and the \code{'x'} 
@@ -149,9 +176,43 @@ infeasibility
 \[
  Gx + s = 0, \qquad Ax=0, \qquad  c^T x = -1, \qquad s \succeq 0.
 \]
+The field \code{'residual as dual infeasibility certificate'} gives
+the residual 
+\[
+ \max\{ \frac{\|Gx + s\|}{\max\{1, \|h\|\}},
+ \frac{\|Ax\|}{\max\{1,\|b\|\}} \}.
+\]
 
-\item[\code{'unknown'}] The \code{'x'}, \code{'s'}, \code{'y'}, 
-\code{'z'} entries are \None.
+\item[\code{'unknown'}] 
+This indicates that the algorithm terminated 
+early due to numerical difficulties or because the maximum number 
+of iterations was reached. 
+The \code{'x'}, \code{'s'}, \code{'y'}, \code{'z'} entries 
+contain the iterates when the algorithm terminated.
+Whether these entries are useful, as approximate solutions or certificates
+of primal and dual infeasibility, can be determined from the other fields 
+in the dictionary.
+The fields \code{'primal objective'}, \code{'dual objective'}, 
+\code{'gap'}, \code{'relative gap'}, \code{'primal infeasibility'},
+\code{'dual infeasibility'} are defined as when \code{'status'} is
+\code{'optimal'}.
+The field \code{'residual as primal infeasibility certificate'} is defined
+as
+\[
+\frac{\|G^Tz+A^Ty\|}{-(h^Tz + b^Ty) \max\{1, \|h\| \}}.
+\]
+if \tm{h^Tz+b^Ty < 0}, and \None\ otherwise.  A small value of this 
+residual indicates that \tm{y} and \tm{z}, divided by \tm{-h^Tz-b^Ty}, are 
+an approximate proof of primal infeasibility.
+The field \code{'residual as dual infeasibility certificate'} is defined
+as
+\[
+\max\{\frac{\|Gx+s\|}{-c^Tx \max\{1,\|h\|\}}, 
+ \frac{\|Ax\|}{-c^Tx \max\{1,\|b\|\}}\}
+\]
+if \tm{c^Tx < 0}, and as \None\ otherwise.
+A small value indicates that \tm{x} and \tm{s},
+divided by \tm{-c^Tx} are an approximate proof of dual infeasibility.  
 \end{description}
 
 It is required that 
@@ -186,8 +247,7 @@ As an example we solve the problem
 \]
 
 \begin{verbatim}
->>> from cvxopt.base import matrix
->>> from cvxopt import solvers 
+>>> from cvxopt import matrix, solvers
 >>> c = matrix([-6., -4., -5.])
 >>> G = matrix([[ 16., 7.,  24.,  -8.,   8.,  -1.,  0., -1.,  0.,  0.,   7.,  -5.,   1.,  -5.,   1.,  -7.,   1.,   -7.,  -4.], 
                 [-14., 2.,   7., -13., -18.,   3.,  0.,  0., -1.,  0.,   3.,  13.,  -6.,  13.,  12., -10.,  -6.,  -10., -28.],
@@ -319,7 +379,9 @@ default starting points are used for the corresponding variables.
 The role of the optional argument \var{kktsolver} is explained in 
 section~\ref{s-conelp-struct}.  
 
-\function{coneqp()} returns a dictionary with keys \code{'status'}, 
+\function{coneqp()} returns a dictionary that contains the result and 
+information about the accuracy of the solution.
+The most important fields have keys \code{'status'}, 
 \code{'x'}, \code{'s'}, \code{'y'}, \code{'z'}.  
 The \code{'status'} field  is a string with possible values
 \code{'optimal'} and \code{'unknown'}.  
@@ -331,17 +393,46 @@ solutions, which approximately satisfy
  Gx+s = h, \qquad Ax = b, \qquad Px + G^Tz + A^T y + c = 0, \qquad
  s \succeq 0, \qquad z \succeq 0, \qquad s^T z  = 0.
 \]
-\item[\code{'unknown'}] The \code{'x'}, \code{'s'}, \code{'y'}, 
-\code{'z'} entries are \None.
+\item[\code{'unknown'}] 
+This indicates that the algorithm terminated 
+early due to numerical difficulties or because the maximum number 
+of iterations was reached. 
+The \code{'x'}, \code{'s'}, \code{'y'}, \code{'z'} entries 
+contain the iterates when the algorithm terminated.
 \end{description}
+The other entries in the output dictionary summarize the accuracy
+with which the optimality conditions are satisfied.
+The fields \code{'primal objective'}, \code{'dual objective'}, and
+\code{'gap'} give the primal objective \tm{c^Tx}, the dual objective
+calculated as 
+\[
+ (1/2) x^TPx + q^T x + z^T(Gx-h) + y^T(Ax-b)
+\]
+and the gap \tm{s^Tz}.  The field \code{'relative gap'}
+is the relative gap, defined as
+\[
+ \frac{s^Tz}{-\mbox{primal objective}}
+ \quad \mbox{if\ } \mbox{primal objective} < 0, \qquad
+ \frac{s^Tz}{\mbox{dual objective}}
+ \quad \mbox{if\ } \mbox{dual objective} > 0, \qquad
+\]
+and \None\ otherwise.  The fields \code{'primal infeasibility'} 
+and \code{'dual infeasibility'} are the residuals in the primal and dual
+equality constraints, defined as
+\[
+ \max\{ \frac{\|Gx+s-h\|}{\max\{1, \|h\|\}}, 
+  \frac{\|Ax-b\|}{\max\{1,\|b\|\}} \}, \qquad
+  \frac{\|Px + G^Tz + A^Ty + q\|}{\max\{1, \|q\|\}}, 
+\]
+respectively.
 
 It is required that the problem is solvable and that 
 \[
 \Rank(A) = p, \qquad 
 \Rank(\left[\begin{array}{c} P \\ G \\ A \end{array}\right]) = n,
 \]
-where \tm{p} is the number or rows of \tm{A} and \tm{n} is the number of columns 
-of \tm{G} and \tm{A}.
+where \tm{p} is the number or rows of \tm{A} and \tm{n} is the number of 
+columns of \tm{G} and \tm{A}.
 \end{funcdesc}
 
 As an example, we solve a constrained least-squares problem
@@ -365,8 +456,7 @@ with
  \end{array} \right]. 
 \]
 \begin{verbatim}
->>> from cvxopt import base, solvers
->>> from cvxopt.base import matrix
+>>> from cvxopt import matrix, solvers
 >>> A = matrix([ [ .3, -.4,  -.2,  -.4,  1.3 ], 
                  [ .6, 1.2, -1.7,   .3,  -.3 ],
                  [-.3,  .0,   .6, -1.2, -2.0 ] ])
@@ -410,13 +500,20 @@ The \var{solver} argument is used to choose among three solvers.
 When it is omitted or \None, the CVXOPT function \function{conelp()} is 
 used.   The external solvers GLPK and MOSEK (if installed) can be selected
 by setting \code{\var{solver} = 'glpk'} or \code{\var{solver} = 'mosek'}; 
-see section~\ref{s-external}.
-
+see section~\ref{s-external}.  
 The meaning of the other arguments and the return value are the same as 
 for \function{conelp()} called with 
 \code{dims = \{'l': G.size[0], 'q': [], 's': []\}}. 
-The initial values are ignored when \code{solver = 'mosek'} or 
-\code{solver = 'glpk'}.
+
+The initial values are ignored when \code{solver} \code{'mosek'} or 
+\code{'glpk'}.
+With the GLPK option, the solver does not return certificates of
+primal or dual infeasibility: if the status is
+\code{'primal infeasible'} or \code{'dual infeasible'}, all entries of 
+the output dictionary are \None.
+If the GLPK or MOSEK solvers are used, and the code returns with
+status \code{'unknown'}, all the other fields in the output dictionary
+are \None.
 \end{funcdesc}
 
 As a simple example we solve the LP
@@ -429,8 +526,7 @@ As a simple example we solve the LP
  \end{array} 
 \]
 \begin{verbatim}
->>> from cvxopt.base import matrix
->>> from cvxopt import solvers 
+>>> from cvxopt import matrix, solvers
 >>> c = matrix([-4., -5.])
 >>> G = matrix([[2., 1., -1., 0.], [1., 2., 0., -1.]])
 >>> h = matrix([3., 3., 0., 0.])
@@ -496,7 +592,7 @@ vectors that approximately satisfy
 \end{funcdesc}
 
 As an example we compute the trade-off curve on page 187
-of the book \citetitle{http://www.stanford.edu/\~{}boyd/cvxbook}{Convex 
+of the book \citetitle{http://www.stanford.edu/\%7Eboyd/cvxbook}{Convex 
 Optimization}, by solving the quadratic program 
 \[
 \begin{array}{ll}
@@ -515,7 +611,7 @@ using the \ulink{Matplotlib}{http://matplotlib.sourceforge.net} package.
 
 \begin{verbatim}
 from math import sqrt
-from cvxopt.base import matrix
+from cvxopt import matrix
 from cvxopt.blas import dot 
 from cvxopt.solvers import qp
 import pylab
@@ -655,14 +751,16 @@ constraint.
 The arguments \var{primalstart} and \var{dualstart} are ignored when 
 the MOSEK solver is used.
 
-\function{socp()} returns a dictionary with keys \var{'status'},
-\var{'x'}, \var{'sl'}, \var{'sq'}, \var{'y'}, \var{'zl'}, \var{'zq'}.
-The meaning is similar to the output of \function{conelp()}.
-The \var{'sl'} and \var{'zl'} fields are matrices with the primal 
+\function{socp()} returns a dictionary that include entries with
+keys \var{'status'},
+\code{'x'}, \code{'sl'}, \code{'sq'}, \code{'y'}, \code{'zl'}, \code{'zq'},
+The \code{'sl'} and \code{'zl'} fields are matrices with the primal 
 slacks and dual variables associated with the componentwise linear 
 inequalities.
-The \var{'sq'} and \var{'zq'} fields are lists with the primal slacks and 
-dual variables associated with the second-order cone inequalities.
+The \code{'sq'} and \code{'zq'} fields are lists with the primal slacks 
+and dual variables associated with the second-order cone inequalities.
+The other entries in the output dictionary have the same meaning as
+in the output of \function{conelp()}.
 \end{funcdesc}
 
 As an example, we solve  the second-order cone program
@@ -680,8 +778,7 @@ As an example, we solve  the second-order cone program
 \end{array}
 \]
 \begin{verbatim}
->>> from cvxopt.base import matrix
->>> from cvxopt import solvers
+>>> from cvxopt import matrix, solvers
 >>> c = matrix([-2., 1., 5.])
 >>> G = [ matrix( [[12., 13., 12.], [6., -3., -12.], [-5., -5., 6.]] ) ]
 >>> G += [ matrix( [[3., 3., -1., 1.], [-6., -6., -9., 19.], [10., -2., -2., -3.]] ) ]
@@ -801,14 +898,16 @@ constraint.
 The arguments \var{primalstart} and \var{dualstart} are ignored when 
 the DSDP solver is used.
 
-\function{sdp()} returns a dictionary with keys \code{'status'}, 
-\code{'x'}, \code{'sl'}, \code{'ss'}, \code{'y'}, \code{'zl'},  
-\code{'ss'}.
-The \var{'sl'} and \var{'zl'} fields are matrices with the primal 
+\function{sdp()} returns a dictionary that includes entries with keys 
+\code{'status'}, \code{'x'}, \code{'sl'}, \code{'ss'}, \code{'y'}, 
+\code{'zl'},  \code{'ss'}.
+The \code{'sl'} and \code{'zl'} fields are matrices with the primal 
 slacks and dual variables associated with the componentwise linear 
 inequalities.
-The \var{'ss'} and \var{'zs'} fields are lists with the primal slacks and 
-dual variables associated with the second-order cone inequalities.
+The \code{'ss'} and \code{'zs'} fields are lists with the primal slacks 
+and dual variables associated with the second-order cone inequalities.
+The other entries in the output dictionary have the same meaning as in the
+output of \function{conelp()}.
 \end{funcdesc}
 
 We illustrate the calling sequence with a small example.
@@ -846,8 +945,7 @@ We illustrate the calling sequence with a small example.
 \end{array}
 \]
 \begin{verbatim}
->>> from cvxopt.base import matrix
->>> from cvxopt import solvers
+>>> from cvxopt import matrix, solvers
 >>> c = matrix([1.,-1.,1.])
 >>> G = [ matrix([[-7., -11., -11., 3.], 
                   [ 7., -18., -18., 8.], 
@@ -908,7 +1006,7 @@ solution of a set of linear equations (`KKT equations') of the form
 (with \tm{P=0} in \function{conelp()}).
 The matrix \tm{W} depends on the current iterates and is defined as 
 follows.  We use the notation of sections~\ref{s-conelp} 
-and~\ref{s-coneqp}.  Suppose 
+and~\ref{s-coneqp}.  Let 
 \[
  u = \left(u_\mathrm{l}, \; u_{\mathrm{q},0}, \; \ldots, \; 
  u_{\mathrm{q},M-1}, \; \svec{(u_{\mathrm{s},0})}, \; \ldots, \; 
@@ -1089,12 +1187,12 @@ By exploiting the structure in the inequalities, the cost of
 an iteration of an interior-point method can be reduced
 to the cost of least-squares problem of the same dimensions. 
 (See section 11.8.2 in the book 
-\citetitle{http://www.ee.ucla.edu/\~{}vandenbe/cvxbook}{Convex Optimization}.) 
+\citetitle{http://www.stanford.edu/\%7Eboyd/cvxbook}{Convex 
+Optimization}.)
 The code belows takes advantage of this fact.
 
 \begin{verbatim}
-from cvxopt import base, blas, lapack, solvers
-from cvxopt.base import matrix, spmatrix, mul, div
+from cvxopt import blas, lapack, solvers, matrix, spmatrix, mul, div
 
 def l1(P, q):
     """
@@ -1203,8 +1301,7 @@ The SDP
 can be solved efficiently by exploiting properties of the diag operator.
 
 \begin{verbatim}
-from cvxopt import base, blas, lapack, solvers
-from cvxopt.base import matrix
+from cvxopt import blas, lapack, solvers, matrix
 
 def mcsdp(w):
     """
@@ -1474,8 +1571,7 @@ with variables \tm{x} and \tm{u}.  The implementation below is
 efficient when \tm{A} has many more columns than rows. 
 
 \begin{verbatim}
-from cvxopt.base import matrix, spdiag, mul, div
-from cvxopt import base, blas, lapack, solvers
+from cvxopt import matrix, spdiag, mul, div, blas, lapack, solvers, sqrt
 import math
 
 def l1regls(A, y):
@@ -1562,8 +1658,7 @@ def l1regls(A, y):
         d1, d2 = W['di'][:n]**2, W['di'][n:]**2
 
         # ds is square root of diagonal of D
-        ds = math.sqrt(2.0) * div( mul( W['di'][:n], W['di'][n:]), 
-            base.sqrt(d1+d2) )
+        ds = math.sqrt(2.0) * div( mul( W['di'][:n], W['di'][n:]), sqrt(d1+d2) )
         d3 =  div(d2 - d1, d1 + d2)
      
         # Asc = A*diag(d)^-1/2
diff --git a/doc/copyright.tex b/doc/copyright.tex
index addc2b7..30b6933 100644
--- a/doc/copyright.tex
+++ b/doc/copyright.tex
@@ -23,10 +23,10 @@ suite of sparse matrix algorithms, including:
 \item AMD Version 2.2.0.  
  Copyright (c) 2007 by Timothy A.\ Davis, Patrick R.\ Amestoy, and 
  Iain S.\ Duff.  
-\item CHOLMOD Version 1.6.0.  
+\item CHOLMOD Version 1.7.0.  
  Copyright (c) 2005-2007 by University of Florida, Timothy A. Davis 
  and W. Hager.
-\item COLAMD version 2.7.0.  Copyright (c) 1998-2007 by Timothy A.\ Davis.
+\item COLAMD Version 2.7.0.  Copyright (c) 1998-2007 by Timothy A.\ Davis.
 \item UMFPACK Version 5.2.0.  Copyright (c) 1995-2006 by Timothy A.\  Davis.
 \EIT
 
diff --git a/doc/cvxopt.tex b/doc/cvxopt.tex
index ab02ca9..d76a5d5 100644
--- a/doc/cvxopt.tex
+++ b/doc/cvxopt.tex
@@ -14,7 +14,7 @@
 \newcommand{\BEAS}{\begin{eqnarray*}}
 \newcommand{\EEAS}{\end{eqnarray*}}
 \newcommand{\Rank}{\mathop{\bf rank}}
-\newcommand{\Tr}{\mathop{\bf tr}}
+\newcommand{\Tr}{\mathop{\mathbf{tr}}}
 \newcommand{\lse}{\mathop{\mathbf{lse}}}
 \newcommand{\diag}{\mbox{\bf diag}\,}
 \newcommand{\ones}{{\bf 1}}
@@ -82,7 +82,7 @@
 
 \title{CVXOPT User's Guide} 
 \author{Joachim Dahl \& Lieven Vandenberghe}
-\date{Release 1.0 -- April 24, 2008} 
+\date{Release 1.1 -- October 15, 2008} 
 \begin{document}
 \Configure{crosslinks*}{next}{prev}{up}{}
 
@@ -90,11 +90,10 @@
 
 \input{copyright}
 \input{intro}
-\input{base}
+\input{matrices}
 \input{blas}
 \input{lapack}
 \input{fftw}
-\input{base_sparse}
 \input{spsolvers}
 \input{coneprog}
 \input{solvers}
diff --git a/doc/fftw.tex b/doc/fftw.tex
index f5e0f9e..f5d6c1f 100644
--- a/doc/fftw.tex
+++ b/doc/fftw.tex
@@ -29,19 +29,27 @@ discrete Fourier transforms: if \var{X} has {\it n} rows,
 \]
 \end{funcdesc}
 
-The separable discrete two dimensional Fourier transform first
-computes the corresponding one dimensional tranform along the columns 
-of the matrix, followed by the one dimensional transform along the
-rows of the matrix. 
-
-\begin{funcdesc}{dft2}{X}
-Replaces a dense complex matrix with the two dimensional
-discrete Fourier transform. 
+The module also includes a discrete \tm{N}-dimensional Fourier transform.
+The input matrix is interpreted as an \tm{N}-dimensional
+matrix stored in column-major order. The discrete \tm{N}-dimensional 
+Fourier transform computes the corresponding one-dimensional transform 
+along each dimension.  For example, 
+the two-dimensional transform applies a one-dimensional transform to 
+all the columns of the matrix, followed by a one-dimensional transform
+to all the rows of the matrix. 
+
+\begin{funcdesc}{dftn}{X\optional{, dims=X.size}}
+Replaces a dense complex matrix with its \tm{N}-dimensional
+discrete Fourier transform. The dimensions of the \tm{N}-dimensional
+matrix are given by the \tm{N}-tuple \var{dim}. 
+The two-dimensional transform is computed as \code{dftn(X, X.size)}. 
 \end{funcdesc}
 
-\begin{funcdesc}{idft2}{X}
-Replaces a dense complex matrix with the inverse two dimensional
-discrete Fourier transform.
+\begin{funcdesc}{idftn}{X\optional{, dims=X.size}}
+Replaces a dense complex \tm{N}-dimensional matrix with its inverse 
+\tm{N}-dimensional discrete Fourier transform. The dimensions of the matrix
+are given by the tuple \var{dim}. The two-dimensional inverse transform 
+is computed as \code{idftn(X, X.size)}.
 \end{funcdesc}
 
 \section{Discrete Cosine Transform} 
@@ -75,23 +83,31 @@ Replaces the columns of a dense real matrix with the inverses
 of the discrete cosine transforms defined above.  
 \end{funcdesc}
 
-The separable discrete two dimensional cosine transform first
-computes the corresponding one dimensional tranform along the columns 
-of the matrix, followed by the one dimensional transform along the
-rows of the matrix. 
-
-\begin{funcdesc}{dct2}{X\optional{, type=2}}
-Replaces a dense real matrix with the two dimensional
-discrete cosine transform.
+The module also includes a discrete \tm{N}-dimensional cosine transform.
+The input matrix is interpreted as an \tm{N}-dimensional
+matrix stored in column-major order. 
+The discrete \tm{N}-dimensional cosine transform computes the corresponding 
+one-dimensional transform along each dimension.  For example, 
+the two-dimensional transform applies a one-dimensional transform to 
+all the rows of the matrix, followed by a one-dimensional transform
+to all the columns of the matrix. 
+
+\begin{funcdesc}{dctn}{X\optional{, dims=X.size\optional{, type=2}}}
+Replaces a dense real matrix with its \tm{N}-dimensional
+discrete cosine transform. The dimensions of the \tm{N}-dimensional
+matrix are given by the \tm{N}-tuple \var{dim}. 
+The two-dimensional transform is computed as \code{dctn(X, X.size)}. 
 \end{funcdesc}
 
-\begin{funcdesc}{idct2}{X\optional{, type=2}}
-Replaces a dense real matrix with the inverse two dimensional
-discrete cosine transform.
+\begin{funcdesc}{idctn}{X\optional{, dims=X.size\optional{, type=2}}}
+Replaces a dense real \tm{N}-dimensional matrix with its inverse 
+\tm{N}-dimensional discrete cosine transform. The dimensions of the matrix
+are given by the tuple \var{dim}.  The two-dimensional inverse transform 
+is computed as \code{idctn(X, X.size)}.
 \end{funcdesc}
 
 \section{Discrete Sine Transform} 
-\begin{funcdesc}{dst}{X\optional{, type=1}}
+\begin{funcdesc}{dst}{X, dims\optional{, type=1}}
 Replaces the columns of a dense real matrix with their discrete
 sine transforms.  The second argument, an integer between 1 and 4,
 denotes the type of transform (DST-I, DST-II, DST-III, DST-IV).
@@ -115,22 +131,32 @@ These transforms are defined as follows
 \EEAS
 \end{funcdesc}
 
-\begin{funcdesc}{idst}{X\optional{, type=1}}
+\begin{funcdesc}{idst}{X, dims\optional{, type=1}}
 Replaces the columns of a dense real matrix with the inverses
 of the discrete sine transforms defined above.  
 \end{funcdesc}
 
-The separable discrete two dimensional sine transform first
-computes the corresponding one dimensional tranform along the columns 
-of the matrix, followed by the one dimensional transform along the
-rows of the matrix. 
-
-\begin{funcdesc}{dst2}{X\optional{, type=1}}
-Replaces a dense real matrix with the two dimensional
-discrete sine transform.
+The module also includes a discrete \tm{N}-dimensional sine transform.
+The input matrix is interpreted as an \tm{N}-dimensional
+matrix stored in column-major order.  
+The discrete \tm{N}-dimensional sine transform computes the corresponding 
+one-dimensional transform along each dimension.  For example, 
+the two-dimensional transform applies a one-dimensional transform to 
+all the rows of the matrix, followed by a one-dimensional transform
+to all the columns of the matrix. 
+
+\begin{funcdesc}{dstn}{X\optional{, dims=X.size\optional{, type=2}}}
+Replaces a dense real matrix with its \tm{N}-dimensional
+discrete sine transform. The dimensions of the \tm{N}-dimensional
+matrix are given by the \tm{N}-tuple \var{dim}. 
+The two-dimensional transform is computed as 
+\code{dstn(X, X.size)}. 
 \end{funcdesc}
 
-\begin{funcdesc}{idst2}{X\optional{, type=1}}
-Replaces a dense real matrix with the inverse two dimensional
-discrete sine transform.
+\begin{funcdesc}{idstn}{X\optional{, dims=X.size\optional{, type=2}}}
+Replaces a dense real \tm{N}-dimensional matrix with its inverse 
+\tm{N}-dimensional discrete sine transform. 
+The dimensions of the matrix are given by the tuple \var{dim}. 
+The two-dimensional inverse transform is computed as 
+\code{idstn(X, X.size)}.
 \end{funcdesc}
diff --git a/doc/figures/floorplan.pdf b/doc/figures/floorplan.pdf
index c211716..9338306 100644
Binary files a/doc/figures/floorplan.pdf and b/doc/figures/floorplan.pdf differ
diff --git a/doc/figures/normappr.pdf b/doc/figures/normappr.pdf
index d726169..b2bca3c 100644
Binary files a/doc/figures/normappr.pdf and b/doc/figures/normappr.pdf differ
diff --git a/doc/figures/portfolio1.pdf b/doc/figures/portfolio1.pdf
index 82c0312..64db667 100644
Binary files a/doc/figures/portfolio1.pdf and b/doc/figures/portfolio1.pdf differ
diff --git a/doc/figures/portfolio2.pdf b/doc/figures/portfolio2.pdf
index 79e2450..352c7ca 100644
Binary files a/doc/figures/portfolio2.pdf and b/doc/figures/portfolio2.pdf differ
diff --git a/doc/intro.tex b/doc/intro.tex
index 42b2d58..4ad5423 100644
--- a/doc/intro.tex
+++ b/doc/intro.tex
@@ -10,24 +10,24 @@ optimization applications straightforward by building on Python's
 extensive standard library and on the strengths of Python as a 
 high-level programming language.  
 
-Release 1.0 of CVXOPT includes routines for basic linear algebra 
-calculations, interfaces to efficient libraries for solving dense and 
-sparse linear equations, convex optimization solvers written in Python,
-interfaces to a few other optimization libraries, 
-and a modeling tool for piecewise-linear convex optimization problems.
-These components are organized in different modules.  
+CVXOPT extends the built-in Python objects with two matrix objects: 
+a \mtrx\ object for dense matrices and an \spmtrx\ object for sparse 
+matrices.  These two matrix types are introduced in 
+chapter~\ref{chap:matrix} of this user's guide, together with the 
+arithmetic operations and functions defined for them.  
+The following chapters~\ref{chap:blas}--\ref{c-spsolvers} describe
+interfaces to several libraries for dense and sparse matrix computations. 
+The CVXOPT optimization routines are described in 
+chapters~\ref{chap:coneprog}--\ref{chap:modeling}.
+These include convex optimization solvers written in Python, 
+interfaces to a few other optimization libraries, and a modeling tool 
+for piecewise-linear convex optimization problems.
+
+CVXOPT is organized in different modules.  
 \begin{description}
-\item[\module{cvxopt.base}] This module defines a Python type
- \mtrx\ for storing and manipulating dense matrices, 
- a Python type \spmtrx\ for storing and manipulating sparse 
- matrices, routines for generating sparse dense matrices 
- (see section~\ref{s-random}), and routines 
- for sparse matrix-vector and matrix-matrix 
- multiplication (see chapters~\ref{chap:matrix} 
- and~\ref{chap:spmatrix}).
 \item[\module{cvxopt.blas}] Interface to most of the double-precision 
  real and complex BLAS (chapter~\ref{chap:blas}).
-\item[\module{cvxopt.lapack}] Interface to the dense double-precision 
+\item[\module{cvxopt.lapack}] Interface to dense double-precision 
  real and complex linear equation solvers and eigenvalue routines 
  from LAPACK (chapter~\ref{chap:lapack}).
 \item[\module{cvxopt.fftw}] An optional interface to the 
@@ -39,7 +39,7 @@ These components are organized in different modules.
 \item[\module{cvxopt.cholmod}] Interface to the sparse Cholesky 
  solver from CHOLMOD (section~\ref{s-cholmod}).
 \item[\module{cvxopt.solvers}] Convex optimization routines
- and optional interfaces to solvers from GLPK, MOSEK and DSDP5
+ and optional interfaces to solvers from GLPK, MOSEK, and DSDP5
  (chapters~\ref{chap:coneprog} and~\ref{chap:solvers}).
 \item[\module{cvxopt.modeling}]  Routines for specifying and solving 
  linear programs and convex optimization problems with piecewise-linear 
diff --git a/doc/lapack.tex b/doc/lapack.tex
index dad040d..d03e34c 100644
--- a/doc/lapack.tex
+++ b/doc/lapack.tex
@@ -5,8 +5,8 @@ The module \module{cvxopt.lapack} includes functions for
 solving dense sets of linear equations, for the corresponding matrix 
 factorizations (LU, Cholesky, $\mathrm{LDL^T}$),
 for solving least-squares and least-norm problems, for QR 
-factorization, for symmetric eigenvalue problems and for singular 
-value decomposition.  
+factorization, for symmetric eigenvalue problems, singular 
+value decomposition, and Schur factorization.  
 
 In this chapter we briefly describe the Python calling sequences.
 For further details on the underlying LAPACK functions we refer to the 
@@ -100,7 +100,7 @@ In the following example we compute
 for randomly generated problem data, factoring the coefficient matrix 
 once.
 \begin{verbatim}
->>> from cvxopt.base import matrix, normal
+>>> from cvxopt import matrix, normal
 >>> from cvxopt.lapack import gesv, getrs
 >>> n = 10
 >>> A = normal(n,n)
@@ -129,9 +129,8 @@ least \tm{n}.
 If \var{ipiv} is provided, then \var{A} must have 
 \tm{2\s{k}{l} + \s{k}{u} + 1} rows.  On entry the diagonals of \tm{A} are
 stored in rows \tm{\s{k}{l} + 1} to 
-\tm{2\s{k}{l} + \s{k}{u} +1} of the \var{A}, using
-the BLAS format for general band matrices 
-(see section~\ref{s-conventions}).
+\tm{2\s{k}{l} + \s{k}{u} +1} of \var{A}, using the BLAS format for 
+general band matrices (see section~\ref{s-conventions}).
 On exit, the factorization is returned in {\var A} and \var{ipiv}.
 
 If \var{ipiv} is not provided, then \var{A} must have 
@@ -186,7 +185,7 @@ As an example, we solve a linear equation with
  \end{array}\right].
 \]
 \begin{verbatim}
->>> from cvxopt.base import matrix
+>>> from cvxopt import matrix
 >>> from cvxopt.lapack import gbsv, gbtrf, gbtrs
 >>> n, kl, ku = 4, 2, 1
 >>> A = matrix([[0., 1., 3., 6.], [2., 4., 7., 10.], [5., 8., 11., 0.], [9., 12., 0., 0.]])
@@ -346,7 +345,7 @@ As an example, we use \function{posv()} to solve the linear system
 by block-elimination. 
 We first pick a random problem.
 \begin{verbatim}
->>> from cvxopt.base import matrix, div, normal, uniform
+>>> from cvxopt import matrix, div, normal, uniform
 >>> from cvxopt.blas import syrk, gemv
 >>> from cvxopt.lapack import posv
 >>> m, n = 100, 50  
@@ -681,7 +680,7 @@ QR factorization of a real or complex matrix \var{A}:
   A = Q R.
 \]
 If \var{A} is \tm{m} by \tm{n}, then \tm{Q} is \tm{m} by \tm{m} 
-and orthogonal/unitary, and \var{R} is \tm{m} by \tm{n}
+and orthogonal/unitary, and \tm{R} is \tm{m} by \tm{n}
 and upper triangular (if \tm{m} is greater than or equal to \tm{n}), 
 or upper trapezoidal (if \tm{m} is less than or equal to \tm{n}).  
 \var{tau}  is a matrix of the same type as {\var A} and of length at 
@@ -734,8 +733,7 @@ In the following example, we solve a least-squares problem
 by a direct call to \function{gels()}, and by separate calls to 
 \function{geqrf()}, \function{ormqr()}, and \function{trtrs()}.
 \begin{verbatim}
->>> from cvxopt import random, blas, lapack
->>> from cvxopt.base import matrix
+>>> from cvxopt import random, blas, lapack, matrix
 >>> m, n = 10, 5
 >>> A, b = random.normal(m,n), random.normal(m,1)
 >>> x1 = +b
@@ -971,6 +969,188 @@ or \code{'A'} or \var{jobz} is \code{'O'} and \tm{m} is less than
 On exit, the contents of \var{A} are destroyed.
 \end{funcdesc}
 
+\section{Schur and Generalized Schur Factorization}
+\begin{funcdesc}{gees}{A\optional{, w=\None\optional{, 
+V=\None\optional{, select=\None}}}}
+Computes the Schur factorization 
+\[
+ A = V S V^T \quad \mbox{($A$ real)}, \qquad 
+ A = V S V^H \quad \mbox{($A$ complex)}
+\]
+of a real or complex \tm{n} by \tm{n} matrix \tm{A}.  
+If \tm{A} is real, the matrix of Schur vectors \tm{V} is orthogonal, 
+and \tm{S} is a real upper quasi-triangular matrix with 1 by 1 or 2 by 2 
+diagonal blocks.  The 2 by 2 blocks correspond to complex conjugate pairs
+of eigenvalues of \tm{A}.
+If \tm{A} is complex, the matrix of Schur vectors \tm{V} is unitary, 
+and \tm{S} is a complex upper triangular matrix with the eigenvalues of
+\tm{A} on the diagonal.
+
+The optional argument \var{w} is a complex matrix of length at least 
+\tm{n}.  If it is provided, the eigenvalues of \var{A} are returned in 
+\var{w}.
+The optional argument \var{V} is an \tm{n} by \tm{n} matrix of the same
+type as \var{A}.  If it is provided, then the Schur vectors are returned
+in \var{V}.
+
+The argument \var{select} is an optional ordering routine.
+It must be a Python function that can be called as \samp{f(s)} with
+a complex argument \var{s}, and returns \True\ or \False.
+The eigenvalues for which \var{select} returns \True\ will be 
+selected to appear first along the diagonal.
+(In the real Schur factorization, if either one of a complex conjugate
+pair of eigenvalues is selected, then both are selected.)
+
+On exit, \var{A} is replaced with the matrix \tm{S}.
+The function \function{gees()} returns an integer equal to the number 
+of eigenvalues that were selected by the ordering routine.
+If \var{select} is \None, then \function{gees()} returns 0.
+\end{funcdesc}
+
+As an example we compute the complex Schur form of the matrix
+\[
+ A = \left[\begin{array}{rrrrr}
+   -7 &  -11 & -6  & -4 &  11 \\
+    5 &  -3  &  3  & -12 & 0 \\
+   11 &  11  & -5  & -14 & 9 \\
+   -4 &   8  &  0  &  8 &  6 \\
+   13 & -19  & -12 & -8 & 10 
+ \end{array}\right].
+\] 
+\begin{verbatim}
+>>> A = matrix([[-7., 5., 11., -4., 13.], [-11., -3., 11., 8., -19.], [-6., 3., -5., 0., -12.], [-4., -12., -14., 8., -8.], [11., 0., 9., 6., 10.]])
+>>> S = matrix(A, tc='z')
+>>> w = matrix(0.0, (5,1), 'z')
+>>> gees(S, w)
+0
+>>> print S
+[ 5.67e+00+j1.69e+01 -2.13e+01+j2.85e+00  1.40e+00+j5.88e+00 -4.19e+00+j2.05e-01  3.19e+00-j1.01e+01]
+[ 0.00e+00-j0.00e+00  5.67e+00-j1.69e+01  1.09e+01+j5.93e-01 -3.29e+00-j1.26e+00 -1.26e+01+j7.80e+00]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  1.27e+01+j3.43e-17 -6.83e+00+j2.18e+00  5.31e+00-j1.69e+00]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00 -1.31e+01-j0.00e+00 -2.60e-01-j0.00e+00]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00 -7.86e+00-j0.00e+00]
+>>> print w
+[ 5.67e+00+j1.69e+01]
+[ 5.67e+00-j1.69e+01]
+[ 1.27e+01+j3.43e-17]
+[-1.31e+01-j0.00e+00]
+[-7.86e+00-j0.00e+00]
+\end{verbatim}
+An ordered Schur factorization with the eigenvalues in the left
+half of the complex plane ordered first, can be computed as follows. 
+\begin{verbatim}
+>>> S = matrix(A, tc='z')
+>>> def F(x): return (x.real < 0.0)
+...
+>>> gees(S, w, select = F)
+2
+>>> print S
+[-1.31e+01-j0.00e+00 -1.72e-01+j7.93e-02 -2.81e+00+j1.46e+00  3.79e+00-j2.67e-01  5.14e+00-j4.84e+00]
+[ 0.00e+00-j0.00e+00 -7.86e+00-j0.00e+00 -1.43e+01+j8.31e+00  5.17e+00+j8.79e+00  2.35e+00-j7.86e-01]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  5.67e+00+j1.69e+01 -1.71e+01-j1.41e+01  1.83e+00-j4.63e+00]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  5.67e+00-j1.69e+01 -8.75e+00+j2.88e+00]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  1.27e+01+j3.43e-17]
+>>> print w
+[-1.31e+01-j0.00e+00]
+[-7.86e+00-j0.00e+00]
+[ 5.67e+00+j1.69e+01]
+[ 5.67e+00-j1.69e+01]
+[ 1.27e+01+j3.43e-17]
+\end{verbatim}
+
+
+\begin{funcdesc}{gges}{A, B\optional{, a=\None, b=\None\optional{, 
+Vl=\None\optional{, Vr=\None\optional{, select=\None}}}}}
+Computes the generalized Schur factorization 
+\[
+ A = V_l S V_r^T, \quad B = V_l T V_r^T \quad \mbox{($A$ and $B$ real)}, 
+ \qquad 
+ A = V_l S V_r^H, \quad B = V_l T V_r^H, \quad \mbox{($A$ and $B$ complex)}
+\]
+of a pair of real or complex \tm{n} by \tm{n} matrices \tm{A}, \tm{B}.  
+If \tm{A} and \tm{B} are real, then the matrices of left and right Schur 
+vectors \tm{\s{V}{l}} and \tm{\s{V}{r}} are orthogonal, 
+\tm{S} is a real upper quasi-triangular matrix with 1 by 1 or 2 by 2 
+diagonal blocks, and \tm{T} is a real triangular matrix with nonnegative
+diagonal.  The 2 by 2 blocks along the diagonal of \tm{S} correspond to 
+complex conjugate pairs of generalized eigenvalues of \tm{A}, \tm{B}.
+If \tm{A} and \tm{B} are complex, the matrices of left and right Schur 
+vectors \tm{\s{V}{l}} and \tm{\s{V}{r}} are unitary, \tm{S} is complex 
+upper triangular, and \tm{T} is complex upper triangular with 
+nonnegative real diagonal.  
+
+The optional arguments \var{a} and \var{b} are \ztc\ and \dtc\ matrices 
+of length at least \tm{n}.  If these are provided, the generalized
+eigenvalues of \var{A}, \var{B} are returned in \var{a} and \var{b}.
+(The generalized eigenvalues are the ratios \var{a[k] / b[k]}.) 
+The optional arguments \var{Vl} and \var{Vr} are \tm{n} by \tm{n} matrices
+of the same type as \var{A} and \var{B}.  If they are provided, then the 
+left Schur vectors are returned in \var{Vl} and the right Schur vectors
+are returned in \var{Vr}.
+
+The argument \var{select} is an optional ordering routine.
+It must be a Python function that can be called as \samp{f(x,y)} with
+a complex argument \var{x} and a real argument \var{y}, and returns 
+\True\ or \False.
+The eigenvalues for which \var{select} returns \True\ will be 
+selected to appear first on the diagonal.
+(In the real Schur factorization, if either one of a complex conjugate
+pair of eigenvalues is selected, then both are selected.)
+
+On exit, \var{A} is replaced with the matrix \tm{S} and 
+\var{B} is replaced with the matrix \tm{T}.
+The function \function{gges()} returns an integer equal to the number 
+of eigenvalues that were selected by the ordering routine.
+If \var{select} is \None, then \function{gges()} returns 0.
+\end{funcdesc}
+
+As an example, we compute the generalized complex Schur form of the
+matrix \tm{A} of the previous example, and 
+\[
+ B = \left[\begin{array}{ccccc}
+  1 & 0 & 0 & 0 & 0 \\
+  0 & 1 & 0 & 0 & 0 \\
+  0 & 0 & 1 & 0 & 0 \\
+  0 & 0 & 0 & 1 & 0 \\
+  0 & 0 & 0 & 0 & 0 \end{array}\right].
+\]
+\begin{verbatim}
+>>> A = matrix([[-7., 5., 11., -4., 13.], [-11., -3., 11., 8., -19.], [-6., 3., -5., 0., -12.], [-4., -12., -14., 8., -8.], [11., 0., 9., 6., 10.]])
+>>> B = matrix(0.0, (5,5))
+>>> B[:19:6] = 1.0
+>>> S = matrix(A, tc='z')
+>>> T = matrix(B, tc='z')
+>>> a = matrix(0.0, (5,1), 'z')
+>>> b = matrix(0.0, (5,1))
+>>> gges(S, T, a, b)
+0
+>>> print S
+[ 6.64e+00-j8.87e+00 -7.81e+00-j7.53e+00  6.16e+00-j8.51e-01  1.18e+00+j9.17e+00  5.88e+00-j4.51e+00]
+[ 0.00e+00-j0.00e+00  8.48e+00+j1.13e+01 -2.12e-01+j1.00e+01  5.68e+00+j2.40e+00 -2.47e+00+j9.38e+00]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00 -1.39e+01-j0.00e+00  6.78e+00-j0.00e+00  1.09e+01-j0.00e+00]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00 -6.62e+00-j0.00e+00 -2.28e-01-j0.00e+00]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00 -2.89e+01-j0.00e+00]
+>>> print T
+[ 6.46e-01-j0.00e+00  4.29e-01-j4.79e-02  2.02e-01-j3.71e-01  1.08e-01-j1.98e-01 -1.95e-01+j3.58e-01]
+[ 0.00e+00-j0.00e+00  8.25e-01-j0.00e+00 -2.17e-01+j3.11e-01 -1.16e-01+j1.67e-01  2.10e-01-j3.01e-01]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  7.41e-01-j0.00e+00 -3.25e-01-j0.00e+00  5.87e-01-j0.00e+00]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  8.75e-01-j0.00e+00  4.84e-01-j0.00e+00]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00  0.00e+00-j0.00e+00]
+>>> print a
+[ 6.64e+00-j8.87e+00]
+[ 8.48e+00+j1.13e+01]
+[-1.39e+01-j0.00e+00]
+[-6.62e+00-j0.00e+00]
+[-2.89e+01-j0.00e+00]
+>>> print b
+[ 6.46e-01]
+[ 8.25e-01]
+[ 7.41e-01]
+[ 8.75e-01]
+[ 0.00e+00]
+\end{verbatim}
+
+
 \section{Example: Analytic Centering}
 The analytic centering problem is defined as
 \[
@@ -993,8 +1173,7 @@ system.   The code can be further optimized by replacing the
 matrix-vector products with the level-2 BLAS function \function{gemv()}.
 
 \begin{verbatim}
-from cvxopt.base import matrix, log, mul, div
-from cvxopt import blas, lapack, random
+from cvxopt import matrix, log, mul, div, blas, lapack, random
 from math import sqrt
 
 def acent(A,b):
@@ -1015,7 +1194,7 @@ def acent(A,b):
     for iter in xrange(MAXITERS):
         
         # Gradient is g = A^T * (1./(b-A*x)).
-        d = (b-A*x)**-1
+        d = (b - A*x)**-1
         g = A.T * d
 
         # Hessian is H = A^T * diag(d)^2 * A.
diff --git a/doc/matrices.tex b/doc/matrices.tex
new file mode 100644
index 0000000..471c0ad
--- /dev/null
+++ b/doc/matrices.tex
@@ -0,0 +1,1363 @@
+\chapter{Dense and Sparse Matrices}\label{chap:matrix}
+
+This chapter describes the two CVXOPT matrix types: 
+\mtrx\ objects, used for dense matrix computations, 
+and \spmtrx\ objects, used for sparse matrix computations.
+
+\section{Dense Matrices}\label{s-creating-matrices}
+A dense matrix is created by calling the function \function{matrix()}. 
+The arguments specify the values of the coefficients, the
+dimensions, and the type (integer, double or complex) of the matrix.
+
+\begin{funcdesc}{matrix}{x\optional{, size\optional{, tc}}}
+\var{size} is a tuple of length two with the matrix dimensions.
+The number of rows and/or the number of columns can be zero.
+
+\var{tc} stands for typecode. The possible values are \itc, \dtc\ and 
+\ztc, for integer, real (double) and complex matrices, respectively.  
+
+\var{x} can be a number, a sequence of numbers, a dense or sparse 
+matrix, a one- or two-dimensional \program{NumPy} array, or a list of
+lists of matrices and numbers.  
+\BIT
+\item If \var{x} is a number (Python \intgr, \flt\ or \cmplx), a matrix
+is created with the dimensions specified by \var{size} and with all the 
+coefficients equal to \var{x}.  
+The default value of \var{size} is \tm{(1,1)}, and the default value
+of \var{tc} is the type of \var{x}.
+If necessary, the type of \var{x} is converted (from integer to double
+when used to create a matrix of type \dtc, and from integer or
+double to complex when used to create a matrix of type \ztc).
+
+\begin{verbatim}
+>>> from cvxopt import matrix
+>>> A = matrix(1, (1,4))   
+>>> print A
+[ 1  1  1  1]
+>>> A = matrix(1.0, (1,4))   
+>>> print A
+[ 1.00e+00  1.00e+00  1.00e+00  1.00e+00]
+>>> A = matrix(1+1j)     
+>>> print A
+[ 1.00e+00+j1.00e+00]
+\end{verbatim}
+
+\item If \var{x} is a sequence of numbers (list, tuple, \module{array}
+array, xrange object, one-dimensional \program{NumPy} array, \ldots),
+then the numbers are interpreted as the coefficients of a matrix in 
+column-major order.  The length of \var{x} must be equal to the product 
+of \code{size[0]} and \code{size[1]}.
+If \var{size} is not specified, a matrix with one column is created. 
+If \var{tc} is not specified, it is determined from the elements of 
+\var{x} (and if that is impossible, for example because \var{x} is
+an empty list, a value \itc\ is used).  
+Type conversion takes place as for scalar \var{x}.
+
+The following example shows several ways to define the same integer 
+matrix.
+\begin{verbatim}
+>>> A = matrix([0, 1, 2, 3], (2,2))  
+>>> A = matrix((0, 1, 2, 3), (2,2))  
+>>> A = matrix(xrange(4), (2,2))
+>>> from array import array
+>>> A = matrix(array('i', [0,1,2,3]), (2,2))
+>>> print A
+[ 0  2]
+[ 1  3]
+\end{verbatim}
+
+\item If \var{x} is a dense or sparse matrix, or a two-dimensional 
+\program{NumPy} array of type \itc, 
+\dtc\ or \ztc, then the  coefficients of \var{x} are copied, in 
+column-major order, to a new matrix of the given size.  
+The total number of elements in the new matrix (the product of 
+\code{size[0]} and \code{size[1]}) must be the same as the product of  
+the dimensions of \var{x}.  If \var{size} is not specified, the 
+dimensions of \var{x} are used.  
+The default value of \var{tc} is the type of \var{x}. 
+Type conversion takes place when the type of \var{x} differs from 
+\var{tc}, in a similar way as for scalar \var{x}.  
+
+\begin{verbatim}
+>>> A = matrix([1., 2., 3., 4., 5., 6.], (2,3))  
+>>> print A
+[ 1.00e+00  3.00e+00  5.00e+00]
+[ 2.00e+00  4.00e+00  6.00e+00]
+>>> B = matrix(A, (3,2))  
+>>> print B
+[ 1.00e+00  4.00e+00]
+[ 2.00e+00  5.00e+00]
+[ 3.00e+00  6.00e+00]
+>>> C = matrix(B, tc='z')      
+>>> print C
+[ 1.00e+00-j0.00e+00  4.00e+00-j0.00e+00]
+[ 2.00e+00-j0.00e+00  5.00e+00-j0.00e+00]
+[ 3.00e+00-j0.00e+00  6.00e+00-j0.00e+00]
+>>> from numpy import array
+>>> x = array([[1., 2., 3.], [4., 5., 6.]])
+>>> x
+array([[ 1.  2.  3.]
+       [ 4.  5.  6.]])
+>>> print matrix(x)
+[ 1.00e+00  2.00e+00  3.00e+00]
+[ 4.00e+00  5.00e+00  6.00e+00]
+\end{verbatim}
+
+\item If \var{x} is a list of lists of dense or sparse matrices 
+and numbers (Python \intgr, \flt\ or \cmplx), 
+then each element of \var{x} is interpreted as a 
+block-column stored in column-major order. 
+If \var{size} is not specified, the block-columns are juxtaposed
+to obtain a matrix with \code{len(\var{x})} block-columns.
+If \var{size} is specified, then the matrix with \code{len(\var{x})}
+block-columns is resized by copying its elements in column-major order 
+into a matrix of the dimensions given by \var{size}.  
+If \var{tc} is not specified, it is determined from the elements of 
+\var{x} (and if that is impossible, for example because \var{x} is
+a list of empty lists, a value \itc\ is used).  
+The same rules for type conversion apply as for scalar \var{x}.
+
+\begin{verbatim}
+>>> print matrix([[1., 2.], [3., 4.], [5., 6.]])
+[ 1.00e+00  3.00e+00  5.00e+00]
+[ 2.00e+00  4.00e+00  6.00e+00]
+>>> A1 = matrix([1, 2], (2,1))
+>>> B1 = matrix([6, 7, 8, 9, 10, 11], (2,3))
+>>> B2 = matrix([12, 13, 14, 15, 16, 17], (2,3))
+>>> B3 = matrix([18, 19, 20], (1,3))
+>>> C = matrix([[A1, 3.0, 4.0, 5.0], [B1, B2, B3]])
+>>> print C
+[ 1.00e+00  6.00e+00  8.00e+00  1.00e+01]
+[ 2.00e+00  7.00e+00  9.00e+00  1.10e+01]
+[ 3.00e+00  1.20e+01  1.40e+01  1.60e+01]
+[ 4.00e+00  1.30e+01  1.50e+01  1.70e+01]
+[ 5.00e+00  1.80e+01  1.90e+01  2.00e+01]
+\end{verbatim}
+
+A matrix with a single block-column can be represented by a single 
+list (\ie, if \var{x} is a list of lists, and has length one, 
+then the argument \code{\var{x}} can be replaced by \code{\var{x}[0]}).
+\begin{verbatim}
+>>> D = matrix([B1, B2, B3])
+>>> print D
+[  6   8  10]
+[  7   9  11]
+[ 12  14  16]
+[ 13  15  17]
+[ 18  19  20]
+\end{verbatim}
+\EIT
+\end{funcdesc}
+
+\section{Sparse Matrices} \label{s-creating-spmatrix}
+A general \spmtrx\ object can be thought of as a \emph{triplet 
+description} of a sparse matrix, \ie, a list of entries of the matrix, 
+with for each entry the value, row index, and column index.  
+Entries that are not included in the list are assumed to be zero.  
+For example, the sparse matrix
+\BEQ \label{e-sparse-A}
+ A = \left[ \begin{array}{rrrrr}
+  0 & 2 & 0 & 0 & 3 \\
+  2 & 0 & 0 & 0 & 0 \\
+ -1 & -2 & 0 & 4 & 0 \\
+  0 & 0 & 1 & 0 & 0 \end{array} \right]
+\EEQ
+has the triplet description 
+\[
+  (2,1,0), \qquad (-1,2,0), \qquad (2,0,1), \qquad (-2,2,1), \qquad
+  (1,3,2), \qquad (4,2,3), \qquad (3,0,4).
+\]
+The list may include entries with a zero value, so triplet
+descriptions are not necessarily unique.
+The list
+\[
+  (2,1,0), \qquad (-1,2,0), \qquad (0,3,0), \qquad (2,0,1), \qquad 
+ (-2,2,1), \qquad (1,3,2), \qquad (4,2,3), \qquad (3,0,4)
+\]
+is another triplet description of the same matrix.
+
+An \spmtrx\ object corresponds to a particular triplet 
+description of a sparse matrix.  We will refer to the entries in
+the triplet description as the \emph{nonzero entries} of the object, 
+even though they may have a numerical value zero.
+
+Three functions are provided to create sparse matrices. 
+The first, \function{spmatrix()}, constructs a sparse matrix from 
+a triplet description. 
+\begin{funcdesc}{spmatrix}{x, I, J\optional{, size\optional{, tc}}}
+
+\var{I} and \var{J} are sequences of integers (lists, tuples, 
+\module{array} arrays, xrange objects, \ldots) or integer matrices 
+(\mtrx\ objects with typecode \itc), containing the row and column 
+indices of the nonzero entries.  
+The lengths of \var{I} and \var{J} must be  equal.  If they 
+are matrices, they are treated as lists of indices stored in 
+column-major order, \ie, as lists \code{list(\var{I})}, respectively, 
+\code{list(\var{J})}. 
+
+\var{size} is a tuple of nonnegative integers with the row and column 
+dimensions of the matrix.
+The \var{size} argument is only needed when creating a matrix with 
+a zero last row or last column.  If \var{size} is not specified, it 
+is determined from \var{I} and \var{J}:
+the default value for \code{\var{size}[0]} is max(\var{I})+1 
+if \var{I} is nonempty and zero otherwise.  
+The default value for \code{\var{size}[1]} is 
+max(\var{J})+1 if \var{J} is nonempty and zero otherwise.
+
+\var{tc} is the typecode, \dtc\ or \ztc, for double and complex 
+matrices, respectively.   Integer sparse matrices are not implemented.
+
+\var{x} can be a number, a sequence of numbers, or a dense matrix.  
+This argument specifies the numerical values of the nonzero entries.
+\BIT
+\item If \var{x} is a number (Python \intgr, \flt\ or \cmplx), 
+a matrix is created with the sparsity pattern defined by \var{I} and 
+\var{J}, and nonzero entries initialized to the value of \var{x}.  
+The default value of \var{tc} is \dtc\ if \var{x} is \intgr\ or \flt,
+and \ztc\ if \var{x} is \cmplx.  
+
+The following code creates a 4 by 4 sparse identity matrix.
+\begin{verbatim}
+>>> from cvxopt import spmatrix
+>>> A = spmatrix(1.0, range(4), range(4))
+>>> print A  
+[ 1.00e+00     0         0         0    ]
+[    0      1.00e+00     0         0    ]
+[    0         0      1.00e+00     0    ]
+[    0         0         0      1.00e+00]
+\end{verbatim}
+
+\item If \var{x} is a sequence of numbers, a sparse matrix is created 
+with the entries of \var{x} copied to the entries indexed by \var{I} 
+and \var{J}.  The list \var{x} must have the same length as \var{I} and 
+\var{J}.
+The default value of \var{tc} is determined from the elements of 
+\var{x}: 
+\dtc\ if \var{x} contains integers and floating-point numbers or
+if \var{x} is an empty list,
+and \ztc\ if \var{x} contains at least one complex number.
+
+As an example, the matrix~(\ref{e-sparse-A}) can be created as follows.
+\begin{verbatim}
+>>> A = spmatrix([2,-1,2,-2,1,4,3], [1,2,0,2,3,2,0], [0,0,1,1,2,3,4])
+>>> print A 
+[    0      2.00e+00     0         0      3.00e+00]
+[ 2.00e+00     0         0         0         0    ]
+[-1.00e+00 -2.00e+00     0      4.00e+00     0    ]
+[    0         0      1.00e+00     0         0    ]
+\end{verbatim}
+
+\item If \var{x} is a dense matrix, a sparse matrix is created with 
+all the entries of \var{x} copied, in column-major order, to the 
+entries indexed by \var{I} and \var{J}.
+The matrix \var{x} must have the same length as \var{I} and \var{J}.
+The default value of \var{tc} is \dtc\ if \var{x} is an \itc\ or \dtc\
+matrix, and \ztc\ otherwise.
+\EIT
+
+If \var{I} and \var{J} contain repeated entries, the corresponding 
+values of the coefficients are added.
+\end{funcdesc}
+
+The function \function{sparse()} constructs a sparse matrix from
+a block-matrix description.
+   
+\begin{funcdesc}{sparse}{x\optional{, tc}}
+\var{tc} is the typecode, \dtc\ or \ztc, for double and complex 
+matrices, respectively.
+  
+\var{x} can be a \mtrx, \spmtrx, or a list of lists of matrices 
+(\mtrx\ or \spmtrx\ objects) and numbers (Python \intgr, \flt\ or 
+\cmplx). 
+\BIT
+\item If \var{x} is a \mtrx\ or \spmtrx\ object, then a sparse matrix 
+of the same size and the same numerical value is created. 
+Numerical zeros in \var{x} are treated as structural zeros and removed 
+from the triplet description of the new sparse matrix.
+
+\item If \var{x} is a list of lists of matrices (\mtrx\ or \spmtrx) 
+and numbers (Python \intgr, \flt\ or \cmplx) then each element of 
+\var{x} is interpreted as a (block-)column matrix stored in 
+colum-major order, and a block-matrix is constructed by juxtaposing
+the \code{len(\var{x})} block-columns
+(as in \function{matrix()}, see section~\ref{s-creating-matrices}). 
+Numerical zeros are removed from the triplet description of the new 
+matrix.  
+\EIT
+\begin{verbatim}
+>>> from cvxopt import matrix, spmatrix, sparse
+>>> A = matrix([[1., 2., 0.], [2., 1., 2.], [0., 2., 1.]])
+>>> print A
+[ 1.00e+00  2.00e+00  0.00e+00]
+[ 2.00e+00  1.00e+00  2.00e+00]
+[ 0.00e+00  2.00e+00  1.00e+00]
+>>> B = spmatrix([], [], [], (3,3))
+>>> print B
+[0 0 0]
+[0 0 0]
+[0 0 0]
+>>> C = spmatrix([3, 4, 5], [0, 1, 2], [0, 1, 2])
+>>> print C
+[ 3.00e+00     0         0    ]
+[    0      4.00e+00     0    ]
+[    0         0      5.00e+00]
+>>> D = sparse([[A, B], [B, C]])
+>>> print D
+[ 1.00e+00  2.00e+00     0         0         0         0    ]
+[ 2.00e+00  1.00e+00  2.00e+00     0         0         0    ]
+[    0      2.00e+00  1.00e+00     0         0         0    ]
+[    0         0         0      3.00e+00     0         0    ]
+[    0         0         0         0      4.00e+00     0    ]
+[    0         0         0         0         0      5.00e+00]
+\end{verbatim}
+A matrix with a single block-column can be represented by a single list.
+\begin{verbatim}
+>>> D = sparse([A, C])
+>>> print D
+[ 1.00e+00  2.00e+00     0    ]
+[ 2.00e+00  1.00e+00  2.00e+00]
+[    0      2.00e+00  1.00e+00]
+[ 3.00e+00     0         0    ]
+[    0      4.00e+00     0    ]
+[    0         0      5.00e+00]
+\end{verbatim}
+\end{funcdesc}
+
+The function \function{spdiag()} constructs a block-diagonal
+sparse matrix from a list of matrices.
+\begin{funcdesc}{spdiag}{x}
+\var{x} is a dense or sparse matrix with a single row or column, or a 
+list of square dense or sparse matrices or scalars.  
+If \var{x} is matrix, a sparse diagonal matrix is returned with 
+the entries of \var{x} on its diagonal.
+If \var{x} is list, a sparse block-diagonal matrix is returned with
+the elements in the list as its diagonal blocks.
+  
+\begin{verbatim}
+>>> from cvxopt import matrix, spmatrix, spdiag
+>>> A = 3.0
+>>> B = matrix([[1,-2],[-2,1]])
+>>> C = spmatrix([1,1,1,1,1],[0,1,2,0,0,],[0,0,0,1,2])
+>>> D = spdiag([A, B, C])
+>>> print D
+[ 3.00e+00     0         0         0         0         0    ]
+[    0      1.00e+00 -2.00e+00     0         0         0    ]
+[    0     -2.00e+00  1.00e+00     0         0         0    ]
+[    0         0         0      1.00e+00  1.00e+00  1.00e+00]
+[    0         0         0      1.00e+00     0         0    ]
+[    0         0         0      1.00e+00     0         0    ]
+\end{verbatim}
+\end{funcdesc}
+
+
+\section{Arithmetic Operations} 
+\label{s-arithmetic} \label{s-spmatrix-arith}
+The following table lists the arithmetic operations defined for 
+dense and sparse matrices.  
+In the table \var{A} and \var{B} are dense or sparse matrices 
+of compatible dimensions, 
+\var{c} is a scalar (a Python number or a dense 1 by 1 matrix), 
+\var{D} is a dense matrix, and \var{e} is a Python number.
+
+\begin{center}
+\begin{tabular}{l|l}
+\hline
+Unary plus/minus & \code{+\var{A}}, \code{-\var{A}} \\ \hline 
+Addition & \code{\var{A} + \var{B}}, \code{\var{A} + \var{c}}, 
+    \code{\var{c} + \var{A}}\\ \hline 
+Subtraction & \code{\var{A} - \var{B}}, \code{\var{A} - \var{c}}, 
+    \code{\var{c} - \var{A}}\\ \hline 
+Matrix multiplication & \code{{\var{A} * \var{B}}} \\ \hline
+Scalar multiplication and division & \code{\var{c} * \var{A}}, 
+    \code{\var{A} * \var{c}}, \code{\var{A} / \var{c}} \\ \hline 
+Remainder after division & \code{\var{D} \% \var{c}} \\ \hline 
+Elementwise exponentiation  & \code{\var{D}**\var{e}} \\ \hline 
+\end{tabular}
+\end{center}
+
+If one of the operands is integer (a scalar \intgr\ or a matrix of 
+type \itc) and the other operand is double (a scalar \flt\ or a matrix 
+of type \dtc), then the integer operand is converted to double, and the 
+result is a matrix of type \dtc.
+If one of the operands is integer or double, and the other operand is 
+complex (a scalar \cmplx\ or a matrix of type \ztc), 
+then the first operand is converted to complex, and the result is 
+a matrix of type \ztc.  
+(An exception to this rule is elementwise exponentiation:
+the result of \code{\var{D}**\var{e}} is a real matrix if \var{D} 
+and \var{e} are integer.)
+
+Addition, subtraction, and matrix multiplication with two matrix operands 
+result in a sparse matrix if both matrices are sparse, and in a dense 
+matrix otherwise.
+The result of a scalar multiplication or division is dense 
+if \var{A} is dense, and sparse if \var{A} is sparse.
+Postmultiplying a matrix with a number \var{c} means the same as 
+premultiplying, \ie, scalar multiplication.  Dividing a matrix by 
+\var{c} means dividing all entries by \var{c}.  
+
+If \var{c} in the expressions \code{\var{A} + \var{c}}, 
+\code{\var{c} + \var{A}}, \code{\var{A} - \var{c}}, 
+\code{\var{c} - \var{A}} is a number, then it is interpreted as a 
+dense matrix with the same dimensions as \var{A}, type given by the type 
+of \var{c}, and all entries equal to \var{c}.
+If \var{c}  is a 1 by 1 dense matrix and \var{A} is not 1 by 1, 
+then \var{c} is interpreted as a dense matrix with the same size of 
+\var{A} and all entries equal to \code{\var{c}[0]}.
+
+If \var{c} is a 1 by 1 dense matrix, then, if possible, the products 
+\code{\var{c} * \var{A}} and \code{\var{A} * \var{c}} are interpreted as 
+matrix-matrix products.
+If the product cannot be interpreted as a matrix-matrix product
+(because the dimensions of \var{A} are incompatible), then the product is 
+interpreted as the scalar multiplication with \code{\var{c}[0]}.
+The division \code{\var{A}/\var{c}} and remainder 
+\code{\var{A} \% \var{c}} with \var{c} a 1 by 1 matrix are always 
+interpreted as \code{\var{A} / \var{c}[0]}, resp.,
+\code{\var{A} \% \var{c}[0]}.
+
+Note that Python rounds the result of an integer division towards minus 
+infinity.
+
+The following in-place operations are also defined, but only if 
+they do not change the type (sparse or dense, integer, real, or complex) of the 
+matrix \var{A}.  
+These in-place operations do not return a new matrix but modify the 
+existing object \var{A}.
+\begin{center}\begin{tabular}{l|l} \hline
+ In-place addition & 
+    \code{\var{A} += \var{B}}, \code{\var{A} += \var{c}} \\ \hline
+ In-place subtraction & 
+    \code{\var{A} -= \var{B}}, \code{\var{A} -= \var{c}} \\ \hline   
+ In-place scalar multiplication and division & 
+    \code{\var{A} *= \var{c}}, \code{\var{A} /= \var{c}} \\ \hline
+ In-place remainder & \code{\var{A} \%= \var{c}} \\ \hline
+\end{tabular}\end{center}
+For example, if \var{A} has typecode \itc, then \code{\var{A} += \var{B}} 
+is allowed if \var{B} has typecode \itc.
+It is not allowed if \var{B} has typecode \dtc\ or \ztc\ because the 
+addition \code{\var{A} + \var{B}} results in a \dtc\ or \ztc\ matrix of 
+and therefore cannot be assigned to \var{A} without changing its type.
+As another example, if \var{A} is a sparse matrix, then
+\samp{\var{A} += 1.0} is not allowed because the 
+operation \samp{\var{A} = \var{A} + 1.0} results in a dense matrix, 
+so it cannot be assigned to \var{A} without changing its type.
+
+In-place matrix-matrix products are not allowed.  (Except when \var{c} is 
+a 1 by 1 dense matrix, in which case \code{\var{A} *= \var{c}} is 
+interpreted as the scalar product \code{\var{A} *= \var{c}[0]}.)
+
+In-place remainder is only defined for dense \var{A}.
+
+It is important to know when a matrix operation creates
+a new object.  The following rules apply.
+\begin{itemize}
+\item A simple assignment (\samp{A = B}) is given the standard 
+Python interpretation, \ie, it assigns to the variable \var{A} a 
+reference (or pointer) to the object referenced by \var{B}.
+\begin{verbatim}
+>>> B = matrix([[1.,2.], [3.,4.]])  
+>>> print B
+[ 1.00e+00  3.00e+00]
+[ 2.00e+00  4.00e+00]
+>>> A = B
+>>> A[0,0] = -1 
+>>> print B   # modifying A[0,0] also modified B[0,0]
+[-1.00e+00  3.00e+00]
+[ 2.00e+00  4.00e+00]
+\end{verbatim}
+
+\item The regular (\ie, not in-place) arithmetic operations always 
+return new objects.   
+\begin{verbatim}
+>>> B = matrix([[1.,2.], [3.,4.]])  
+>>> A = +B
+>>> A[0,0] = -1 
+>>> print B   # modifying A[0,0] does not modify B[0,0]
+[ 1.00e+00  3.00e+00]
+[ 2.00e+00  4.00e+00]
+\end{verbatim}
+
+\item  The in-place operations directly modify the coefficients of the 
+ existing matrix object and do not create a new object. 
+\begin{verbatim}
+>>> B = matrix([[1.,2.], [3.,4.]])  
+>>> A = B
+>>> A *= 2
+>>> print B   # in-place operation also changed B
+[ 2.00e+00  6.00e+00]
+[ 4.00e+00  8.00e+00]
+>>> A = 2*A
+>>> print B   # regular operation creates a new A, so does not change B
+[ 2.00e+00  6.00e+00]
+[ 4.00e+00  8.00e+00]
+\end{verbatim}
+\end{itemize}
+
+
+%\begin{verbatim}
+%>>> B.V += 1.0   # add 1 to the nonzero entries
+%>>> print B
+%[ 2.00e+00  8.00e+00     0         0    ]
+%[    0      9.00e+00  7.00e+00     0    ]
+%[    0         0      5.00e+00     0    ]
+%[    0         0         0         0    ]
+%\end{verbatim}
+
+
+\section{Indexing and Slicing} \label{s-indexing}
+Matrices can be indexed using one or two arguments.  In single-argument
+indexing of a matrix \var{A}, the index runs from 
+\code{-len(\var{A})} to \code{len(\var{A})-1}, and is interpreted as an
+index in the one-dimensional array of coefficients of \var{A} 
+in column-major order.   Negative indices have the standard Python 
+interpretation: for negative \var{k}, \code{\var{A}[\var{k}]} is the 
+same element as  \code{\var{A}[len(\var{A})+\var{k}]}.
+
+Four different types of one-argument indexing are implemented.
+\begin{enumerate}
+\item The index can be a single integer.  This returns a 
+number, \eg, \code{\var{A}[0]} is the first element of \var{A}.
+
+\item The index can be an integer matrix.  This returns a 
+column matrix: the command \samp{A[matrix([0,1,2,3])]} 
+returns the 4 by 1 matrix consisting of the first four elements of
+\var{A}.   The size of the index matrix is ignored: 
+\samp{A[matrix([0,1,2,3], (2,2))]} returns the same 4 by 1 matrix.
+
+\item The index can be a list of integers.  This returns a column 
+matrix, \eg, \code{\var{A}[[0,1,2,3]]} is the 4 by 1 matrix consisting 
+of elements 0, 1, 2, 3 of \var{A}.   
+
+\item The index can be a Python slice.  This returns a matrix with one 
+column (possibly 0 by 1, or 1 by 1).  For example, \code{\var{A}[::2]} 
+is the column matrix defined by taking every other element of \var{A}, 
+stored in column-major order.  
+\code{\var{A}[0:0]} is a matrix with size (0,1).
+\end{enumerate}
+Thus, single-argument indexing returns a scalar (if the index is an 
+integer), or a matrix with one column.  This is consistent with the 
+interpretation that single-argument indexing accesses the matrix in 
+column-major order.
+
+Note that an index list or an index matrix are equivalent,
+but they are both useful, especially when we perform operations on 
+index sets.  For example, if \var{I} and \var{J} are lists then 
+\code{\var{I}+\var{J}} is the concatenated list, and \code{2*\var{I}} 
+is \var{I} repeated twice.  If they are matrices, these operations are 
+interpreted as arithmetic operations.
+For large index sets, indexing with integer matrices is also faster 
+than indexing with lists. 
+
+The following example illustrates one-argument indexing.
+\begin{verbatim}
+>>> from cvxopt import matrix, spmatrix
+>>> A = matrix(range(16), (4,4), 'd')
+>>> print A
+[ 0.00e+00  4.00e+00  8.00e+00  1.20e+01]
+[ 1.00e+00  5.00e+00  9.00e+00  1.30e+01]
+[ 2.00e+00  6.00e+00  1.00e+01  1.40e+01]
+[ 3.00e+00  7.00e+00  1.10e+01  1.50e+01]
+>>> A[4]
+4.0
+>>> I = matrix([0, 5, 10, 15])
+>>> print A[I]      # the diagonal
+[ 0.00e+00]
+[ 5.00e+00]
+[ 1.00e+01]
+[ 1.50e+01]
+>>> I = [0,2];  J = [1,3]
+>>> print A[2*I+J]  # duplicate I and append J
+[ 0.00e+00]
+[ 2.00e+00]
+[ 0.00e+00]
+[ 2.00e+00]
+[ 1.00e+00]
+[ 3.00e+00]
+>>> I = matrix([0, 2]);  J =  matrix([1, 3])
+>>> print A[2*I+J]  # multiply I by 2 and add J
+[ 1.00e+00]
+[ 7.00e+00]
+>>> print A[4::4]   # get every fourth element skipping the first four  
+[ 4.00e+00]
+[ 8.00e+00]
+[ 1.20e+01]
+\end{verbatim}
+
+In two-argument indexing the arguments can be any combinations of the
+four types listed above.  The first argument indexes the rows of 
+the matrix and the second argument indexes the columns.  If both 
+indices are scalars, then a scalar is returned.  In all other cases, 
+a matrix is returned.  We continue the example.
+\begin{verbatim}
+>>> print A[:,1]
+[ 4.00e+00]
+[ 5.00e+00]
+[ 6.00e+00]
+[ 7.00e+00]
+>>> J = matrix([0, 2])
+>>> print A[J,J]
+[ 0.00e+00  8.00e+00]
+[ 2.00e+00  1.00e+01]
+>>> print A[:2, -2:]   
+[ 8.00e+00  1.20e+01]
+[ 9.00e+00  1.30e+01]
+>>> A = spmatrix([0,2,-1,2,-2,1], [0,1,2,0,2,1], [0,0,0,1,1,2]) 
+>>> print A[:, [0,1]]
+[ 0.00e+00  2.00e+00]
+[ 2.00e+00     0    ]
+[-1.00e+00 -2.00e+00]
+>>> B = spmatrix([0,2*1j,0,-2], [1,2,1,2], [0,0,1,1,])
+>>> print B[-2:,-2:]
+[ 0.00e+00-j0.00e+00  0.00e+00-j0.00e+00]
+[ 0.00e+00+j2.00e+00 -2.00e+00-j0.00e+00]
+\end{verbatim}
+
+Expressions of the form \code{\var{A}[\var{I}]} or 
+\code{\var{A}[\var{I},\var{J}]} can also appear on the lefthand side 
+of an assignment.   
+The righthand side must be a scalar (\ie, a number or a 1 by 1 dense
+matrix), a sequence of numbers, or a dense or sparse matrix. 
+If the righthand side is a scalar, it is interpreted as a dense
+matrix with identical entries and the dimensions of the lefthand side.
+If the righthand side is a sequence of numbers (list, tuple, 
+\module{array} array, xrange object, \ldots) its values are 
+interpreted as the coefficients of a dense matrix in column-major order.  
+If the righthand side is a matrix (\mtrx\ or \spmtrx), it must 
+have the same size as the lefthand side.  Sparse matrices are 
+converted to dense in the assignment to a dense matrix.
+
+Indexed assignments are only allowed if they do not change the type of 
+the matrix.  For example, if \var{A} is a matrix with type \dtc, then 
+\code{\var{A}[\var{I}] = \var{B}} is only permitted if \var{B} is 
+an \intgr, a \flt, or a matrix of type \itc\ or \dtc.
+If \var{A} is an integer matrix, then \code{\var{A}[\var{I}] = \var{B}} 
+is only permitted if \var{B} is an \intgr\  or an integer matrix.
+
+The following examples illustrate indexed assignment.
+\begin{verbatim}
+>>> A = matrix(range(16), (4,4))
+>>> A[::2,::2] = matrix([[-1, -2], [-3, -4]])
+>>> print A
+[ -1   4  -3  12]
+[  1   5   9  13]
+[ -2   6  -4  14]
+[  3   7  11  15]
+>>> A[::5] += 1
+>>> print A
+[  0   4  -3  12]
+[  1   6   9  13]
+[ -2   6  -3  14]
+[  3   7  11  16]
+>>> A[0,:] = -1, 1, -1, 1
+>>> print A
+[ -1   1  -1   1]
+[  1   6   9  13]
+[ -2   6  -3  14]
+[  3   7  11  16]
+>>> A[2:,2:] = xrange(4)
+>>> print A
+[ -1   1  -1   1]
+[  1   6   9  13]
+[ -2   6   0   2]
+[  3   7   1   3]
+\end{verbatim}
+
+\begin{verbatim}
+>>> A = spmatrix([0,2,-1,2,-2,1], [0,1,2,0,2,1], [0,0,0,1,1,2]) 
+>>> print A
+[ 0.00e+00  2.00e+00     0    ]
+[ 2.00e+00     0      1.00e+00]
+[-1.00e+00 -2.00e+00     0    ]
+>>> C = spmatrix([10,-20,30], [0,2,1], [0,0,1])
+>>> print C
+[ 1.00e+01     0    ]
+[    0      3.00e+01]
+[-2.00e+01     0    ]
+>>> A[:,0] = C[:,0]
+>>> print A
+[ 1.00e+01  2.00e+00     0    ]
+[    0         0      1.00e+00]
+[-2.00e+01 -2.00e+00     0    ]
+>>> D = matrix(range(6), (3,2))
+>>> A[:,0] = D[:,0]
+>>> print A
+[ 0.00e+00  2.00e+00     0    ]
+[ 1.00e+00     0      1.00e+00]
+[ 2.00e+00 -2.00e+00     0    ]
+>>> A[:,0] = 1
+>>> print A
+[ 1.00e+00  2.00e+00     0    ]
+[ 1.00e+00     0      1.00e+00]
+[ 1.00e+00 -2.00e+00     0    ]
+>>> A[:,0] = 0
+>>> print A
+[ 0.00e+00  2.00e+00     0    ]
+[ 0.00e+00     0      1.00e+00]
+[ 0.00e+00 -2.00e+00     0    ]
+\end{verbatim}
+
+
+\section{Attributes and Methods} 
+Dense and sparse matrices have the following attributes.
+
+\begin{memberdesc}{size}
+A tuple with the dimensions of the matrix. The size of the matrix
+can be changed by altering this attribute, as long as the number
+of elements in the matrix remains unchanged.  
+\end{memberdesc} 
+
+\begin{memberdesc}{typecode}
+A \ctype{char}, either \itc, \dtc, or \ztc, for integer, real,
+and complex matrices, respectively.  A read-only attribute.
+\end{memberdesc} 
+
+\begin{methoddesc}{trans}
+Returns the transpose of the matrix as a new matrix.
+One can also use \code{A.T} instead of \code{A.trans()}.
+\end{methoddesc}
+
+\begin{methoddesc}{ctrans}
+Returns the conjugate transpose of the matrix as a new matrix.
+One can also use \code{A.H} instead of \code{A.ctrans()}.
+\end{methoddesc}
+
+\begin{methoddesc}{real}
+For complex matrices, returns the real part as a real matrix.
+For integer and real matrices, returns a copy of the matrix.
+\end{methoddesc}
+
+\begin{methoddesc}{imag}
+For complex matrices, returns the imaginary part as a real matrix.
+For integer and real matrices, returns an integer or real zero matrix.
+\end{methoddesc}
+
+In addition, sparse matrices have the following attributes.
+\begin{memberdesc}{V}
+A single-column dense matrix containing the numerical values of the 
+nonzero entries in column-major order.  Making an assignment to 
+the attribute is an efficient way of changing the values of the sparse 
+matrix, without changing the sparsity pattern.
+
+When the attribute \member{V} is read, a \emph{copy} of \member{V} is 
+returned, as a new dense matrix. 
+This implies, for example, that an indexed assignment 
+\samp{A.V[I] = B} does not work, or at least 
+cannot be used to modify \var{A}.  Instead the attribute \code{V}\ 
+will be read and returned as a new matrix; then the elements of this 
+new matrix are modified.
+\end{memberdesc} 
+
+\begin{memberdesc}{I}
+A single-column integer dense matrix with the row indices of the entries in
+\code{V}.  A read-only attribute.
+\end{memberdesc} 
+
+\begin{memberdesc}{J}
+A single-column integer dense matrix with the column indices of the entries
+in \code{V}.  A read-only attribute.
+\end{memberdesc} 
+
+\begin{memberdesc}{CCS}
+A triplet (column pointers, row indices, values) with the 
+compressed-column-storage 
+representation of the matrix.  A read-only attribute.  This attribute
+can be used to export sparse matrices to other packages such as MOSEK.
+\end{memberdesc} 
+
+The next example below illustrates assignments to \member{V}.
+\begin{verbatim}
+>>> from cvxopt import spmatrix, matrix
+>>> A = spmatrix(range(5), [0,1,1,2,2], [0,0,1,1,2])
+>>> print A
+[ 0.00e+00     0         0    ]
+[ 1.00e+00  2.00e+00     0    ]
+[    0      3.00e+00  4.00e+00]
+>>> B = spmatrix(A.V, A.J, A.I, (4,4))  # transpose and add a zero row and column
+>>> print B
+[ 0.00e+00  1.00e+00     0         0    ]
+[    0      2.00e+00  3.00e+00     0    ]
+[    0         0      4.00e+00     0    ]
+[    0         0         0         0    ]
+>>> B.V = matrix([1., 7., 8., 6., 4.])   # assign new values to nonzero entries
+>>> print B
+[ 1.00e+00  7.00e+00     0         0    ]
+[    0      8.00e+00  6.00e+00     0    ]
+[    0         0      4.00e+00     0    ]
+[    0         0         0         0    ]
+\end{verbatim}
+
+The following attributes and methods are defined for dense matrices.
+\begin{memberdesc}{\_\_array\_struct\_\_}
+A PyCObject implementing the \program{NumPy} array interface  
+(see section~\ref{s-array-interface} for details).
+\end{memberdesc} 
+
+\begin{funcdesc}{tofile}{f}
+Writes the elements of the matrix in column-major order to a binary 
+file \var{f}. 
+\end{funcdesc}
+
+\begin{funcdesc}{fromfile}{f}
+Reads the contents of a binary file \var{f} into the matrix object.
+\end{funcdesc}
+
+The last two methods are illustrated in the following examples.
+\begin{verbatim}
+>>> from cvxopt import matrix, spmatrix
+>>> A = matrix([[1.,2.,3.], [4.,5.,6.]])  
+>>> print A
+[ 1.00e+00  4.00e+00]
+[ 2.00e+00  5.00e+00]
+[ 3.00e+00  6.00e+00]
+>>> f = open('mat.bin','w')
+>>> A.tofile(f)
+>>> f.close()
+>>> B = matrix(0.0, (2,3))
+>>> f = open('mat.bin','r')
+>>> B.fromfile(f)
+>>> f.close()
+>>> print B
+[ 1.00e+00  3.00e+00  5.00e+00]
+[ 2.00e+00  4.00e+00  6.00e+00]
+\end{verbatim}
+
+\begin{verbatim}
+>>> A = spmatrix(range(5), [0,1,1,2,2], [0,0,1,1,2])
+>>> f = open('test.bin','w')
+>>> A.V.tofile(f)  
+>>> A.I.tofile(f) 
+>>> A.J.tofile(f)
+>>> f.close()
+>>> f = open('test.bin','r')
+>>> V = matrix(0.0, (5,1));  V.fromfile(f)  
+>>> I = matrix(0, (5,1));  I.fromfile(f)  
+>>> J = matrix(0, (5,1));  J.fromfile(f)  
+>>> B = spmatrix(V, I, J)
+>>> print B
+[ 0.00e+00     0         0    ]
+[ 1.00e+00  2.00e+00     0    ]
+[    0      3.00e+00  4.00e+00]
+\end{verbatim}
+Note that the \function{dump()} and \function{load()} functions in 
+the \module{pickle} module offer a convenient alternative for writing
+matrices to files and reading matrices from files.
+
+
+\section{Built-In Functions} \label{s-builtinfuncs}
+Many Python built-in functions and operations can be used with matrix 
+arguments.  We list some useful examples.
+
+\begin{funcdesc}{len}{x}
+If \var{x} is a dense matrix, returns the product of the number of rows 
+and the number of columns.
+If \var{x} is a sparse matrix, returns the number of nonzero entries.
+\end{funcdesc}
+
+\begin{funcdesc}{bool}{\optional{x}}
+Returns \False\ if \var{x} is a zero matrix and \True\ otherwise.
+\end{funcdesc}
+
+\begin{funcdesc}{max}{x}
+If \var{x} is a dense matrix, returns the maximum element of \var{x}.
+If \var{x} is a sparse, returns the maximum nonzero element of \var{x}.
+\end{funcdesc}
+
+\begin{funcdesc}{min}{x}
+If \var{x} is a dense matrix, returns the minimum element of \var{x}.
+If \var{x} is a sparse matrix, returns the minimum nonzero element of 
+\var{x}.
+\end{funcdesc}
+
+\begin{funcdesc}{abs}{x}
+Returns a matrix with the absolute values of the elements of \var{x}.
+\end{funcdesc}
+
+\begin{funcdesc}{sum}{x\optional{, start=0.0}}
+Returns the sum of \var{start} and the elements of \var{x}.
+\end{funcdesc}
+
+Dense and sparse matrices can be used as  arguments to the 
+\function{list()}, \function{tuple()}, \function{zip()}, 
+\function{map()}, and \function{filter()} functions described in 
+section 2.1 of the Python Library Reference.  
+However, one should note that when used with sparse matrix arguments, 
+these functions only consider the nonzero entries.
+For example, \code{list(\var{A})} and \code{tuple(\var{A})} 
+construct a list, respectively a tuple, from the elements of \var{A} 
+if \var{A} is dense, and of the nonzero elements of \var{A} if \var{A} is 
+sparse.
+
+\code{zip(\var{A}, \var{B}, \ldots)} returns a list of tuples, 
+with the {\it i}th tuple containing the {\it i}th elements 
+(or nonzero elements) of \var{A}, \var{B}, \ldots.   
+\begin{verbatim}
+>>> from cvxopt import matrix
+>>> A = matrix([[-11., -5., -20.], [-6., -0., 7.]])
+>>> B = matrix(range(6), (3,2))
+>>> list(A)
+[-11.0, -5.0, -20.0, -6.0, 0.0, 7.0]
+>>> tuple(B)
+(0, 1, 2, 3, 4, 5)
+>>> zip(A, B)
+[(-11.0, 0), (-5.0, 1), (-20.0, 2), (-6.0, 3), (0.0, 4), (7.0, 5)]
+\end{verbatim}
+
+\code{map(\var{f},\var{A})}, where \var{f} is a function and \var{A} 
+is a dense matrix, returns a list constructed by applying \var{f} to each 
+element of \var{A}.  If \var{A} is sparse, the function \var{f} is applied to 
+each nonzero element of \var{A}.
+Multiple arguments can be provided, for example, 
+as in \code{map(\var{f},\var{A},\var{B})}, if \var{f} is a function 
+with two arguments.
+In the following example, we return an integer 0-1 matrix with the
+result of an elementwise comparison.
+\begin{verbatim}
+>>> A = matrix([ [0.5, -0.1, 2.0], [1.5, 0.2, -0.1], [0.3, 1.0, 0.0]]) 
+>>> print A
+[ 5.00e-01  1.50e+00  3.00e-01]
+[-1.00e-01  2.00e-01  1.00e+00]
+[ 2.00e+00 -1.00e-01  0.00e+00]
+>>> print matrix(map(lambda x: 0 <= x <= 1, A), A.size)
+[ 1  0  1]
+[ 0  1  1]
+[ 0  0  1]
+\end{verbatim}
+
+\code{filter(\var{f},\var{A})}, where \var{f} is a function and 
+\var{A} is a matrix, returns a list containing the elements of \var{A} 
+(or nonzero elements of \var{A} is \var{A} is sparse)
+for which \var{f} is true.
+\begin{verbatim}
+>>> filter(lambda x: x%2, A)         # list of odd elements in A
+[5, -7, -1, -5, 1, 5, -1, -3, -7]
+>>> filter(lambda x: -2 < x < 3, A)  # list of elements between -2 and 3
+[-1, 2, 1, 2, -1, 2]
+\end{verbatim}
+
+It is also possible to iterate over matrix elements, as illustrated in
+the following example.
+\begin{verbatim}
+>>> A = matrix([[5, -3], [9, 11]])
+>>> for x in A: print max(x,0)
+...
+5
+0
+9
+11
+>>> [max(x,0) for x in A]
+[5, 0, 9, 11]
+\end{verbatim}
+
+The expression \samp{\var{x} in \var{A}} returns \True\ if an element 
+of \var{A} (or a nonzero element of \var{A} if \var{A} is sparse)
+is equal to \var{x} and \False\ otherwise.
+
+\section{Other Matrix Functions} \label{s-otherfuncs}
+The following functions can be imported from \module{cvxopt}.
+\begin{funcdesc}{sqrt}{x}
+The elementwise square root of a dense matrix \var{x}.  The result is 
+returned as a real matrix if \var{x} is an integer or real matrix and 
+as a complex matrix if \var{x} is a complex matrix.  Raises an 
+exception when \var{x} is an integer or real matrix with negative 
+elements.
+
+As an example we take the elementwise square root of the sparse matrix
+\BEQ \label{e-spA-example}
+ A = \left[ \begin{array}{rrrrr}
+  0 & 2 & 0 & 0 & 3 \\
+  2 & 0 & 0 & 0 & 0 \\
+  1 & 2 & 0 & 4 & 0 \\
+  0 & 0 & 1 & 0 & 0 \end{array} \right]
+\EEQ
+\begin{verbatim}
+>>> from cvxopt import spmatrix, sqrt
+>>> A = spmatrix([2,1,2,2,1,3,4], [1,2,0,2,3,0,2], [0,0,1,1,2,3,3]) 
+>>> B = spmatrix(sqrt(A.V), A.I, A.J)
+>>> print B
+[    0      1.41e+00     0      1.73e+00]
+[ 1.41e+00     0         0         0    ]
+[ 1.00e+00  1.41e+00     0      2.00e+00]
+[    0         0      1.00e+00     0    ]
+\end{verbatim}
+\end{funcdesc}
+
+\begin{funcdesc}{sin}{x}
+The sine function applied elementwise to a dense matrix \var{x}.  
+The result is returned as a real matrix if \var{x} is an integer
+or real matrix and as a complex matrix otherwise.  
+\end{funcdesc}
+
+\begin{funcdesc}{cos}{x}
+The cosine function applied elementwise to a dense matrix \var{x}.  
+The result is returned as a real matrix if \var{x} is an integer
+or real matrix and as a complex matrix otherwise.  
+\end{funcdesc}
+
+\begin{funcdesc}{exp}{x}
+The exponential function applied elementwise to a dense matrix \var{x}.  
+The result is returned as a real matrix if \var{x} is an integer 
+or real matrix and as a complex matrix otherwise.  
+\end{funcdesc}
+
+\begin{funcdesc}{log}{x}
+The natural logarithm applied elementwise to a dense matrix \var{x}.  
+The result is returned as a real matrix if \var{x} is an integer
+or real matrix and as a complex matrix otherwise.  
+Raises an exception when \var{x} is an integer or real matrix with 
+nonnegative elements, or a complex matrix with zero elements.
+\end{funcdesc}
+
+\begin{funcdesc}{mul}{x0, \optional{, x1 \optional{, x2 \ldots}}}
+If the arguments are dense or sparse matrices of the same size,
+returns the elementwise product of its arguments.  The result is a sparse
+matrix if one or more of its arguments is sparse, and a dense matrix 
+otherwise.
+
+If the arguments include scalars, a scalar product with the scalar is made.
+(A 1 by 1 dense matrix is treated as a scalar if the dimensions of the 
+other arguments are not all 1 by 1.)
+
+\function{mul()} can also be called with an iterable (list, tuple, xrange 
+object, or generator) as its single argument, if the iterable generates 
+a list of dense or sparse matrices or scalars.
+
+\begin{verbatim}
+>>> from cvxopt import matrix, spmatrix, mul
+>>> A = matrix([[1.0, 2.0], [3.0, 4.0]])
+>>> B = spmatrix([2.0, 3.0], [0, 1], [0, 1])
+>>> print mul(A, B, -1.0)
+[-2.00e+00     0    ]
+[    0     -1.20e+01]
+\end{verbatim}
+
+\begin{verbatim}
+>>> print mul( matrix([k, k+1]) for k in [1,2,3] )
+[  6]
+[ 24]
+\end{verbatim}
+\end{funcdesc}
+
+\begin{funcdesc}{div}{x, y}
+Returns the elementwise division of \var{x} by \var{y}.  
+\var{x} is a dense or sparse matrix, or a scalar
+(Python number of 1 by 1 dense matrix).
+\var{y} is a dense matrix or a scalar.
+\end{funcdesc}
+
+\begin{funcdesc}{max}{x0\optional{, x1\optional{, x2 \ldots}}}
+When called with a single matrix argument, returns the maximum of the 
+elements of the matrix (including the zero entries, if the matrix is 
+sparse).  
+
+When called with multiple arguments, the arguments must be matrices of 
+the same size, or scalars, and the elementwise maximum is returned.   
+A 1 by 1 dense matrix is
+treated as a scalar if the other arguments are not all 1 by 1.
+If one of the arguments is scalar, and the other arguments are not all 1 
+by 1, then the scalar argument is interpreted as a dense matrix with all 
+its entries equal to the scalar.
+
+The result is a sparse matrix if all its arguments are sparse matrices.  
+The result is a number if all its arguments are numbers.
+The result is a dense matrix if at least one of the arguments is a dense 
+matrix.
+
+\function{max()} can also be called with an iterable (list, tuple, xrange 
+object, or generator) as its single argument, if the iterable generates a 
+list of dense or sparse matrices or scalars.
+\begin{verbatim}
+>>> from cvxopt import matrix, spmatrix, max
+>>> A = spmatrix([2, -3], [0, 1], [0, 1])
+>>> print max(A, -A, 1)
+[ 2.00e+00  1.00e+00]
+[ 1.00e+00  3.00e+00]
+\end{verbatim}
+
+It is important to note the difference between this \function{max()}
+and the built-in \function{max()}, explained in the previous section.
+\begin{verbatim}
+>>> from cvxopt import spmatrix
+>>> A = spmatrix([-1.0, -2.0], [0,1], [0,1])
+>>> max(A)          # built-in max of a sparse matrix takes maximum over nonzero elements
+-1.0
+>>> max(A, -1.5)
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+NotImplementedError: matrix comparison not implemented
+>>> from cvxopt import max
+>>> max(A)          # cvxopt.max takes maximum over all the  elements
+0.0
+>>> print max(A, -1.5)
+[-1.00e+00  0.00e+00]
+[ 0.00e+00 -1.50e+00]
+\end{verbatim}
+\end{funcdesc}
+
+\begin{funcdesc}{min}{x0\optional{, x1\optional{, x2 \ldots}}}
+When called with a single matrix argument, returns the minimum of the 
+elements of the matrix (including the zero entries, if the matrix is 
+sparse).  
+
+When called with multiple arguments, the arguments must be matrices of 
+the same size, or scalars, and the elementwise maximum is returned.   
+A 1 by 1 dense matrix is
+treated as a scalar if the other arguments are not all 1 by 1.
+If one of the arguments is scalar, and the other arguments are not 
+all 1 by 1, then the scalar argument is interpreted as a dense matrix with all its entries 
+equal to the scalar.
+
+\function{min()} can also be called with an iterable (list, tuple, 
+xrange object, or generator) as its single argument, if the iterable 
+generates a list of dense or sparse matrices or scalars.
+\end{funcdesc}
+
+
+\section{Randomly Generated Matrices} 
+\label{s-random}
+The \module{cvxopt} module provides two functions \function{normal()} 
+and \function{uniform()} for generating
+randomly distributed matrices.  The default installation relies on
+the pseudo-random number generators in the Python standard 
+library \module{random}.  Alternatively, the random number generators in 
+the \ulink{GNU Scientific Library (GSL)}{http://www.gnu.org/software/gsl}
+can be used, if this option is selected during the installation of CVXOPT.
+The random matrix functions based on GSL are faster than the default 
+functions based on the \module{random} module.
+
+\begin{funcdesc}{normal}{nrows\optional{, ncols\optional{, 
+ mean\optional{, std}}}}
+Returns a type \dtc\ dense matrix of size \var{nrows} by 
+\var{ncols} with elements chosen from a normal distribution 
+with mean \var{mean} and standard deviation \var{std}.
+The default values for the optional arguments are 
+\var{ncols}=1, \var{mean}=0.0, \var{std}=1.0.
+\end{funcdesc}
+
+\begin{funcdesc}{uniform}{nrows\optional{, ncols\optional{, 
+  a\optional{, b}}}}
+Returns a type \dtc\ dense matrix of size \var{nrows} by 
+\var{ncols} matrix with elements uniformly distributed 
+between \var{a} and \var{b}.
+The default values for the optional arguments are 
+\var{ncols}=1, \var{a}=0.0, \var{b}=1.0.
+\end{funcdesc}
+
+\begin{funcdesc}{setseed}{\optional{value}}
+Sets the state of the random number generator.  \var{value} must be an 
+integer.  If \var{value} is absent or equal to zero, the value is taken 
+from the system clock.  
+If the Python random number generators are used, this is equivalent
+to \function{random.seed(value)}.
+\end{funcdesc}
+
+\begin{funcdesc}{getseed}{}
+Returns the current state of the random number generator.  
+This function is only available if the GSL random number generators are 
+installed.   
+(The state of the random number generators in the Python \module{random} 
+module can be managed via the functions \function{random.getstate()} and 
+\function{random.setstate()}.)
+\end{funcdesc}
+
+
+\section{The NumPy Array Interface} \label{s-array-interface}
+
+The CVXOPT \mtrx\ object is compatible with the \program{NumPy} Array 
+Interface, 
+which allows Python objects that represent multidimensional 
+arrays to exchange data using information stored in the 
+attribute \code{\_\_array\_struct\_\_}.  
+
+\textbf{See also:}
+\BIT
+\item \seelink{http://numpy.scipy.org/array_interface.shtml}
+{NumPy Array Interface Specification}{}
+\item \seelink{http://numpy.scipy.org}{NumPy home page}{}
+\EIT
+
+As already mentioned in section~\ref{s-creating-matrices},
+a two-dimensional array object (for example, a \program{NumPy} matrix or
+two-dimensional array) can be converted to a CVXOPT \mtrx\ object by using 
+the \function{matrix()} constructor.
+Conversely, CVXOPT matrices can be used as array-like objects
+in \program{NumPy}.  The following example illustrates the 
+compatibility of CVXOPT matrices and \program{NumPy} arrays. 
+\begin{verbatim}
+>>> from cvxopt import matrix
+>>> a = matrix(range(6), (2,3), 'd')
+>>> print a
+[ 0.00e+00  2.00e+00  4.00e+00]
+[ 1.00e+00  3.00e+00  5.00e+00]
+>>> from numpy import array
+>>> b = array(a)
+>>> b
+array([[ 0.  2.  4.]
+       [ 1.  3.  5.]])
+>>> a*b
+array([[  0.   4.  16.]
+       [  1.   9.  25.]])
+>>> from numpy import mat
+>>> c = mat(a)
+>>> c
+matrix([[ 0.  2.  4.]
+        [ 1.  3.  5.]])
+>>> a.T * c 
+matrix([[  1.,   3.,   5.],
+        [  3.,  13.,  23.],
+        [  5.,  23.,  41.]])
+\end{verbatim}
+In the first product, \code{a*b} is interpreted as \program{NumPy} array 
+multiplication, \ie, componentwise multiplication.
+The second product \code{a.T*c} is interpreted as \program{NumPy} matrix 
+multiplication, \ie, standard matrix multiplication.
+
+
+\iffalse
+\section{Sparse BLAS Functions}
+The \module{cvxopt.base} module includes a few arithmetic functions 
+that extend functions from \module{cvxopt.blas} to sparse matrices.
+These functions are faster than the corresponding operations 
+implemented using the overloaded arithmetic described 
+in~section~\ref{s-spmatrix-arith}.
+They also work in-place, \ie, they modify their arguments without 
+creating new objects.
+
+
+\begin{funcdesc}{gemv}{A, x, y\optional{, trans='N'\optional{, 
+  alpha=1.0\optional{, beta=0.0}}}}
+Matrix-vector product with a general dense or sparse matrix:
+\[ 
+y := \alpha Ax + \beta y \quad (\mathrm{trans} = \mathrm{'N'}),
+  \qquad 
+y := \alpha A^T x + \beta y \quad (\mathrm{trans} = \mathrm{'T'}), 
+  \qquad
+y := \alpha A^H x + \beta y \quad (\mathrm{trans} = \mathrm{'C'}). 
+\]
+If \var{A} is a dense matrix, this is identical to 
+\function{blas.gemv()}.  If \var{A} is sparse, the result is the same 
+as when \function{blas.gemv()} is called with \code{matrix(A)} as 
+argument, however, without explicitly converting \var{A} to dense.
+\end{funcdesc}
+
+\begin{funcdesc}{symv}{A, x, y\optional{, uplo='L'\optional{, 
+alpha=1.0\optional{, beta=0.0}}}} 
+Matrix-vector product with a dense or sparse real symmetric matrix:
+\[
+   y := \alpha A x + \beta y.
+\]
+If \var{A} is a dense matrix, this is identical to 
+\function{blas.symv()}.  If \var{A} is sparse, the result is the same 
+as when \function{blas.symv()} is called with \code{matrix(A)} as 
+argument, however, without explicitly converting \var{A} to dense.
+\end{funcdesc}
+
+\begin{funcdesc}{gemm}{A, B, C\optional{, transA='N'\optional{, 
+transB='N'\optional{, alpha=1.0\optional{, beta=0.0\optional{, 
+partial=False}}}}}}
+Matrix-matrix product of two general sparse or dense matrices:
+\[
+  C := \alpha \op(A) \op(B) + \beta C 
+\]
+where
+\[
+\op(A) =  \left\{ \begin{array}{ll}
+ A & \mathrm{transA} = \mathrm{'N'} \\
+ A^T & \mathrm{transA} = \mathrm{'T'} \\
+ A^H & \mathrm{transA} = \mathrm{'C'} \end{array} \right.
+\qquad
+\op(B) =  \left\{ \begin{array}{ll}
+ B & \mathrm{transB} = \mathrm{'N'} \\
+ B^T & \mathrm{transB} = \mathrm{'T'} \\
+ B^H & \mathrm{transB} = \mathrm{'C'}. \end{array} \right.
+\]
+If \var{A}, \var{B} and \var{C} are dense matrices, this is 
+identical to \function{blas.gemm()}, described in section~\ref{s-blas3},
+and the argument \var{partial} is ignored.
+
+If \var{A} and/or \var{B} are sparse and \var{C} is dense, the result
+is the same as when \function{blas.gemm()} is called with
+\code{matrix(A)} and \code{matrix(B)} as arguments, without explicitly 
+converting  \var{A} and \var{B} to dense.
+The argument \var{partial} is ignored.
+
+If \var{C} is a sparse matrix, the matrix-matrix product in the
+definition of \function{blas.gemm()} is computed, but as a sparse 
+matrix.  
+If \var{partial} is \False, the result is stored in \var{C}, 
+and the sparsity pattern of \var{C} is  modified if necessary.
+If \var{partial} is \True, the operation only updates the nonzero
+elements in \var{C}, even if the sparsity pattern of \var{C} differs
+from that of the matrix product.  
+\end{funcdesc}
+
+\begin{funcdesc}{syrk}{A, C\optional{, uplo='L'\optional{, 
+trans='N'\optional{, alpha=1.0\optional{, beta=0.0\optional{, 
+partial=False}}}}}}
+Rank-\tm{k} update of a sparse or dense real or complex symmetric
+matrix:
+\[
+ C := \alpha AA^T + \beta C \quad (\mathrm{trans} = \mathrm{'N'}), 
+ \qquad 
+ C := \alpha A^TA + \beta C \quad (\mathrm{trans} = \mathrm{'T'}), 
+\]
+If \var{A} and \var{C} are dense, this is identical to 
+\function{blas.syrk()}, described in section~\ref{s-blas3},
+and the argument \var{partial} is ignored.
+
+If \var{A} is sparse and \var{C} is dense, the result is the same as 
+when \function{blas.syrk()} is called with \code{matrix(A)} as 
+argument, without explicitly converting  \var{A} to dense.  
+The argument \var{partial} is ignored.
+
+If \var{C} is sparse, the product in the definition of 
+\function{blas.syrk()} is computed, but as a sparse matrix.  
+If \var{partial} is \False, the result is stored in \var{C}, 
+and the sparsity pattern of \var{C} is  modified if necessary.
+If \var{partial} is \True, the operation only updates the nonzero
+elements in \var{C}, even if the sparsity pattern of \var{C} differs
+from that of the matrix product.  
+\end{funcdesc}
+
+In the following example, we first compute 
+\[
+C =  A^TB, \qquad
+A = \left[ \begin{array}{ccc}
+0 & 1 & 0 \\ 1 & 0 & 1 \\ 0 & 1 & 0 \\ 1 & 0 & 0 \end{array}\right],
+\qquad
+B = \left[ \begin{array}{ccc}
+   0 & -1 & 0 \\ 2 & 0 & 2 \\ 0 & 3 & 0 \\ 2 & 0 & 0 
+   \end{array}\right].
+\]
+\begin{verbatim}
+>>> from cvxopt.base import spmatrix, gemm
+>>> A = spmatrix(1, [1,3,0,2,1], [0,0,1,1,2])
+>>> B = spmatrix([2,2,-1,3,2], [1,3,0,2,1], [0,0,1,1,2])
+>>> C = spmatrix([], [], [], size=(3,3))
+>>> gemm(A, B, C, transA='T')
+>>> print C
+[ 4.00e+00     0      2.00e+00]
+[    0      2.00e+00     0    ]
+[ 2.00e+00     0      2.00e+00]
+\end{verbatim}
+Now suppose we want to replace \var{C}  with
+\[
+C = A^TD, \qquad 
+D = \left[ \begin{array}{ccc}
+   0 & 1 & 0 \\ 3 & 0 & -2 \\ 0 & 1 & 0 \\ 4 & 0 & 0 
+   \end{array}\right].
+\]
+The new matrix has the same sparsity pattern as \var{C}, so we can 
+use \function{gemm()} with the \code{partial=True} option.
+This saves time in large sparse matrix multiplications when the 
+sparsity pattern of the result is known beforehand.
+\begin{verbatim}
+>>> D = spmatrix([3,4,1,1,-2], [1,3,0,2,1], [0,0,1,1,2])
+>>> gemm(A, D, C, transA='T', partial=True)
+>>> print C
+[ 7.00e+00     0     -2.00e+00]
+[    0      2.00e+00     0    ]
+[ 3.00e+00     0     -2.00e+00]
+\end{verbatim}
+\fi
diff --git a/doc/modeling.tex b/doc/modeling.tex
index b358698..275f547 100644
--- a/doc/modeling.tex
+++ b/doc/modeling.tex
@@ -2,16 +2,18 @@
 \label{chap:modeling}
 The module \module{cvxopt.modeling} can be used to specify and solve 
 optimization problems  with convex piecewise-linear objective and 
-constraint functions.
-
-To specify an optimization problem one first defines 
-the optimization variables (see section~\ref{s-variables}),
-and then defines the objective and constraint functions 
+constraint functions.  Using this modeling tool, one can specify an 
+optimization problem by first defining the optimization variables 
+(see section~\ref{s-variables}),
+and then specifying the objective and constraint functions 
 using linear operations (vector addition and subtraction,
 matrix-vector multiplication, indexing and slicing)
 and nested evaluations of \function{max()}, \function{min()}, 
 \function{abs()} and \function{sum()} (see section~\ref{s-functions}).
 
+A more general Python modeling package is 
+\seelink{http://cvxmod.net}{CVXMOD}{}.
+
 \section{Variables} \label{s-variables}
 Optimization variables are represented by \pytype{variable} objects.
 
@@ -47,7 +49,7 @@ If \var{y} is an \intgr\ or \flt\, all the elements of
 \end{memberdesc}
 
 \begin{verbatim}
->>> from cvxopt.base import matrix
+>>> from cvxopt import matrix
 >>> from cvxopt.modeling import variable
 >>> x = variable(3,'a')
 >>> len(x)
@@ -477,7 +479,7 @@ denotes the matrix types used in the matrix representation of the LP.
 The default value is \code{'dense'}.
 
 The second argument is either \None. \code{'glpk'} or \code{'mosek'},
-and selects one of three available LP solvers: a default solver 
+and selects one of three available LP solvers: the default solver 
 written in Python, the GLPK solver (if installed) or the
 MOSEK LP solver (if installed); see section~\ref{s-lpsolver}.
 The default value  is \None.
@@ -531,6 +533,7 @@ As an example we solve the LP
  \end{array}
 \]
 \begin{verbatim}
+>>> from cvxopt.modeling import op
 >>> x = variable()
 >>> y = variable()
 >>> c1 = ( 2*x+y <= 3 ) 
@@ -559,6 +562,7 @@ As an example we solve the LP
 
 We can solve the same LP in  matrix form as follows.
 \begin{verbatim}
+>>> from cvxopt.modeling import op, dot
 >>> x = variable(2)
 >>> A = matrix([[2.,1.,-1.,0.], [1.,2.,0.,-1.]])
 >>> b = matrix([3.,3.,0.,0.])
@@ -628,7 +632,7 @@ package for plotting the histograms of the residual vectors for the
 two solutions.  It generates the figure shown below.
 
 \begin{verbatim}
-from cvxopt.base import normal
+from cvxopt import normal
 from cvxopt.modeling import variable, op, max, sum
 import pylab
 
@@ -702,7 +706,7 @@ the equivalent LP
 for randomly generated data.
 
 \begin{verbatim}
-from cvxopt.base import normal, uniform
+from cvxopt import normal, uniform
 from cvxopt.modeling import variable, dot, op, sum 
 
 m, n = 500, 100
diff --git a/doc/printing.tex b/doc/printing.tex
index d410acf..cf02b60 100644
--- a/doc/printing.tex
+++ b/doc/printing.tex
@@ -10,8 +10,8 @@ The command \code{"print A"} calls the function \function{str(A)}, whereas
 the command \code{"A"} calls \function{repr(A)}.
 The following example illustrates the default formatting of dense matrices.
 \begin{verbatim}
->>> from cvxopt import base 
->>> A = base.matrix(range(50), (5,10), 'd')
+>>> from cvxopt import matrix 
+>>> A = matrix(range(50), (5,10), 'd')
 >>> A  
 <5x10 matrix, tc='d'>
 >>> print A
@@ -39,7 +39,7 @@ respectively.
 >>> printing.options
 {'width': 7, 'dformat': '% .2e', 'iformat': '% i', 'height': -1}
 >>> printing.options['dformat'] = '%.1f'
->>> print_options['width'] = -1
+>>> printing.options['width'] = -1
 >>> print A
 [ 0.0  5.0 10.0 15.0 20.0 25.0 30.0 35.0 40.0 45.0]
 [ 1.0  6.0 11.0 16.0 21.0 26.0 31.0 36.0 41.0 46.0]
@@ -50,23 +50,24 @@ respectively.
 
 In order to make the built-in Python functions \function{repr()} and 
 \function{str()} accessible for further customization, two functions
-are provided in \module{cvxopt.base}.  
-The function \function{base.matrix\_repr()} is used when \function{repr()}
+are provided in \module{cvxopt}.  
+The function \function{cvxopt.matrix\_repr()} is used when \function{repr()}
 is called with a matrix argument;
-and \function{base.matrix\_str()} is used when \function{str()}
+and \function{cvxopt.matrix\_str()} is used when \function{str()}
 is called with a matrix argument.  By default, the functions are set to
 \function{printing.matrix\_repr\_default()} and
 \function{printing.matrix\_str\_default()}, respectively,
 but they can be redefined to any other Python functions. 
 For example, if we prefer \code{"A"} to return the same output
-as \code{"print A"}, we can simply redefine \function{base.matrix\_repr()} 
-as shown below.
+as \code{"print A"}, we can simply redefine 
+\function{cvxopt..matrix\_repr()} as shown below.
 \begin{verbatim}
->>> from cvxopt import base, printing
->>> A = base.matrix(range(4), (2,2), 'd')
+>>> import cvxopt
+>>> from cvxopt import matrix, printing
+>>> A = matrix(range(4), (2,2), 'd')
 >>> A
 <2x2 matrix, tc='d'>
->>> base.matrix_repr = printing.matrix_str_default
+>>> cvxopt.matrix_repr = printing.matrix_str_default
 >>> A
 [ 0.00e+00  2.00e+00]
 [ 1.00e+00  3.00e+00]
@@ -74,16 +75,17 @@ as shown below.
 
 The formatting for sparse matrices is similar.  
 The functions \function{repr()} and \function{str()} for sparse
-matrices are \function{base.spmatrix\_repr()} 
-and \function{base.spmatrix\_str()}, respectively.
+matrices are \function{cvxopt.spmatrix\_repr()} 
+and \function{cvxopt.spmatrix\_str()}, respectively.
 By default, they are set to \function{printing.spmatrix\_repr\_default()}
 and \function{printing.spmatrix\_repr\_str()}.
 
 \begin{verbatim}
->>> from cvxopt import base, printing 
->>> A = base.spmatrix(range(5), range(5), range(5), (5,10) )
+>>> import cvxopt
+>>> from cvxopt import printing, spmatrix 
+>>> A = spmatrix(range(5), range(5), range(5), (5,10))
 >>> A
-<5x10 matrix, tc='d', nnz=5>
+<5x10 sparse matrix, tc='d', nnz=5>
 >>> print A
 [ 0.00e+00     0         0         0         0         0         0     ... ]
 [    0      1.00e+00     0         0         0         0         0     ... ]
@@ -91,7 +93,7 @@ and \function{printing.spmatrix\_repr\_str()}.
 [    0         0         0      3.00e+00     0         0         0     ... ]
 [    0         0         0         0      4.00e+00     0         0     ... ]
 
->>> base.spmatrix_repr = printing.spmatrix_str_default
+>>> cvxopt.spmatrix_repr = printing.spmatrix_str_default
 >>> A
 [ 0.00e+00     0         0         0         0         0         0     ... ]
 [    0      1.00e+00     0         0         0         0         0     ... ]
@@ -104,12 +106,11 @@ As can be seen from the example, the default behaviour is to print
 the entire matrix including structural zeros. An alternative triplet 
 printing style is defined in \function{printing.spmatrix\_str\_triplet}. 
 \begin{verbatim}
->>> base.spmatrix_str = printing.spmatrix_str_triplet
+>>> cvxopt.spmatrix_str = printing.spmatrix_str_triplet
 >>> print A
 (0,0)  0.00e+00
 (1,1)  1.00e+00
 (2,2)  2.00e+00
 (3,3)  3.00e+00
 (4,4)  4.00e+00
-
 \end{verbatim}
diff --git a/doc/solvers.tex b/doc/solvers.tex
index 69e2f11..7311b10 100644
--- a/doc/solvers.tex
+++ b/doc/solvers.tex
@@ -11,7 +11,7 @@ optimization problems of the form
   & A x = b.
  \end{array}
 \]
-The functions \tm{\s{f}{k}} convex and twice differentiabler and the linear
+The functions \tm{\s{f}{k}} convex and twice differentiable and the linear
 inequalities are generalized inequalities with respect to a proper convex 
 cone, defined as a product of a nonnegative orthant, second-order cones, 
 and positive semidefinite cones.   
@@ -24,7 +24,8 @@ that exploit structure in specific classes of problems.
 The last section describes the algorithm parameters that control the 
 solvers.
 
-\section{General Solver} \label{s-cp}
+\section{Problems with Nonlinear Objectives} \label{s-cp}
+
 \begin{funcdesc}{cp}{F\optional{, G, h\optional{, dims\optional{, 
 A, b\optional{, kktsolver}}}}}
 Solves a convex optimization problem
@@ -36,7 +37,6 @@ Solves a convex optimization problem
   & A x = b,
  \end{array}
 \EEQ
-
 The argument \var{F} is a function that evaluates the objective and 
 nonlinear constraint functions.  It must handle the following calling 
 sequences.
@@ -68,7 +68,6 @@ sequences.
  If \var{F} is called with two arguments, it can be assumed that 
  \tm{x} is in the domain of \tm{f}.
 \end{itemize}
-
 The linear inequalities are with respect to a cone \tm{C} defined as a 
 Cartesian product of a nonnegative orthant, a number of second-order 
 cones, and a number of positive semidefinite cones:
@@ -127,7 +126,9 @@ is that the linear inequalities are componentwise inequalities.
 The role of the optional argument \var{kktsolver} is explained in 
 section~\ref{s-nlcp}.  
 
-\function{cp()} returns a dictionary with keys \code{'status'}, 
+\function{cp()} returns a dictionary that contains the 
+result and information about the accuracy of the solution.
+The most important fields have keys \code{'status'}, 
 \code{'x'}, \code{'snl'}, \code{'sl'}, \code{'y'}, \code{'znl'}, 
 \code{'zl'}. 
 The possible values of the \code{'status'} key are:
@@ -155,13 +156,29 @@ z_\mathrm{nl} \succeq 0, \qquad z_\mathrm{l} \succeq 0, \qquad
 s_\mathrm{nl}^T z_\mathrm{nl} +  s_\mathrm{l}^T z_\mathrm{l} = 0.
 \]
 
-\item[\code{'unknown'}] This indicates that the algorithm reached
-the maximum number of iterations before a solution was found.
+\item[\code{'unknown'}] This indicates that the algorithm terminated 
+before a solution was found, due to numerical difficulties or because 
+the maximum number of iterations was reached.
 The \code{'x'}, \code{'snl'}, \code{'sl'}, \code{'y'}, \code{'znl'} and 
-\code{'zl'} entries are \None. 
+\code{'zl'} contain the iterates when the algorithm terminated.
 \end{description}
+\function{cp()} solves the problem by applying \function{cpl()} to the 
+epigraph form problem
+\[
+ \begin{array}{ll}
+ \mbox{minimize} & t \\ 
+ \mbox{subject to} & f_0(x) \leq t  \\ 
+   & f_k(x) \leq 0, \quad k =1, \ldots, m \\
+   & Gx \preceq h \\
+   & Ax = b.
+ \end{array}
+\]
+The other entries in the output dictionary of \function{cp()} describe
+the accuracy of the solution and are copied from the output of 
+\function{cpl()} applied to this epigraph form problem.
 
-\function{cp()} requires that the problem is solvable and that 
+\function{cp()} requires that the problem is strictly primal and dual
+feasible and that 
 \[
 \Rank(A) = p, \qquad 
 \Rank\left(\left[\begin{array}{cccccc} 
@@ -186,8 +203,7 @@ The function \function{acent()} defined  below solves the problem,
 assumping it is solvable.
 
 \begin{verbatim}
-from cvxopt import solvers 
-from cvxopt.base import matrix, spdiag, log
+from cvxopt import solvers, matrix, spdiag, log
 
 def acent(A, b):
     m, n = A.size
@@ -214,8 +230,7 @@ problem
 \]
 
 \begin{verbatim}
-from cvxopt import solvers 
-from cvxopt.base import matrix, spdiag, sqrt, div
+from cvxopt import solvers, matrix, spdiag, sqrt, div
 
 def robls(A, b, rho): 
     m, n = A.size
@@ -250,8 +265,7 @@ def robls(A, b, rho):
 \]
 
 \begin{verbatim}
-from cvxopt.base import matrix, log, div, spdiag
-from cvxopt import solvers
+from cvxopt import matrix, log, div, spdiag, solvers
 
 def F(x = None, z = None):
      if x is None:  return 0, matrix(0.0, (3,1)) 
@@ -279,11 +293,6 @@ print sol['x']
 
 \section{Problems with Linear Objectives} \label{s-cpl}
 
-The following function for problems with a linear objective 
-is simpler to use than \function{cp()}.  (In fact, \function{cp()} 
-converts the problem to one with a linear objective, and then 
-calls~\function{cpl()}.) 
-
 \begin{funcdesc}{cpl}{c, F\optional{, G, h\optional{, dims\optional{, 
 A, b\optional{, kktsolver}}}}}
 Solves a convex optimization problem with a linear objective
@@ -329,10 +338,138 @@ It must handle the following calling sequences.
  If \var{F} is called with two arguments, it can be assumed that 
  \tm{x} is in the domain of \tm{f}.
 \end{itemize}
+The linear inequalities are with respect to a cone \tm{C} defined as a 
+Cartesian product of a nonnegative orthant, a number of second-order 
+cones, and a number of positive semidefinite cones:
+\[
+C = C_0 \times C_1 \times \cdots \times C_M \times C_{M+1} \times
+ \cdots \times C_{M+N}
+\]
+with
+\[
+C_0 = 
+ \{ u \in \reals^l \;| \; u_k \geq 0, \; k=1, \ldots,l\}, \qquad 
+C_{k+1} = \{ (u_0, u_1) \in \reals \times \reals^{r_{k}-1} \; | \;
+   u_0 \geq \|u_1\|_2 \},  \quad k=0,\ldots, M-1, \qquad 
+C_{k+M+1} = \left\{ \svec(u) \; | \;
+  u \in \symm^{t_k}_+ \right\}, \quad k=0,\ldots,N-1.
+\]
+Here $\svec(u)$ denotes a symmetric matrix \tm{u} stored as a vector 
+in column major order.  
 
-The other arguments have the same meaning as in~\function{cp()}.
+The arguments \var{h} and \var{b} are real single-column dense 
+matrices.  \var{G} and \var{A} are real dense or sparse matrices.
+The default values for \var{A} and \var{b} are sparse matrices with 
+zero rows, meaning that there are no equality constraints.  
+The number of rows of \var{G} and \var{h} is equal to
+\[
+ K = l + \sum_{k=0}^{M-1} r_k + \sum_{k=0}^{N-1} t_k^2.
+\]
+The columns of \var{G} and \var{h} are vectors in
+\[
+\reals^l \times \reals^{r_0} \times \cdots \times 
+\reals^{r_{M-1}} \times \reals^{t_0^2}  \times \cdots \times 
+\reals^{t_{N-1}^2},
+\]
+where the last \tm{N} components represent symmetric matrices stored in 
+column major order.  The strictly upper triangular entries of these 
+matrices are not accessed (i.e.,  the symmetric matrices are stored
+in the 'L'-type column major order used in the \module{blas} and
+\module{lapack} modules).
 
-\function{cpl()} requires that the problem is solvable and that 
+The argument \var{dims} is a dictionary with the dimensions of the 
+cones.  It has three fields. 
+\begin{description}
+\item[\var{dims['l']}:] \tm{l}, the dimension of the nonnegative orthant
+ (a nonnegative integer).
+\item[\var{dims['q']}:] $[r_0, \ldots, r_{M-1}]$, 
+a list with the dimensions of the second-order cones (positive integers).
+\item[\var{dims['s']}:] $[t_0, \ldots, t_{N-1}]$, 
+a list with the dimensions of the positive semidefinite cones
+(nonnegative integers).
+\end{description}
+
+The default value of \var{dims} is 
+\code{\{'l': h.size[0], 'q': [], 's': []\}}, \ie, the default assumption
+is that the linear inequalities are componentwise inequalities.
+
+The role of the optional argument \var{kktsolver} is explained in 
+section~\ref{s-nlcp}.  
+
+\function{cpl()} returns a dictionary that contains the 
+result and information about the accuracy of the solution.
+The most important fields have keys \code{'status'}, 
+\code{'x'}, \code{'snl'}, \code{'sl'}, \code{'y'}, \code{'znl'}, 
+\code{'zl'}. 
+The possible values of the \code{'status'} key are:
+\begin{description}
+\item[\code{'optimal'}]  In this case the 
+\code{'x'} entry of the dictionary is the primal optimal solution,
+  the \code{'snl'} and \code{'sl'} entries are the corresponding
+ slacks in the nonlinear and linear inequality constraints, and the 
+\code{'znl'}, \code{'zl'} and \code{'y'} entries are the optimal 
+ values of the dual variables associated with the nonlinear 
+ inequalities, the linear inequalities, and the linear equality 
+ constraints.  These vectors approximately satisfy the 
+ Karush-Kuhn-Tucker (KKT) conditions
+\[
+ c +  Df(x)^T z_\mathrm{nl} + G^T z_\mathrm{l} + A^T y = 0, \qquad
+ f(x) + s_\mathrm{nl} = 0, \quad k=1,\ldots,m, \qquad
+ Gx + s_\mathrm{l} = h, \qquad Ax = b, 
+\]
+\[
+s_\mathrm{nl}\succeq 0, \qquad s_\mathrm{l}\succeq 0, \qquad 
+z_\mathrm{nl} \succeq 0, \qquad z_\mathrm{l} \succeq 0, \qquad
+s_\mathrm{nl}^T z_\mathrm{nl} +  s_\mathrm{l}^T z_\mathrm{l} = 0.
+\]
+
+\item[\code{'unknown'}] 
+This indicates that the algorithm terminated 
+before a solution was found, due to numerical difficulties or because 
+the maximum number of iterations was reached.
+The \code{'x'}, \code{'snl'}, \code{'sl'}, \code{'y'}, \code{'znl'} and 
+\code{'zl'} contain the iterates when the algorithm terminated.
+\end{description}
+The other entries in the output dictionary describe the accuracy
+of the solution. 
+The entries \code{'primal objective'}, \code{'dual objective'}, 
+\code{'gap'}, and
+\code{'relative gap'}, give the primal objective \tm{c^Tx},
+the dual objective calculated as
+\[
+   c^Tx + z_\mathrm{nl}^T f(x) + z_\mathrm{l}^T (Gx - h) + y^T(Ax-b),
+\]
+the duality gap 
+\[
+  s_\mathrm{nl}^T z_\mathrm{nl} +  s_\mathrm{l}^T z_\mathrm{l},
+\]
+and the relative gap.  The relative gap is defined as 
+\[
+ \frac{\mbox{gap}}{-\mbox{primal objective}}
+ \quad \mbox{if\ } \mbox{primal objective} < 0, \qquad
+ \frac{\mbox{gap}}{\mbox{dual objective}}
+ \quad \mbox{if\ } \mbox{dual objective} > 0, \qquad
+\]
+and \None\ otherwise.  The entry with key \code{'primal infeasibility'}
+gives the residual in the primal constraints, 
+\[
+\frac{\| ( f(x) + s_{\mathrm{nl}},  Gx + s_\mathrm{l} - h, 
+ Ax-b ) \|_2} 
+{\max\{1, \| ( f(x_0) + \ones,  
+Gx_0 + \ones-h, Ax_0-b) \|_2 \}} 
+\]
+where \tm{\s{x}{0}} is the point returned by \code{F()}. 
+The entry with key \code{'dual infeasibility'} gives the residual
+\[
+\frac{\| c +  Df(x)^Tz_\mathrm{nl} + 
+ G^Tz_\mathrm{l} + A^T y \|_2 }
+{\max\{ 1, 
+\| c + Df(x_0)^T\ones + G^T\ones \|_2 \}} 
+\leq \epsilon_\mathrm{feas}. 
+\]
+
+\function{cpl()} requires that the problem is strictly primal and
+dual feasible and that 
 \[
 \Rank(A) = p, \qquad 
 \Rank\left(\left[\begin{array}{cccccc} 
@@ -348,7 +485,7 @@ for all \tm{x} and all positive \tm{z}.
 \item[Example: floor planning]
 
 This example is the floor planning problem of section 8.8.2 in the book 
-\citetitle{http://www.stanford.edu/\~{}boyd/cvxbook}{Convex Optimization}: 
+\seelink{http://www.stanford.edu/\%7eboyd/cvxbook}{Convex Optimization}{}: 
 \[
 \begin{array}{ll}
  \mbox{minimize} & W + H \\
@@ -378,8 +515,7 @@ a figure.
 
 \begin{verbatim}
 import pylab
-from cvxopt import solvers
-from cvxopt.base import matrix, spmatrix, mul, div
+from cvxopt import solvers, matrix, spmatrix, mul, div
 
 def floorplan(Amin):
 
@@ -511,6 +647,7 @@ pylab.show()
 \end{center}
 \end{description}
 
+
 \section{Geometric Programming} \label{s-gp}
 \begin{funcdesc}{gp}{K, F, g \optional{, G, h \optional{, A, b}}}
 Solves a geometric program in convex form
@@ -528,8 +665,9 @@ where
  F = \left[ \begin{array}{cccc}
  F_0^T & F_1^T & \cdots & F_m^T \end{array}\right]^T, \qquad
  g = \left[ \begin{array}{cccc}
- g_0^T & g_1^T & \cdots & g_m^T \end{array}\right]^T. 
+ g_0^T & g_1^T & \cdots & g_m^T \end{array}\right]^T, 
 \]
+and the vector inequality denotes componentwise inequality.
 \var{K} is a list of \tm{m+1} positive integers with 
 \code{\var{K}[\var i]}
 equal to the number of rows in \tm{\s{F}{i}}.
@@ -568,15 +706,19 @@ z_\mathrm{nl} \succeq 0, \qquad z_\mathrm{l} \succeq 0, \qquad
 s_\mathrm{nl}^T z_\mathrm{nl} + s_\mathrm{l}^T z_\mathrm{l} = 0.
 \]
 
-\item[\code{'unknown'}] This means that the algorithm reached
-the maximum number of iterations before a solution was found.
-The \code{'x'}, \code{'snl'}, \code{'sl'}, \code{'y'}, \code{'znl'} 
-and \code{'zl'} entries are \None. 
+\item[\code{'unknown'}] 
+This indicates that the algorithm terminated 
+before a solution was found, due to numerical difficulties or because 
+the maximum number of iterations was reached.
+The \code{'x'}, \code{'snl'}, \code{'sl'}, \code{'y'}, \code{'znl'} and 
+\code{'zl'} contain the iterates when the algorithm terminated.
 \end{description}
+The other entries in the output dictionary describe the accuracy
+of the solution, and are taken from the output of \function{cp()}.
 \end{funcdesc}
 
 As an example, we solve the small GP of section 2.4 of the paper 
-\citetitle{http://www.stanford.edu/\~{}boyd/gp\_tutorial.html}{A Tutorial on Geometric Programming}.  
+\citetitle{http://www.stanford.edu/\%7eboyd/\s{gp}{tutorial}.html}{A Tutorial on Geometric Programming}.  
 The  posynomial form of the problem is
 \[
  \begin{array}{ll}
@@ -593,8 +735,7 @@ The  posynomial form of the problem is
 with variables \tm{h}, \tm{w}, \tm{d}.
 
 \begin{verbatim}
-from cvxopt.base import matrix, log, exp
-from cvxopt import solvers
+from cvxopt import matrix, log, exp, solvers
 
 Aflr  = 1000.0
 Awall = 100.0
@@ -637,13 +778,14 @@ where
  \nabla f_1(x) & \cdots & \nabla f_m(x) & G^T \end{array}\right]^T.
 \]
 
-The matrix \tm{W} depends on the current iterates and is defined as follows.
-Suppose 
+The matrix \tm{W} depends on the current iterates and is defined as 
+follows.  Suppose 
 \[
  u = \left(u_\mathrm{nl}, \; u_\mathrm{l}, \; u_{\mathrm{q},0}, \; 
   \ldots, \; 
  u_{\mathrm{q},M-1}, \; \svec{(u_{\mathrm{s},0})}, \; \ldots, \; 
   \svec{(u_{\mathrm{s},N-1})}\right), \qquad
+ u_\mathrm{nl} \in\reals^m, \qquad 
  u_\mathrm{l} \in\reals^l, \qquad 
  u_{\mathrm{q},k} \in\reals^{r_k}, \quad k = 0,\ldots,M-1, \qquad 
  u_{\mathrm{s},k} \in\symm^{t_k},  \quad k = 0,\ldots,N-1.
@@ -854,8 +996,7 @@ The following code follows this method.  It also uses BLAS functions
 for matrix-matrix and matrix-vector products.
 
 \begin{verbatim}
-from cvxopt.base import matrix, spdiag, mul, div, log 
-from cvxopt import base, blas, lapack, solvers, base
+from cvxopt import matrix, spdiag, mul, div, log, blas, lapack, solvers, base
 
 def l2ac(A, b):
     """
@@ -903,11 +1044,10 @@ def l2ac(A, b):
     #     D * x = bx / z[0] - A'*v.
 
     S = matrix(0.0, (m,m))
-    Asc = matrix(0.0, (m,n))
     v = matrix(0.0, (m,1))
     def Fkkt(x, z, W):
         ds = (2.0 * div(1 + x**2, (1 - x**2)**2))**-0.5
-        base.gemm(A, spdiag(ds), Asc)
+        Asc = spdiag(ds) * A
         blas.syrk(Asc, S)
         S[::m+1] += 1.0 
         lapack.potrf(S)
@@ -983,9 +1123,9 @@ where
 \qquad
 L(x,y,z) = c^Tx + z_\mathrm{nl}^T f(x) + z_\mathrm{l}^T (Gx-h) + y^T(Ax-b).
 \]
-The functions \function{cp()}, \function{qp()}, and \function{gp()} 
+The functions \function{cp()} and \function{gp()} 
 call \function{cpl()} and hence use the same stopping criteria
-(with \tm{\s{x}{0}=0} for \function{qp()} and \function{gp()}).
+(with \tm{\s{x}{0}=0} for \function{gp()}).
 
 The MOSEK interior-point algorithm parameters are set to their default 
 values.  They can be modified by adding an entry 
diff --git a/doc/spsolvers.tex b/doc/spsolvers.tex
index 41514e2..526b5b7 100644
--- a/doc/spsolvers.tex
+++ b/doc/spsolvers.tex
@@ -55,8 +55,7 @@ As an example we consider the matrix
 \end{array}\right].
 \]
 \begin{verbatim}
->>> from cvxopt.base import spmatrix
->>> from cvxopt import amd 
+>>> from cvxopt import spmatrix, amd 
 >>> A = spmatrix([10,3,5,-2,5,2], [0,2,1,2,2,3], [0,0,1,1,2,3])
 >>> P = amd.order(A)
 >>> print P
@@ -107,8 +106,7 @@ A = \left[\begin{array}{rrrrr}
  \end{array}\right].
 \EEQ
 \begin{verbatim}
->>> from cvxopt.base import spmatrix, matrix
->>> from cvxopt import umfpack 
+>>> from cvxopt import spmatrix, matrix, umfpack 
 >>> V = [2,3, 3,-1,4, 4,-3,1,2, 2, 6,1]
 >>> I = [0,1, 0, 2,4, 1, 2,3,4, 2, 1,4]
 >>> J = [0,0, 1, 1,1, 2, 2,2,2, 3, 4,4]
@@ -181,8 +179,7 @@ The following code computes
  x = A^{-T}B^{-1}A^{-1}\ones.
 \]
 \begin{verbatim}
->>> from cvxopt.base import spmatrix, matrix
->>> from cvxopt import umfpack
+>>> from cvxopt import spmatrix, matrix, umfpack
 >>> VA = [2,3, 3,-1,4, 4,-3,1,2, 2, 6,1]
 >>> VB = [4,3, 3,-1,4, 4,-3,1,2, 2, 6,2]
 >>> I =  [0,1, 0, 2,4, 1, 2,3,4, 2, 1,4]
@@ -247,8 +244,7 @@ As an  example, we solve
    0 & 4 \\ 1 & 5 \\ 2 & 6 \\ 3 & 7\end{array} \right].
 \EEQ
 \begin{verbatim}
->>> from cvxopt.base import matrix, spmatrix
->>> from cvxopt import cholmod
+>>> from cvxopt import matrix, spmatrix, cholmod
 >>> A = spmatrix([10,3, 5,-2, 5, 2], [0,2, 1,3, 2, 3], [0,0, 1,1, 2, 3])
 >>> X = matrix(range(8), (4,2), 'd')
 >>> cholmod.linsolve(A,X)
@@ -370,8 +366,8 @@ For the same example as above:
 \begin{verbatim}
 >>> X = matrix(range(8), (4,2), 'd')
 >>> F = cholmod.symbolic(A)
->>> cholmod.numeric(A,F)
->>> cholmod.solve(F,X)
+>>> cholmod.numeric(A, F)
+>>> cholmod.solve(F, X)
 >>> print X
 [-1.46e-01  4.88e-02]
 [ 1.33e+00  4.00e+00]
@@ -415,18 +411,18 @@ of the coefficient matrix in~(\ref{e-A-pd}) by two methods.
 \begin{verbatim}
 >>> import math
 >>> from cvxopt.cholmod import options
->>> from cvxopt.base import log
+>>> from cvxopt import log
 >>> F = cholmod.symbolic(A)
->>> cholmod.numeric(A,F)
+>>> cholmod.numeric(A, F)
 >>> print 2.0 * sum(log(cholmod.diag(F)))
 5.50533153593
 \end{verbatim}
 \begin{verbatim}
 >>> options['supernodal'] = 0
 >>> F = cholmod.symbolic(A)
->>> cholmod.numeric(A,F)
+>>> cholmod.numeric(A, F)
 >>> Di = matrix(1.0, (4,1))
->>> cholmod.solve(F,Di,sys=6)
+>>> cholmod.solve(F, Di, sys=6)
 >>> print -sum(log(Di))
 5.50533153593
 \end{verbatim}
@@ -488,8 +484,7 @@ by
 where \code{o} denotes Hadamard product.
 
 \begin{verbatim}
-from cvxopt.base import matrix, spmatrix, log, mul
-from cvxopt import blas, lapack, amd, cholmod
+from cvxopt import matrix, spmatrix, log, mul, blas, lapack, amd, cholmod
 
 def covsel(Y):
     """
diff --git a/examples/book/README b/examples/book/README
index a862c04..347b2e1 100644
--- a/examples/book/README
+++ b/examples/book/README
@@ -4,4 +4,4 @@ www.stanford.edu/~boyd/cvxbook).
 
 The scripts require the plotting library Matplotlib, available at
 matplotlib.sourceforge.net.  They were tested with Matplotlib 
-version 0.90.1.
+version 0.91.2.
diff --git a/examples/book/chap4/portfolio b/examples/book/chap4/portfolio
index 3cc4859..059dc4a 100755
--- a/examples/book/chap4/portfolio
+++ b/examples/book/chap4/portfolio
@@ -4,7 +4,7 @@
 # Risk-return trade-off.
 
 from math import sqrt
-from cvxopt.base import matrix
+from cvxopt import matrix
 from cvxopt.blas import dot 
 from cvxopt.solvers import qp, options 
 import pylab
diff --git a/examples/book/chap4/rls b/examples/book/chap4/rls
index 6ef3173..0130b80 100755
--- a/examples/book/chap4/rls
+++ b/examples/book/chap4/rls
@@ -5,8 +5,7 @@
 
 import pylab
 from pickle import load
-from cvxopt import blas, lapack
-from cvxopt.base import matrix
+from cvxopt import blas, lapack, matrix
 from cvxopt import solvers 
 solvers.options['show_progress'] = 0
 
diff --git a/examples/book/chap6/basispursuit b/examples/book/chap6/basispursuit
index c98ce93..dd1ca1e 100755
--- a/examples/book/chap6/basispursuit
+++ b/examples/book/chap6/basispursuit
@@ -3,7 +3,7 @@
 # Figures 6.21-23, pages 335-337.
 # Basis pursuit.
 
-from cvxopt.base import matrix, mul, div, cos, sin, exp, sqrt
+from cvxopt import matrix, mul, div, cos, sin, exp, sqrt 
 from cvxopt import blas, lapack, solvers
 import pylab
 
diff --git a/examples/book/chap6/consumerpref b/examples/book/chap6/consumerpref
index 69870c4..9b4d763 100755
--- a/examples/book/chap6/consumerpref
+++ b/examples/book/chap6/consumerpref
@@ -4,8 +4,7 @@
 # Consumer preference analysis.
 
 import pylab
-from cvxopt import solvers
-from cvxopt.base import matrix, sqrt
+from cvxopt import solvers, matrix, sqrt
 from cvxopt.modeling import variable, op
 solvers.options['show_progress'] = 0
 
diff --git a/examples/book/chap6/cvxfit b/examples/book/chap6/cvxfit
index 6d1958a..8dfa2d0 100755
--- a/examples/book/chap6/cvxfit
+++ b/examples/book/chap6/cvxfit
@@ -4,8 +4,7 @@
 # Least-squares fit of a convex function.
 
 import pylab
-from cvxopt import solvers 
-from cvxopt.base import matrix, spmatrix, mul
+from cvxopt import solvers, matrix, spmatrix, mul
 from pickle import load
 solvers.options['show_progress'] = 0
 
diff --git a/examples/book/chap6/huber b/examples/book/chap6/huber
index d9a2daf..26a3c62 100755
--- a/examples/book/chap6/huber
+++ b/examples/book/chap6/huber
@@ -3,8 +3,7 @@
 # Figure 6.5, page 300.
 # Robust regression.
 
-from cvxopt import solvers, lapack
-from cvxopt.base import matrix, spmatrix
+from cvxopt import solvers, lapack, matrix, spmatrix
 import pylab
 from pickle import load
 solvers.options['show_progress'] = 0
diff --git a/examples/book/chap6/inputdesign b/examples/book/chap6/inputdesign
index efe9f0f..2188a9c 100755
--- a/examples/book/chap6/inputdesign
+++ b/examples/book/chap6/inputdesign
@@ -4,8 +4,7 @@
 # Input design.
 
 from math import cos, sqrt
-from cvxopt import lapack
-from cvxopt.base import matrix 
+from cvxopt import lapack, matrix
 import pylab
 
 m, n = 201, 201
diff --git a/examples/book/chap6/penalties b/examples/book/chap6/penalties
index b1c6e07..d7bb1fd 100755
--- a/examples/book/chap6/penalties
+++ b/examples/book/chap6/penalties
@@ -5,15 +5,14 @@
 #
 # The problem data are not the same as in the book figure.
 
-import pylab
-from cvxopt import base, lapack, solvers
-from cvxopt.base import matrix, spdiag, log, div
+import pylab, numpy
+from cvxopt import lapack, solvers, matrix, spdiag, log, div, normal
 from cvxopt.modeling import variable, op, max, sum 
 solvers.options['show_progress'] = 0
 
 m, n = 100, 30
-A = base.normal(m,n)
-b = base.normal(m,1)
+A = normal(m,n)
+b = normal(m,1)
 b /= (1.1 * max(abs(b)))   # Make x = 0 feasible for log barrier.
 
 
@@ -29,7 +28,7 @@ pylab.figure(1, facecolor='w', figsize=(10,10))
 pylab.subplot(411)
 nbins = 100
 bins = [-1.5 + 3.0/(nbins-1)*k for k in xrange(nbins)]
-pylab.hist( A*x1+b ,  bins)
+pylab.hist( A*x1+b , numpy.array(bins))
 nopts = 200
 xs = -1.5 + 3.0/(nopts-1) * matrix(range(nopts))
 pylab.plot(xs, (35.0/1.5) * abs(xs), 'g-')
@@ -48,7 +47,7 @@ lapack.gels(+A, x)
 x2 = x[:n]
 
 pylab.subplot(412)
-pylab.hist( A*x2+b ,  bins)
+pylab.hist(A*x2+b, numpy.array(bins))
 pylab.plot(xs, (8.0/1.5**2) * xs**2 , 'g-')
 pylab.ylabel('l2')
 pylab.axis([-1.5, 1.5, 0, 10])
@@ -63,7 +62,7 @@ op(sum(max(abs(A*x+b)-0.5, 0.0))).solve()
 xdz = x.value
 
 pylab.subplot(413)
-pylab.hist( A*xdz+b ,  bins)
+pylab.hist(A*xdz+b, numpy.array(bins))
 pylab.plot(xs, 15.0/1.0 * matrix([ max(abs(xk)-0.5, 0.0) for xk 
     in xs ]), 'g-')
 pylab.ylabel('Deadzone')
@@ -86,7 +85,7 @@ def F(x=None, z=None):
 xlb = solvers.cp(F)['x']
 
 pylab.subplot(414)
-pylab.hist( A*xlb+b ,  bins)
+pylab.hist(A*xlb+b, numpy.array(bins))
 nopts = 200
 pylab.plot(xs, (8.0/1.5**2) * xs**2, 'g--')
 xs2 = -0.99999 + (2*0.99999 /(nopts-1)) * matrix(range(nopts))
diff --git a/examples/book/chap6/polapprox b/examples/book/chap6/polapprox
index 0f2c294..cfefba5 100755
--- a/examples/book/chap6/polapprox
+++ b/examples/book/chap6/polapprox
@@ -3,8 +3,7 @@
 # Figures 6.19 and 6.20, page 332.
 # Polynomial and spline fitting.
 
-from cvxopt import lapack, solvers 
-from cvxopt.base import matrix, mul
+from cvxopt import lapack, solvers, matrix, mul
 from cvxopt.modeling import op, variable, max
 import pylab
 from pickle import load
diff --git a/examples/book/chap6/regsel b/examples/book/chap6/regsel
index a572a1b..6128e30 100755
--- a/examples/book/chap6/regsel
+++ b/examples/book/chap6/regsel
@@ -6,8 +6,7 @@
 # The problem data are different from the book.
 
 import pylab
-from cvxopt import blas, lapack, solvers 
-from cvxopt.base import matrix, mul
+from cvxopt import blas, lapack, solvers, matrix, mul
 from pickle import load
 solvers.options['show_progress'] = 0
 
diff --git a/examples/book/chap6/robls b/examples/book/chap6/robls
index 2de7b97..6cc8886 100755
--- a/examples/book/chap6/robls
+++ b/examples/book/chap6/robls
@@ -4,9 +4,9 @@
 # Stochastic and worst-case robust approximation.
 
 from math import pi
-from cvxopt import base, blas, lapack, solvers
-from cvxopt.base import matrix, spmatrix, mul, cos, sin, sqrt
-import pylab
+from cvxopt import blas, lapack, solvers
+from cvxopt import matrix, spmatrix, mul, cos, sin, sqrt, uniform
+import pylab, numpy
 from pickle import load
 solvers.options['show_progress'] = 0
 
@@ -121,8 +121,8 @@ lapack.posv(S, xtik)
 xwc = wcls(A, Ap, b)
 
 notrials = 100000
-r = sqrt(base.uniform(1,notrials))
-theta = 2.0 * pi * base.uniform(1,notrials)
+r = sqrt(uniform(1,notrials))
+theta = 2.0 * pi * uniform(1,notrials)
 u = matrix(0.0, (2,notrials))
 u[0,:] = mul(r, cos(theta))
 u[1,:] = mul(r, sin(theta))
@@ -145,13 +145,13 @@ r = P*u + q[:,notrials*[0]]
 reswc = sqrt( matrix(1.0, (1,m)) * mul(r,r) )
 
 pylab.figure(2, facecolor='w')
-pylab.hist(resls, [0.1*k for k in xrange(50)], fc='w', 
+pylab.hist(resls, numpy.array([0.1*k for k in xrange(50)]), fc='w', 
     normed=True)
 pylab.text(4.4, 0.4, 'least-squares')
-pylab.hist(restik, [0.1*k for k in xrange(50)], fc='#D0D0D0', 
+pylab.hist(restik, numpy.array([0.1*k for k in xrange(50)]), fc='#D0D0D0', 
     normed=True)
 pylab.text(2.9, 0.75, 'Tikhonov')
-pylab.hist(reswc, [0.1*k for k in xrange(50)], fc='#B0B0B0', 
+pylab.hist(reswc, numpy.array([0.1*k for k in xrange(50)]), fc='#B0B0B0', 
     normed=True)
 pylab.text(2.5, 2.0, 'robust least-squares')
 pylab.xlabel('residual')
diff --git a/examples/book/chap6/smoothrec b/examples/book/chap6/smoothrec
index ed96754..96b8775 100755
--- a/examples/book/chap6/smoothrec
+++ b/examples/book/chap6/smoothrec
@@ -4,14 +4,13 @@
 # Quadratic smoothing.
 
 from math import pi
-from cvxopt import base, blas, lapack 
-from cvxopt.base import matrix, sin, mul
+from cvxopt import blas, lapack, matrix, sin, mul, normal
 import pylab
 
 n = 4000
 t = matrix(range(n), tc='d')
 ex = 0.5 * mul( sin(2*pi/n * t), sin(0.01 * t))
-corr = ex + 0.05 * base.normal(n,1)
+corr = ex + 0.05 * normal(n,1)
 
 pylab.figure(1, facecolor='w', figsize=(8,5))
 pylab.subplot(211)
diff --git a/examples/book/chap6/tv b/examples/book/chap6/tv
index aae9a11..54bcb7d 100755
--- a/examples/book/chap6/tv
+++ b/examples/book/chap6/tv
@@ -4,8 +4,8 @@
 # Total variation reconstruction.
 
 from math import pi
-from cvxopt import base, blas, lapack, solvers
-from cvxopt.base import matrix, spmatrix, sin, mul, div
+from cvxopt import blas, lapack, solvers 
+from cvxopt import matrix, spmatrix, sin, mul, div, normal
 solvers.options['show_progress'] = 0
 import pylab
 
@@ -13,7 +13,7 @@ n = 2000
 t = matrix( range(n), tc='d' )
 ex = matrix( n/4*[1.0] + n/4*[-1.0] + n/4*[1.0] + n/4*[-1.0] ) + \
     0.5 * sin( 2.0*pi/n * t )
-corr = ex + 0.1 * base.normal(n,1)
+corr = ex + 0.1 * normal(n,1)
 
 pylab.figure(1, facecolor='w', figsize=(8,5))
 pylab.subplot(211)
diff --git a/examples/book/chap7/chernoff b/examples/book/chap7/chernoff
index c12ada4..fccefbb 100755
--- a/examples/book/chap7/chernoff
+++ b/examples/book/chap7/chernoff
@@ -4,8 +4,7 @@
 # Chernoff lower bound.
 
 import pylab
-from cvxopt.base import matrix, mul, exp
-from cvxopt import solvers, blas, base 
+from cvxopt import matrix, mul, exp, normal, solvers, blas
 solvers.options['show_progress'] = False
 
 # Extreme points and inequality description of Voronoi region around 
@@ -89,7 +88,7 @@ if 0:  # uncomment out for the Monte Carlo estimation.
     mcest = []
     ones = matrix(1.0, (1,m))
     for sigma in sigmas: 
-        X = sigma * base.normal(2, N)
+        X = sigma * normal(2, N)
         S = b[0][:,N*[0]] - A[0]*X 
         S = ones * (S - abs(S))
         mcest += [ N - len(filter(lambda x: x < 0.0, S)) ]
diff --git a/examples/book/chap7/expdesign b/examples/book/chap7/expdesign
index 26bf1f8..94d9963 100755
--- a/examples/book/chap7/expdesign
+++ b/examples/book/chap7/expdesign
@@ -5,8 +5,8 @@
 
 from math import pi, log, sqrt
 import pylab
-from cvxopt import base, blas, lapack, solvers
-from cvxopt.base import matrix, spmatrix, spdiag, mul, cos, sin
+from cvxopt import blas, lapack, solvers
+from cvxopt import matrix, spmatrix, spdiag, mul, cos, sin
 solvers.options['show_progress'] = False
 
 V = matrix([-2.1213,    2.1213,
diff --git a/examples/book/chap7/logreg b/examples/book/chap7/logreg
index c0cbd80..5a1f6c3 100755
--- a/examples/book/chap7/logreg
+++ b/examples/book/chap7/logreg
@@ -4,8 +4,7 @@
 # Logistic regression.
 
 import pylab, pickle
-from cvxopt import solvers
-from cvxopt.base import matrix, spdiag, log, exp, div
+from cvxopt import solvers, matrix, spdiag, log, exp, div
 solvers.options['show_progress'] = False
 
 data = pickle.load(open("logreg.bin"))
diff --git a/examples/book/chap7/maxent b/examples/book/chap7/maxent
index 18a8a01..b30be03 100755
--- a/examples/book/chap7/maxent
+++ b/examples/book/chap7/maxent
@@ -4,8 +4,7 @@
 # Maximum entropy distribution.
 
 import pylab
-from cvxopt import solvers, blas
-from cvxopt.base import matrix, spmatrix, spdiag, log, div
+from cvxopt import solvers, blas, matrix, spmatrix, spdiag, log, div
 solvers.options['show_progress'] = False
 
 # minimize     p'*log p  
diff --git a/examples/book/chap7/probbounds b/examples/book/chap7/probbounds
index 0370ee8..8172cfe 100755
--- a/examples/book/chap7/probbounds
+++ b/examples/book/chap7/probbounds
@@ -5,8 +5,7 @@
 
 import pylab
 from math import pi, sqrt
-from cvxopt.base import matrix, spmatrix, mul, cos, sin
-from cvxopt import solvers, blas, lapack
+from cvxopt import matrix, spmatrix, mul, cos, sin, solvers, blas, lapack
 solvers.options['show_progress'] = False
 
 # Extreme points and inequality description of Voronoi region around 
diff --git a/examples/book/chap8/centers b/examples/book/chap8/centers
index 57caa08..137bd7e 100755
--- a/examples/book/chap8/centers
+++ b/examples/book/chap8/centers
@@ -6,7 +6,7 @@
 import pylab
 from math import log, pi
 from cvxopt import blas, lapack, solvers
-from cvxopt.base import matrix, spdiag, sqrt, mul, cos, sin, log
+from cvxopt import matrix, spdiag, sqrt, mul, cos, sin, log
 from cvxopt.modeling import variable, op 
 solvers.options['show_progress'] = False
 
diff --git a/examples/book/chap8/ellipsoids b/examples/book/chap8/ellipsoids
index 5f6a518..5debf74 100755
--- a/examples/book/chap8/ellipsoids
+++ b/examples/book/chap8/ellipsoids
@@ -5,8 +5,7 @@
  
 import pylab
 from math import log, pi
-from cvxopt import blas, lapack, solvers
-from cvxopt.base import matrix, sqrt, mul, cos, sin
+from cvxopt import blas, lapack, solvers, matrix, sqrt, mul, cos, sin
 solvers.options['show_progress'] = False
 
 # Extreme points (with first one appended at the end)
diff --git a/examples/book/chap8/floorplan b/examples/book/chap8/floorplan
index f7b6028..0f8a35f 100755
--- a/examples/book/chap8/floorplan
+++ b/examples/book/chap8/floorplan
@@ -4,8 +4,7 @@
 # Floor planning example.
 
 import pylab
-from cvxopt import solvers
-from cvxopt.base import matrix, spmatrix, mul, div
+from cvxopt import solvers, matrix, spmatrix, mul, div
 
 def floorplan(Amin):
 
diff --git a/examples/book/chap8/linsep b/examples/book/chap8/linsep
index 9ecf7e4..714c612 100755
--- a/examples/book/chap8/linsep
+++ b/examples/book/chap8/linsep
@@ -4,9 +4,8 @@
 # Approximate linear discrimination.
 
 import pylab, pickle
-from cvxopt import solvers 
+from cvxopt import solvers, matrix, spmatrix, spdiag, log, exp, div
 from cvxopt.blas import dot
-from cvxopt.base import matrix, spmatrix, spdiag, log, exp, div
 from cvxopt.modeling import variable, op
 solvers.options['show_progress'] = False
 
diff --git a/examples/book/chap8/placement b/examples/book/chap8/placement
index a47d627..b5616c9 100755
--- a/examples/book/chap8/placement
+++ b/examples/book/chap8/placement
@@ -5,9 +5,8 @@
 #
 # The problem data are different from the example in the book.
 
-import pylab, pickle
-from cvxopt import lapack, solvers
-from cvxopt.base import matrix, spmatrix, sqrt, mul
+import pylab, numpy, pickle
+from cvxopt import lapack, solvers, matrix, spmatrix, sqrt, mul
 from cvxopt.modeling import variable, op
 solvers.options['show_progress'] = False
 
@@ -127,7 +126,7 @@ pylab.title('Linear placement')
 
 pylab.subplot(122) 
 lngths = sqrt((A1*X1 + B)**2 * matrix(1.0, (2,1)))
-pylab.hist(lngths, [.1*k for k in xrange(15)])
+pylab.hist(lngths, numpy.array([.1*k for k in xrange(15)]))
 x = pylab.arange(0, 1.6, 1.6/500)
 pylab.plot( x, 5.0/1.6*x, '--k')
 pylab.axis([0, 1.6, 0, 5.5])
@@ -147,7 +146,7 @@ pylab.title('Quadratic placement')
 
 pylab.subplot(122) 
 lngths = sqrt((A1*X2 + B)**2 * matrix(1.0, (2,1)))
-pylab.hist(lngths, [.1*k for k in xrange(15)])
+pylab.hist(lngths, numpy.array([.1*k for k in xrange(15)]))
 x = pylab.arange(0, 1.5, 1.5/500)
 pylab.plot( x, 5.0/1.5**2 * x**2, '--k')
 pylab.axis([0, 1.5, 0, 5.5])
@@ -167,7 +166,7 @@ pylab.title('Fourth order placement')
 
 pylab.subplot(122) 
 lngths = sqrt((A1*X4 + B)**2 * matrix(1.0, (2,1)))
-pylab.hist(lngths, [.1*k for k in xrange(15)])
+pylab.hist(lngths, numpy.array([.1*k for k in xrange(15)]))
 x = pylab.arange(0, 1.5, 1.5/500)
 pylab.plot( x, 6.0/1.4**4 * x**4, '--k')
 pylab.axis([0, 1.4, 0, 6.5])
diff --git a/examples/doc/chap10/l1svc b/examples/doc/chap10/l1svc
index 299c3c4..bb45b06 100755
--- a/examples/doc/chap10/l1svc
+++ b/examples/doc/chap10/l1svc
@@ -1,8 +1,8 @@
 #!/usr/bin/python
 
-# The 1-norm support vector classifier of section 9.5.
+# The 1-norm support vector classifier of section 10.5.
 
-from cvxopt.base import normal, setseed
+from cvxopt import normal, setseed
 from cvxopt.modeling import variable, op, max, sum
 from cvxopt.blas import nrm2
 
diff --git a/examples/doc/chap10/lp b/examples/doc/chap10/lp
index 259d6db..e9c35c1 100755
--- a/examples/doc/chap10/lp
+++ b/examples/doc/chap10/lp
@@ -2,7 +2,7 @@
 
 # The small LP of section 10.4.  
 
-from cvxopt.base import matrix
+from cvxopt import matrix
 from cvxopt.modeling import variable, op, dot
 
 x = variable()  
diff --git a/examples/doc/chap10/normappr b/examples/doc/chap10/normappr
index 8f998be..b3bbecb 100755
--- a/examples/doc/chap10/normappr
+++ b/examples/doc/chap10/normappr
@@ -1,8 +1,8 @@
 #!/usr/bin/python
 
-# The norm and penalty approximation problems of section 9.5.
+# The norm and penalty approximation problems of section 10.5.
 
-from cvxopt.base import normal, setseed
+from cvxopt import normal, setseed
 from cvxopt.modeling import variable, op, max, sum
 
 setseed(0)
diff --git a/examples/doc/chap10/roblp b/examples/doc/chap10/roblp
index 614022a..8180f81 100755
--- a/examples/doc/chap10/roblp
+++ b/examples/doc/chap10/roblp
@@ -1,8 +1,8 @@
 #!/usr/bin/python
 
-# The robust LP example of section 9.5.
+# The robust LP example of section 10.5.
 
-from cvxopt.base import normal, uniform  
+from cvxopt import normal, uniform  
 from cvxopt.modeling import variable, dot, op, sum  
 from cvxopt.blas import nrm2  
      
diff --git a/examples/doc/chap4/acent b/examples/doc/chap4/acent
index 9923441..312e56c 100755
--- a/examples/doc/chap4/acent
+++ b/examples/doc/chap4/acent
@@ -2,8 +2,7 @@
 
 # The analytic centering example of section 4.9.
 
-from cvxopt.base import matrix, log, mul, div
-from cvxopt import blas, lapack, base
+from cvxopt import matrix, log, mul, div, blas, lapack, base
 from math import sqrt
 
 def acent(A,b):
diff --git a/examples/doc/chap7/covsel b/examples/doc/chap7/covsel
index 67d218b..a4ce66a 100755
--- a/examples/doc/chap7/covsel
+++ b/examples/doc/chap7/covsel
@@ -2,8 +2,7 @@
 
 # The covariance selection example of section 7.4.
 
-from cvxopt.base import matrix, spmatrix, log, mul
-from cvxopt import blas, lapack, amd, cholmod
+from cvxopt import matrix, spmatrix, log, mul, blas, lapack, amd, cholmod
 from pickle import load
 
 def covsel(Y):
diff --git a/examples/doc/chap8/conelp b/examples/doc/chap8/conelp
index 7caa300..5473b06 100755
--- a/examples/doc/chap8/conelp
+++ b/examples/doc/chap8/conelp
@@ -2,8 +2,7 @@
 # 
 # The small linear cone program of section 8.1
 
-from cvxopt.base import matrix
-from cvxopt import solvers
+from cvxopt import matrix, solvers
 
 c = matrix([-6., -4., -5.])
 G = matrix([[ 16., 7.,  24.,  -8.,   8.,  -1.,  0., -1.,  0.,  0.,   7.,  
diff --git a/examples/doc/chap8/coneqp b/examples/doc/chap8/coneqp
index 81f9bf6..9643374 100755
--- a/examples/doc/chap8/coneqp
+++ b/examples/doc/chap8/coneqp
@@ -6,8 +6,7 @@
 # subject to x >= 0
 #            ||x||_2 <= 1
 
-from cvxopt import base, solvers
-from cvxopt.base import matrix
+from cvxopt import matrix, base, solvers
 A = matrix([ [ .3, -.4,  -.2,  -.4,  1.3 ], 
              [ .6, 1.2, -1.7,   .3,  -.3 ],
              [-.3,  .0,   .6, -1.2, -2.0 ] ])
diff --git a/examples/doc/chap8/l1 b/examples/doc/chap8/l1
index e882728..c294db3 100755
--- a/examples/doc/chap8/l1
+++ b/examples/doc/chap8/l1
@@ -2,8 +2,8 @@
 
 # The 1-norm approximation example of section 8.7.  
 
-from cvxopt import base, blas, lapack, solvers
-from cvxopt.base import matrix, spdiag, mul, div
+from cvxopt import blas, lapack, solvers
+from cvxopt import matrix, spdiag, mul, div, setseed, normal
 from math import sqrt
 
 def l1(P, q):
@@ -122,7 +122,7 @@ def l1(P, q):
         primalstart={'x': x0, 's': s0}, dualstart={'z': z0})
     return sol['x'][:n],  sol['z'][m:] - sol['z'][:m]    
 
-base.setseed()
+setseed()
 m, n = 1000, 100
-P, q = base.normal(m,n), base.normal(m,1)
+P, q = normal(m,n), normal(m,1)
 x, y = l1(P,q)
diff --git a/examples/doc/chap8/l1regls b/examples/doc/chap8/l1regls
index 41a60a6..16b173b 100755
--- a/examples/doc/chap8/l1regls
+++ b/examples/doc/chap8/l1regls
@@ -2,8 +2,8 @@
 
 # The 1-norm regularized least-squares example of section 8.7.
 
-from cvxopt.base import matrix, spdiag, mul, div
-from cvxopt import base, blas, lapack, solvers 
+from cvxopt import matrix, spdiag, mul, div, sqrt, normal, setseed
+from cvxopt import blas, lapack, solvers 
 import math
 
 def l1regls(A, y):
@@ -91,7 +91,7 @@ def l1regls(A, y):
 
         # ds is square root of diagonal of D
         ds = math.sqrt(2.0) * div( mul( W['di'][:n], W['di'][n:]), 
-            base.sqrt(d1+d2) )
+            sqrt(d1+d2) )
         d3 =  div(d2 - d1, d1 + d2)
      
         # Asc = A*diag(d)^-1/2
@@ -138,7 +138,7 @@ def l1regls(A, y):
     return solvers.coneqp(P, q, G, h, kktsolver = Fkkt)['x'][:n]
 
 m, n = 100, 1000
-base.setseed()
-A = base.normal(m,n)
-b = base.normal(m)
+setseed()
+A = normal(m,n)
+b = normal(m)
 x = l1regls(A, b)
diff --git a/examples/doc/chap8/lp b/examples/doc/chap8/lp
index 2ffef69..6e7d88d 100755
--- a/examples/doc/chap8/lp
+++ b/examples/doc/chap8/lp
@@ -2,8 +2,7 @@
 
 # The small LP of section 8.3.  
 
-from cvxopt.base import matrix  
-from cvxopt import solvers  
+from cvxopt import matrix, solvers  
 c = matrix([-4., -5.])  
 G = matrix([[2., 1., -1., 0.], [1., 2., 0., -1.]])  
 h = matrix([3., 3., 0., 0.])  
diff --git a/examples/doc/chap8/mcsdp b/examples/doc/chap8/mcsdp
index e9cdbb7..2d74ce5 100755
--- a/examples/doc/chap8/mcsdp
+++ b/examples/doc/chap8/mcsdp
@@ -2,8 +2,7 @@
 
 # The SDP example of section 8.7.
 
-from cvxopt import base, blas, lapack, solvers
-from cvxopt.base import matrix
+from cvxopt import blas, lapack, solvers, matrix, normal
 
 def mcsdp(w):
     """
@@ -122,5 +121,5 @@ def mcsdp(w):
     return sol['x'], matrix(sol['z'], (n,n))
 
 n = 100
-w = base.normal(n,n)
+w = normal(n,n)
 x, z = mcsdp(w)
diff --git a/examples/doc/chap8/portfolio b/examples/doc/chap8/portfolio
index 9f14cec..bf45cee 100755
--- a/examples/doc/chap8/portfolio
+++ b/examples/doc/chap8/portfolio
@@ -3,7 +3,7 @@
 # The risk-return trade-off of section 8.4.
 
 from math import sqrt
-from cvxopt.base import matrix
+from cvxopt import matrix
 from cvxopt.blas import dot 
 from cvxopt.solvers import qp, options 
 import pylab
diff --git a/examples/doc/chap8/qcl1 b/examples/doc/chap8/qcl1
index ce35ba8..b0d01ff 100755
--- a/examples/doc/chap8/qcl1
+++ b/examples/doc/chap8/qcl1
@@ -2,8 +2,7 @@
 
 # The quadratically constrained 1-norm minimization example of section 8.7.
 
-from cvxopt import base, blas, lapack, solvers
-from cvxopt.base import matrix, mul, div
+from cvxopt import blas, lapack, solvers, matrix, mul, div, setseed, normal
 from math import sqrt
 
 def qcl1(A, b):
@@ -128,9 +127,9 @@ def qcl1(A, b):
     else:
         return None, None
 
-base.setseed()
+setseed()
 m, n = 100, 100
-A, b = base.normal(m,n), base.normal(m,1)
+A, b = normal(m,n), normal(m,1)
 
 x, z = qcl1(A, b)
 if x is None: print "infeasible"
diff --git a/examples/doc/chap8/sdp b/examples/doc/chap8/sdp
index 64b9153..b0efddb 100755
--- a/examples/doc/chap8/sdp
+++ b/examples/doc/chap8/sdp
@@ -2,8 +2,7 @@
 
 # The small SDP of section 8.6.  
 
-from cvxopt.base import matrix  
-from cvxopt import solvers  
+from cvxopt import matrix, solvers  
 c = matrix([1.,-1.,1.])  
 G = [ matrix([[-7., -11., -11., 3.],  
               [ 7., -18., -18., 8.],  
diff --git a/examples/doc/chap8/socp b/examples/doc/chap8/socp
index eb1fd50..300f641 100755
--- a/examples/doc/chap8/socp
+++ b/examples/doc/chap8/socp
@@ -2,8 +2,7 @@
 
 # The small SOCP of section 8.5.  
 
-from cvxopt.base import matrix  
-from cvxopt import solvers  
+from cvxopt import matrix, solvers 
 c = matrix([-2., 1., 5.])  
 G = [ matrix( [[12., 13., 12.], [6., -3., -12.], [-5., -5., 6.]] ) ]  
 G += [ matrix( [[3., 3., -1., 1.], [-6., -6., -9., 19.], [10., -2., -2., -3.]] ) ]  
diff --git a/examples/doc/chap9/acent b/examples/doc/chap9/acent
index 580c582..c6ba627 100755
--- a/examples/doc/chap9/acent
+++ b/examples/doc/chap9/acent
@@ -2,9 +2,8 @@
 
 # The equality constrained analytical centering example of section 9.1.  
 
-from cvxopt import base, blas
-from cvxopt.base import matrix, spmatrix, spdiag, log
-from cvxopt import solvers
+from cvxopt import matrix, spmatrix, spdiag, log, normal, uniform
+from cvxopt import blas, solvers
 
 def acent(A, b):
      
@@ -29,17 +28,17 @@ def acent(A, b):
 # Randomly generate a feasible problem
 
 m, n = 50, 500
-y = base.normal(m,1)
+y = normal(m,1)
 
 # Random A with A'*y > 0.
-s = base.uniform(n,1)
-A = base.normal(m,n)
+s = uniform(n,1)
+A = normal(m,n)
 r = s - A.T * y
 # A = A - (1/y'*y) * y*r'
 blas.ger(y, r, A, alpha = 1.0/blas.dot(y,y)) 
 
 # Random feasible x > 0.
-x = base.uniform(n,1)
+x = uniform(n,1)
 b = A*x
 
 x = acent(A,b)
diff --git a/examples/doc/chap9/acent2 b/examples/doc/chap9/acent2
index 54f314a..281ed8b 100755
--- a/examples/doc/chap9/acent2
+++ b/examples/doc/chap9/acent2
@@ -2,7 +2,7 @@
 
 # The analytic centering with cone constraints example of section 9.1.  
 
-from cvxopt.base import matrix, log, div, spdiag  
+from cvxopt import matrix, log, div, spdiag 
 from cvxopt import solvers  
  
 def F(x = None, z = None):  
diff --git a/examples/doc/chap9/floorplan b/examples/doc/chap9/floorplan
index 7d83259..f97e1f7 100755
--- a/examples/doc/chap9/floorplan
+++ b/examples/doc/chap9/floorplan
@@ -3,8 +3,7 @@
 # The floor planning example section 9.2.  
 
 import pylab  
-from cvxopt import solvers  
-from cvxopt.base import matrix, spmatrix, mul, div  
+from cvxopt import solvers, matrix, spmatrix, mul, div  
 solvers.options['show_progress'] = False
  
 def floorplan(Amin):  
diff --git a/examples/doc/chap9/gp b/examples/doc/chap9/gp
index 229dc64..6a82d30 100755
--- a/examples/doc/chap9/gp
+++ b/examples/doc/chap9/gp
@@ -2,8 +2,7 @@
 
 # The small GP of section 9.3
 
-from cvxopt.base import matrix, log, exp  
-from cvxopt import solvers  
+from cvxopt import matrix, log, exp, solvers  
  
 Aflr  = 1000.0  
 Awall = 100.0  
diff --git a/examples/doc/chap9/l2ac b/examples/doc/chap9/l2ac
index 6ada427..2541a26 100755
--- a/examples/doc/chap9/l2ac
+++ b/examples/doc/chap9/l2ac
@@ -2,8 +2,8 @@
 
 # The example of section 9.4.  
 
-from cvxopt.base import matrix, spdiag, mul, div, log 
-from cvxopt import base, blas, lapack, solvers, base
+from cvxopt import matrix, spdiag, mul, div, log, setseed, uniform, normal 
+from cvxopt import blas, lapack, solvers 
 
 
 def l2ac(A, b):
@@ -52,11 +52,10 @@ def l2ac(A, b):
     #     D * x = bx / z[0] - A'*v.
 
     S = matrix(0.0, (m,m))
-    Asc = matrix(0.0, (m,n))
     v = matrix(0.0, (m,1))
     def Fkkt(x, z, W):
         ds = (2.0 * div(1 + x**2, (1 - x**2)**2))**-0.5
-        base.gemm(A, spdiag(ds), Asc)
+        Asc = A * spdiag(ds)
         blas.syrk(Asc, S)
         S[::m+1] += 1.0 
         lapack.potrf(S)
@@ -72,8 +71,8 @@ def l2ac(A, b):
     return solvers.cp(F, kktsolver = Fkkt)['x']
 
 m, n = 200, 2000
-base.setseed()
-A = base.normal(m,n)
-x = base.uniform(n,1)
+setseed()
+A = normal(m,n)
+x = uniform(n,1)
 b = A*x
 x = l2ac(A, b)
diff --git a/examples/doc/chap9/robls b/examples/doc/chap9/robls
index 15e9945..f33c50a 100755
--- a/examples/doc/chap9/robls
+++ b/examples/doc/chap9/robls
@@ -3,8 +3,8 @@
 # The robust least-squares example of section 9.1.  
 
 from math import sqrt, ceil, floor
-from cvxopt import base, solvers, blas, lapack
-from cvxopt.base import matrix, spmatrix, spdiag, sqrt, mul, div
+from cvxopt import solvers, blas, lapack
+from cvxopt import matrix, spmatrix, spdiag, sqrt, mul, div, setseed, normal
 import pylab
 
 def robls(A, b, rho): 
@@ -25,10 +25,10 @@ def robls(A, b, rho):
     return solvers.cp(F)['x']
 
 
-base.setseed()
+setseed()
 m, n  = 500, 100
-A = base.normal(m,n)
-b = base.normal(m,1)
+A = normal(m,n)
+b = normal(m,1)
 xh = robls(A,b,0.1)
 
 try: import pylab
diff --git a/examples/filterdemo/filterdemo_cli b/examples/filterdemo/filterdemo_cli
index 15ac64a..dd8f001 100755
--- a/examples/filterdemo/filterdemo_cli
+++ b/examples/filterdemo/filterdemo_cli
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 import getopt, sys
 
-from cvxopt.base import matrix
+from cvxopt import matrix
 from cvxopt.solvers import options
 from cvxopt.modeling import op, variable, max
 
diff --git a/examples/filterdemo/filterdemo_gui b/examples/filterdemo/filterdemo_gui
index 196f612..4beaa77 100755
--- a/examples/filterdemo/filterdemo_gui
+++ b/examples/filterdemo/filterdemo_gui
@@ -1,5 +1,5 @@
 #!/usr/bin/python
-from cvxopt.base import matrix, cos
+from cvxopt import matrix, cos
 from cvxopt.solvers import options
 from cvxopt.modeling import op, variable, max
 
@@ -80,7 +80,6 @@ class MainGUI:
         except:
             x = gtk.MessageDialog(flags=gtk.DIALOG_MODAL, \
                 type=gtk.MESSAGE_WARNING, message_format= \
-                "KKT matrix is nearly singular.\n"\
                 "Please tighten the filter specifications.");
             x.run()
             return
diff --git a/src/C/SuiteSparse/CHOLMOD/Check/cholmod_check.c b/src/C/SuiteSparse/CHOLMOD/Check/cholmod_check.c
index 13a092d..ddf24b5 100644
--- a/src/C/SuiteSparse/CHOLMOD/Check/cholmod_check.c
+++ b/src/C/SuiteSparse/CHOLMOD/Check/cholmod_check.c
@@ -197,7 +197,7 @@ static void print_value
 static int check_common
 (
     Int print,
-    char *name,
+    const char *name,
     cholmod_common *Common
 )
 {
@@ -205,8 +205,8 @@ static int check_common
     double *Xwork ;
     Int *Flag, *Head ;
     UF_long mark ;
-    Int i, nrow, nmethods, ordering, xworksize, amd_printed, init_print ;
-    char *type = "common" ;
+    Int i, nrow, nmethods, ordering, xworksize, amd_backup, init_print ;
+    const char *type = "common" ;
 
     /* ---------------------------------------------------------------------- */
     /* print control parameters and statistics */
@@ -315,6 +315,9 @@ static int check_common
     {
 	P3 ("%s", "  nmethods:   number of ordering methods to try: ") ;
 	P3 (""ID"\n", nmethods) ;
+        amd_backup = (nmethods > 1) || (nmethods == 1 &&
+            (Common->method [0].ordering == CHOLMOD_METIS ||
+             Common->method [0].ordering == CHOLMOD_NESDIS)) ;
     }
     else
     {
@@ -335,7 +338,9 @@ static int check_common
 	P3 ("%s", "    Select best ordering tried.\n") ;
 	Common->method [0].ordering = CHOLMOD_GIVEN ;
 	Common->method [1].ordering = CHOLMOD_AMD ;
-	Common->method [2].ordering = CHOLMOD_METIS ;
+	Common->method [2].ordering = 
+            (Common->default_nesdis ? CHOLMOD_NESDIS : CHOLMOD_METIS) ;
+        amd_backup = FALSE ;
 #ifndef NPARTITION
 	nmethods = 3 ;
 #else
@@ -343,7 +348,6 @@ static int check_common
 #endif
     }
 
-    amd_printed = FALSE ;
     for (i = 0 ; i < nmethods ; i++)
     {
 	P3 ("    method "ID": ", i) ;
@@ -363,12 +367,12 @@ static int check_common
 
 	    case CHOLMOD_AMD:
 		P3 ("%s", "AMD (or COLAMD if factorizing AA')\n") ;
-		amd_printed = TRUE ;
+		amd_backup = FALSE ;
 		break ;
 
 	    case CHOLMOD_COLAMD:
 		P3 ("%s", "AMD if factorizing A, COLAMD if factorizing AA')\n");
-		amd_printed = TRUE ;
+		amd_backup = FALSE ;
 		break ;
 
 	    case CHOLMOD_METIS:
@@ -433,7 +437,7 @@ static int check_common
     }
 
     /* backup AMD results, if any */
-    if (!amd_printed)
+    if (amd_backup)
     {
 	P3 ("%s", "    backup method: ") ;
 	P3 ("%s", "AMD (or COLAMD if factorizing AA')\n") ;
@@ -579,7 +583,7 @@ int CHOLMOD(check_common)
 int CHOLMOD(print_common)
 (
     /* ---- input ---- */
-    char *name,		/* printed name of Common object */
+    const char *name,		/* printed name of Common object */
     /* --------------- */
     cholmod_common *Common
 )
@@ -603,7 +607,7 @@ static UF_long check_sparse
 (
     Int *Wi,
     Int print,
-    char *name,
+    const char *name,
     cholmod_sparse *A,
     UF_long *nnzdiag,
     cholmod_common *Common
@@ -613,7 +617,7 @@ static UF_long check_sparse
     Int *Ap, *Ai, *Anz ;
     Int nrow, ncol, nzmax, sorted, packed, j, p, pend, i, nz, ilast,
 	space, init_print, dnz, count, xtype ;
-    char *type = "sparse" ;
+    const char *type = "sparse" ;
 
     /* ---------------------------------------------------------------------- */
     /* print header information */
@@ -868,7 +872,7 @@ int CHOLMOD(print_sparse)
 (
     /* ---- input ---- */
     cholmod_sparse *A,	/* sparse matrix to print */
-    char *name,		/* printed name of sparse matrix */
+    const char *name,	/* printed name of sparse matrix */
     /* --------------- */
     cholmod_common *Common
 )
@@ -889,14 +893,14 @@ int CHOLMOD(print_sparse)
 static int check_dense
 (
     Int print,
-    char *name,
+    const char *name,
     cholmod_dense *X,
     cholmod_common *Common
 )
 {
     double *Xx, *Xz ;
     Int i, j, d, nrow, ncol, nzmax, nz, init_print, count, xtype ;
-    char *type = "dense" ;
+    const char *type = "dense" ;
 
     /* ---------------------------------------------------------------------- */
     /* print header information */
@@ -1005,7 +1009,7 @@ int CHOLMOD(print_dense)
 (
     /* ---- input ---- */
     cholmod_dense *X,	/* dense matrix to print */
-    char *name,		/* printed name of dense matrix */
+    const char *name,	/* printed name of dense matrix */
     /* --------------- */
     cholmod_common *Common
 )
@@ -1038,12 +1042,12 @@ static int check_subset
     UF_long len,
     size_t n,
     Int print,
-    char *name,
+    const char *name,
     cholmod_common *Common
 )
 {
     Int i, k, init_print, count ;
-    char *type = "subset" ;
+    const char *type = "subset" ;
 
     init_print = print ;
 
@@ -1129,7 +1133,7 @@ int CHOLMOD(print_subset)
     Int *Set,		/* Set [0:len-1] is a subset of 0:n-1.  Duplicates OK */
     UF_long len,	/* size of Set (an integer array), or < 0 if 0:n-1 */
     size_t n,		/* 0:n-1 is valid range */
-    char *name,		/* printed name of Set */
+    const char *name,	/* printed name of Set */
     /* --------------- */
     cholmod_common *Common
 )
@@ -1162,7 +1166,7 @@ static int check_perm
 (
     Int *Wi,
     Int print,
-    char *name,
+    const char *name,
     Int *Perm,
     size_t len,
     size_t n,
@@ -1171,7 +1175,7 @@ static int check_perm
 {
     Int *Flag ;
     Int i, k, mark, init_print, count ;
-    char *type = "perm" ;
+    const char *type = "perm" ;
 
     /* ---------------------------------------------------------------------- */
     /* checks that take O(1) time */
@@ -1300,7 +1304,7 @@ int CHOLMOD(print_perm)
     Int *Perm,		/* Perm [0:len-1] is a permutation of subset of 0:n-1 */
     size_t len,		/* size of Perm (an integer array) */
     size_t n,		/* 0:n-1 is valid range */
-    char *name,		/* printed name of Perm */
+    const char *name,	/* printed name of Perm */
     /* --------------- */
     cholmod_common *Common
 )
@@ -1346,12 +1350,12 @@ static int check_parent
     Int *Parent,
     size_t n,
     Int print,
-    char *name,
+    const char *name,
     cholmod_common *Common
 )
 {
     Int j, p, init_print, count ;
-    char *type = "parent" ;
+    const char *type = "parent" ;
 
     init_print = print ;
 
@@ -1412,7 +1416,7 @@ int CHOLMOD(print_parent)
     /* ---- input ---- */
     Int *Parent,	/* Parent [0:n-1] is an elimination tree */
     size_t n,		/* size of Parent */
-    char *name,		/* printed name of Parent */
+    const char *name,	/* printed name of Parent */
     /* --------------- */
     cholmod_common *Common
 )
@@ -1431,7 +1435,7 @@ static int check_factor
 (
     Int *Wi,
     Int print,
-    char *name,
+    const char *name,
     cholmod_factor *L,
     cholmod_common *Common
 )
@@ -1443,7 +1447,8 @@ static int check_factor
 	count, precise, init_print, ilast, lnz, head, tail, jprev, plast,
 	jnext, examine_super, nsuper, s, k1, k2, psi, psend, psx, nsrow, nscol,
 	ps2, psxend, ssize, xsize, maxcsize, maxesize, nsrow2, jj, ii, xtype ;
-    char *type = "factor" ;
+    Int for_cholesky ;
+    const char *type = "factor" ;
 
     /* ---------------------------------------------------------------------- */
     /* print header information */
@@ -1827,7 +1832,9 @@ static int check_factor
 			Lpi [0], nsuper, Lpi [nsuper])) ;
 		ERR ("invalid: L->pi invalid") ;
 	    }
-	    if (Lpx [0] != 0 || MAX (1, Lpx [nsuper]) != xsize)
+
+            for_cholesky = (Lpx [0] != 123456) ;
+	    if (for_cholesky && (Lpx [0] != 0 || MAX (1, Lpx[nsuper]) != xsize))
 	    {
 		ERR ("invalid: L->px invalid") ;
 	    }
@@ -1839,12 +1846,16 @@ static int check_factor
 		k2 = Super [s+1] ;
 		psi = Lpi [s] ;
 		psend = Lpi [s+1] ;
-		psx = Lpx [s] ;
 		nsrow = psend - psi ;
 		nscol = k2 - k1 ;
 		nsrow2 = nsrow - nscol ;
 		ps2 = psi + nscol ;
-		psxend = Lpx [s+1] ;
+
+                if (for_cholesky)
+                {
+                    psx = Lpx [s] ;
+                    psxend = Lpx [s+1] ;
+                }
 
 		ETC (s == nsuper-1, count, 4) ;
 
@@ -1852,11 +1863,15 @@ static int check_factor
 		P4 ("col "ID" ", k1) ;
 		P4 ("to "ID". ", k2-1) ;
 		P4 ("nz in first col: "ID".\n", nsrow) ;
-		P4 ("  values start "ID", ", psx) ;
-		P4 ("end "ID"\n", psxend) ;
+
+                if (for_cholesky)
+                {
+                    P4 ("  values start "ID", ", psx) ;
+                    P4 ("end "ID"\n", psxend) ;
+                }
 
 		if (k1 > k2 || k1 < 0 || k2 > n || nsrow < nscol || nsrow2 < 0
-		    || psxend - psx != nsrow * nscol)
+                    || (for_cholesky && psxend - psx != nsrow * nscol))
 		{
 		    ERR ("invalid supernode") ;
 		}
@@ -1963,7 +1978,7 @@ int CHOLMOD(print_factor)
 (
     /* ---- input ---- */
     cholmod_factor *L,	/* factor to print */
-    char *name,		/* printed name of factor */
+    const char *name,	/* printed name of factor */
     /* --------------- */
     cholmod_common *Common
 )
@@ -1983,7 +1998,7 @@ int CHOLMOD(print_factor)
 static int check_triplet
 (
     Int print,
-    char *name,
+    const char *name,
     cholmod_triplet *T,
     cholmod_common *Common
 )
@@ -1991,7 +2006,7 @@ static int check_triplet
     double *Tx, *Tz ;
     Int *Ti, *Tj ;
     Int i, j, p, nrow, ncol, nzmax, nz, xtype, init_print, count ;
-    char *type = "triplet" ;
+    const char *type = "triplet" ;
 
     /* ---------------------------------------------------------------------- */
     /* print header information */
@@ -2150,7 +2165,7 @@ int CHOLMOD(print_triplet)
 (
     /* ---- input ---- */
     cholmod_triplet *T,	/* triplet matrix to print */
-    char *name,		/* printed name of triplet matrix */
+    const char *name,	/* printed name of triplet matrix */
     /* --------------- */
     cholmod_common *Common
 )
@@ -2178,7 +2193,7 @@ int CHOLMOD(dump_malloc) = -1 ;
 /* === cholmod_dump_init ==================================================== */
 /* ========================================================================== */
 
-void CHOLMOD(dump_init) (char *s, cholmod_common *Common)
+void CHOLMOD(dump_init) (const char *s, cholmod_common *Common)
 {
     FILE *f ;
     f = fopen ("debug", "r") ;
@@ -2202,7 +2217,7 @@ void CHOLMOD(dump_init) (char *s, cholmod_common *Common)
 UF_long CHOLMOD(dump_sparse)	/* returns nnz (diag (A)) or EMPTY if error */
 (
     cholmod_sparse *A,
-    char *name,
+    const char *name,
     cholmod_common *Common
 )
 {
@@ -2232,7 +2247,7 @@ UF_long CHOLMOD(dump_sparse)	/* returns nnz (diag (A)) or EMPTY if error */
 int CHOLMOD(dump_factor)
 (
     cholmod_factor *L,
-    char *name,
+    const char *name,
     cholmod_common *Common
 )
 {
@@ -2262,7 +2277,7 @@ int CHOLMOD(dump_perm)
     Int *Perm,
     size_t len,
     size_t n,
-    char *name,
+    const char *name,
     cholmod_common *Common
 )
 {
@@ -2289,7 +2304,7 @@ int CHOLMOD(dump_perm)
 int CHOLMOD(dump_dense)
 (
     cholmod_dense *X,
-    char *name,
+    const char *name,
     cholmod_common *Common
 )
 {
@@ -2310,7 +2325,7 @@ int CHOLMOD(dump_dense)
 int CHOLMOD(dump_triplet)
 (
     cholmod_triplet *T,
-    char *name,
+    const char *name,
     cholmod_common *Common
 )
 {
@@ -2333,7 +2348,7 @@ int CHOLMOD(dump_subset)
     Int *S,
     size_t len,
     size_t n,
-    char *name,
+    const char *name,
     cholmod_common *Common
 )
 {
@@ -2355,7 +2370,7 @@ int CHOLMOD(dump_parent)
 (
     Int *Parent,
     size_t n,
-    char *name,
+    const char *name,
     cholmod_common *Common
 )
 {
@@ -2375,7 +2390,8 @@ int CHOLMOD(dump_parent)
 
 void CHOLMOD(dump_real)
 (
-    char *name, Real *X, UF_long nrow, UF_long ncol, int lower, int xentry,
+    const char *name,
+    Real *X, UF_long nrow, UF_long ncol, int lower, int xentry,
     cholmod_common *Common
 )
 {
@@ -2466,7 +2482,12 @@ void CHOLMOD(dump_super)
 /* === cholmod_dump_mem ===================================================== */
 /* ========================================================================== */
 
-int CHOLMOD(dump_mem) (char *where, UF_long should, cholmod_common *Common)
+int CHOLMOD(dump_mem)
+(
+    const char *where,
+    UF_long should,
+    cholmod_common *Common
+)
 {
     UF_long diff = should - Common->memory_inuse ;
     if (diff != 0)
diff --git a/src/C/SuiteSparse/CHOLMOD/Check/cholmod_write.c b/src/C/SuiteSparse/CHOLMOD/Check/cholmod_write.c
index 0c2d83f..eb578f9 100644
--- a/src/C/SuiteSparse/CHOLMOD/Check/cholmod_write.c
+++ b/src/C/SuiteSparse/CHOLMOD/Check/cholmod_write.c
@@ -40,7 +40,7 @@
  * otherwise.
  */
 
-static int include_comments (FILE *f, char *comments)
+static int include_comments (FILE *f, const char *comments)
 {
     FILE *cf = NULL ;
     char buffer [MAXLINE] ;
@@ -354,7 +354,7 @@ int CHOLMOD(write_sparse)
     FILE *f,		    /* file to write to, must already be open */
     cholmod_sparse *A,	    /* matrix to print */
     cholmod_sparse *Z,	    /* optional matrix with pattern of explicit zeros */
-    char *comments,	    /* optional filename of comments to include */
+    const char *comments,    /* optional filename of comments to include */
     /* --------------- */
     cholmod_common *Common
 )
@@ -657,7 +657,7 @@ int CHOLMOD(write_dense)
     /* ---- input ---- */
     FILE *f,		    /* file to write to, must already be open */
     cholmod_dense *X,	    /* matrix to print */
-    char *comments,	    /* optional filename of comments to include */
+    const char *comments,    /* optional filename of comments to include */
     /* --------------- */
     cholmod_common *Common
 )
diff --git a/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_analyze.c b/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_analyze.c
index 19ce5b4..086d139 100644
--- a/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_analyze.c
+++ b/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_analyze.c
@@ -139,7 +139,31 @@ cholmod_factor *CHOLMOD(analyze)
     cholmod_common *Common
 )
 {
-    return (CHOLMOD(analyze_p) (A, NULL, NULL, 0, Common)) ;
+    return (CHOLMOD(analyze_p2) (TRUE, A, NULL, NULL, 0, Common)) ;
+}
+
+
+/* ========================================================================== */
+/* === cholmod_analyze_p ==================================================== */
+/* ========================================================================== */
+
+/* Orders and analyzes A, AA', PAP', PAA'P', FF', or PFF'P and returns a
+ * symbolic factor that can later be passed to cholmod_factorize, where
+ * F = A(:,fset) if fset is not NULL and A->stype is zero.
+ * UserPerm is tried if non-NULL.  */
+
+cholmod_factor *CHOLMOD(analyze_p)
+(
+    /* ---- input ---- */
+    cholmod_sparse *A,	/* matrix to order and analyze */
+    Int *UserPerm,	/* user-provided permutation, size A->nrow */
+    Int *fset,		/* subset of 0:(A->ncol)-1 */
+    size_t fsize,	/* size of fset */
+    /* --------------- */
+    cholmod_common *Common
+)
+{
+    return (CHOLMOD(analyze_p2) (TRUE, A, UserPerm, fset, fsize, Common)) ;
 }
 
 
@@ -302,7 +326,7 @@ int CHOLMOD(analyze_ordering)
     Int *Post,		/* size n, postordering of elimination tree */
     Int *ColCount,	/* size n, nnz in each column of L */
     /* ---- workspace  */
-    Int *First,		/* size nworkspace for cholmod_postorder */
+    Int *First,		/* size n workspace for cholmod_postorder */
     Int *Level,		/* size n workspace for cholmod_postorder */
     /* --------------- */
     cholmod_common *Common
@@ -373,17 +397,17 @@ int CHOLMOD(analyze_ordering)
 
 
 /* ========================================================================== */
-/* === cholmod_analyze_p ==================================================== */
+/* === cholmod_analyze_p2 =================================================== */
 /* ========================================================================== */
 
-/* Orders and analyzes A, AA', PAP', PAA'P', FF', or PFF'P and returns a
- * symbolic factor that can later be passed to cholmod_factorize, where
- * F = A(:,fset) if fset is not NULL and A->stype is zero.
- * UserPerm is tried if non-NULL.  */
+/* Ordering and analysis for sparse Cholesky or sparse QR.  CHOLMOD itself
+ * always uses for_cholesky = TRUE.  The for_cholesky = FALSE option is
+ * for SuiteSparseQR only. */
 
-cholmod_factor *CHOLMOD(analyze_p)
+cholmod_factor *CHOLMOD(analyze_p2)
 (
     /* ---- input ---- */
+    int for_cholesky,   /* if TRUE, then analyze for Cholesky; else for QR */
     cholmod_sparse *A,	/* matrix to order and analyze */
     Int *UserPerm,	/* user-provided permutation, size A->nrow */
     Int *fset,		/* subset of 0:(A->ncol)-1 */
@@ -398,6 +422,7 @@ cholmod_factor *CHOLMOD(analyze_p)
     cholmod_factor *L ;
     Int k, n, ordering, method, nmethods, status, default_strategy, ncol, uncol,
 	skip_analysis, skip_best ;
+    Int amd_backup ;
     size_t s ;
     int ok = TRUE ;
 
@@ -434,22 +459,30 @@ cholmod_factor *CHOLMOD(analyze_p)
     default_strategy = (nmethods == 0) ;
     if (default_strategy)
     {
-	/* default strategy: try UserPerm, if given.  Try AMD for A, or COLAMD
+	/* default strategy: try UserPerm, if given.  Try AMD for A, or AMD
 	 * to order A*A'.  Try METIS for the symmetric case only if AMD reports
-	 * a high degree of fill-in and flop count.  Always try METIS for the
-	 * unsymmetric case.  METIS is not tried if the Partition Module
-	 * isn't installed.   If Common->default_nesdis is TRUE, then NESDIS
-	 * is used as the 3rd ordering instead. */
+         * a high degree of fill-in and flop count.  METIS is not tried if the
+         * Partition Module isn't installed.   If Common->default_nesdis is
+         * TRUE, then NESDIS is used as the 3rd ordering instead. */
 	Common->method [0].ordering = CHOLMOD_GIVEN ;/* skip if UserPerm NULL */
 	Common->method [1].ordering = CHOLMOD_AMD ;
 	Common->method [2].ordering = 
 	    (Common->default_nesdis ? CHOLMOD_NESDIS : CHOLMOD_METIS) ;
+        amd_backup = FALSE ;
 #ifndef NPARTITION
 	nmethods = 3 ;
 #else
 	nmethods = 2 ;
 #endif
     }
+    else
+    {
+        /* If only METIS and NESDIS are selected, or if 2 or more methods are
+         * being tried, then enable AMD backup */
+        amd_backup = (nmethods > 1) || (nmethods == 1 &&
+            (Common->method [0].ordering == CHOLMOD_METIS ||
+             Common->method [0].ordering == CHOLMOD_NESDIS)) ;
+    }
 
 #ifdef NSUPERNODAL
     /* CHOLMOD Supernodal module not installed, just do simplicial analysis */
@@ -543,7 +576,7 @@ cholmod_factor *CHOLMOD(analyze_p)
 	if (method == nmethods)
 	{
 	    /* All methods failed: backup to AMD */
-	    if (Common->selected == EMPTY && !default_strategy)
+	    if (Common->selected == EMPTY && amd_backup)
 	    {
 		PRINT1 (("All methods requested failed: backup to AMD\n")) ;
 		ordering = CHOLMOD_AMD ;
@@ -604,6 +637,7 @@ cholmod_factor *CHOLMOD(analyze_p)
 	    /* AMD ordering of A, A*A', or A(:,f)*A(:,f)' */
 	    /* -------------------------------------------------------------- */
 
+            amd_backup = FALSE ;    /* no need to try AMD twice ... */
 	    CHOLMOD(amd) (A, fset, fsize, Perm, Common) ;
 	    skip_analysis = TRUE ;
 
@@ -759,7 +793,7 @@ cholmod_factor *CHOLMOD(analyze_p)
 		 * better (this heuristic is based on tests on all symmetric
 		 * positive definite matrices in the UF sparse matrix
 		 * collection, and it works well across a wide range of
-		 * problems).  METIS can take much more time and AMD. */
+		 * problems).  METIS can take much more time than AMD. */
 		break ;
 	    }
 	}
@@ -879,7 +913,7 @@ cholmod_factor *CHOLMOD(analyze_p)
 		&A1, &A2, &S, &F, Common) ;
 
 	/* workspace: Flag (nrow), Head (nrow), Iwork (5*nrow) */
-	CHOLMOD(super_symbolic) (S, F, Lparent, L, Common) ;
+	CHOLMOD(super_symbolic2) (for_cholesky, S, F, Lparent, L, Common) ;
 	PRINT1 (("status %d\n", Common->status)) ;
 
 	CHOLMOD(free_sparse) (&A1, Common) ;
diff --git a/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_postorder.c b/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_postorder.c
index d9b44c5..8629ad4 100644
--- a/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_postorder.c
+++ b/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_postorder.c
@@ -129,9 +129,9 @@ static Int dfs		/* return the new value of k */
  * permutation if Parent is a component tree.
  *
  * An optional node weight can be given.  When starting a postorder at node j,
- * the children of j are ordered in decreasing order of their weight.
+ * the children of j are ordered in increasing order of their weight.
  * If no weights are given (Weight is NULL) then children are ordered in
- * decreasing order of their node number.  The weight of a node must be in the
+ * increasing order of their node number.  The weight of a node must be in the
  * range 0 to n-1.  Weights outside that range are silently converted to that
  * range (weights < 0 are treated as zero, and weights >= n are treated as n-1).
  *
diff --git a/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_solve.c b/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_solve.c
index b51fc80..05473a3 100644
--- a/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_solve.c
+++ b/src/C/SuiteSparse/CHOLMOD/Cholesky/cholmod_solve.c
@@ -1062,11 +1062,10 @@ cholmod_dense *CHOLMOD(solve)
 	/* ------------------------------------------------------------------ */
 
 #ifndef NSUPERNODAL
-	Int blas_ok = TRUE ;
-
 	/* allocate workspace */
 	cholmod_dense *E ;
 	Int dual ;
+        Common->blas_ok = TRUE ;
 	dual = (L->xtype == CHOLMOD_REAL && B->xtype != CHOLMOD_REAL) ? 2 : 1 ;
 	Y = CHOLMOD(allocate_dense) (n, dual*nrhs, n, L->xtype, Common) ;
 	E = CHOLMOD(allocate_dense) (dual*nrhs, L->maxesize, dual*nrhs,
@@ -1085,23 +1084,22 @@ cholmod_dense *CHOLMOD(solve)
 
 	if (sys == CHOLMOD_A || sys == CHOLMOD_LDLt)
 	{
-	    blas_ok = CHOLMOD(super_lsolve) (L, Y, E, Common) ;	   /* Y = L\Y */
-	    blas_ok = blas_ok &&
-		CHOLMOD(super_ltsolve) (L, Y, E, Common) ;	   /* Y = L'\Y*/
+	    CHOLMOD(super_lsolve) (L, Y, E, Common) ;	    /* Y = L\Y */
+            CHOLMOD(super_ltsolve) (L, Y, E, Common) ;	    /* Y = L'\Y*/
 	}
 	else if (sys == CHOLMOD_L || sys == CHOLMOD_LD)
 	{
-	    blas_ok = CHOLMOD(super_lsolve) (L, Y, E, Common) ;	   /* Y = L\Y */
+	    CHOLMOD(super_lsolve) (L, Y, E, Common) ;	    /* Y = L\Y */
 	}
 	else if (sys == CHOLMOD_Lt || sys == CHOLMOD_DLt)
 	{
-	    blas_ok = CHOLMOD(super_ltsolve) (L, Y, E, Common) ;   /* Y = L'\Y*/
+	    CHOLMOD(super_ltsolve) (L, Y, E, Common) ;      /* Y = L'\Y*/
 	}
 	CHOLMOD(free_dense) (&E, Common) ;
 
 	iperm (Y, Perm, 0, nrhs, X) ;			    /* X = P'*Y */
 
-	if (CHECK_BLAS_INT && !blas_ok)
+	if (CHECK_BLAS_INT && !Common->blas_ok)
 	{
 	    /* Integer overflow in the BLAS.  This is probably impossible,
 	     * since the BLAS were used to create the supernodal factorization.
diff --git a/src/C/SuiteSparse/CHOLMOD/Core/cholmod_common.c b/src/C/SuiteSparse/CHOLMOD/Core/cholmod_common.c
index 9b00b8d..c72e228 100644
--- a/src/C/SuiteSparse/CHOLMOD/Core/cholmod_common.c
+++ b/src/C/SuiteSparse/CHOLMOD/Core/cholmod_common.c
@@ -57,6 +57,8 @@ int CHOLMOD(start)
     cholmod_common *Common
 )
 {
+    int k ;
+
     if (Common == NULL)
     {
 	return (FALSE) ;
@@ -167,6 +169,19 @@ int CHOLMOD(start)
     /* Common->called_nd is TRUE if cholmod_analyze called or NESDIS */
     Common->called_nd = FALSE ;
 
+    Common->blas_ok = TRUE ;    /* false if BLAS int overflow occurs */
+
+    /* ---------------------------------------------------------------------- */
+    /* default SuiteSparseQR knobs and statististics */
+    /* ---------------------------------------------------------------------- */
+
+    for (k = 0 ; k < 2  ; k++) Common->SPQR_xstat [k] = 0 ;
+    for (k = 0 ; k < 10 ; k++) Common->SPQR_istat [k] = 0 ;
+    Common->SPQR_grain = 1 ;    /* no Intel TBB multitasking, by default */
+    Common->SPQR_small = 1e6 ;  /* target min task size for TBB */
+    Common->SPQR_shrink = 1 ;   /* controls SPQR shrink realloc */
+    Common->SPQR_nthreads = 0 ; /* 0: let TBB decide how many threads to use */
+
     DEBUG_INIT ("cholmod start", Common) ;
     return (TRUE) ;
 }
diff --git a/src/C/SuiteSparse/CHOLMOD/Core/cholmod_complex.c b/src/C/SuiteSparse/CHOLMOD/Core/cholmod_complex.c
index 8bef584..6217856 100644
--- a/src/C/SuiteSparse/CHOLMOD/Core/cholmod_complex.c
+++ b/src/C/SuiteSparse/CHOLMOD/Core/cholmod_complex.c
@@ -36,6 +36,9 @@
  *
  * s = hypot (x,y) computes s = sqrt (x*x + y*y) but does so more accurately.
  * The NaN cases for the double relops x >= y and x+y == x are safely ignored.
+ * 
+ * Source: Algorithm 312, "Absolute value and square root of a complex number,"
+ * P. Friedland, Comm. ACM, vol 10, no 10, October 1967, page 665.
  */
 
 double CHOLMOD(hypot) (double x, double y)
diff --git a/src/C/SuiteSparse/CHOLMOD/Core/cholmod_error.c b/src/C/SuiteSparse/CHOLMOD/Core/cholmod_error.c
index b1e5d4c..193598c 100644
--- a/src/C/SuiteSparse/CHOLMOD/Core/cholmod_error.c
+++ b/src/C/SuiteSparse/CHOLMOD/Core/cholmod_error.c
@@ -36,9 +36,9 @@ int CHOLMOD(error)
 (
     /* ---- input ---- */
     int status,		/* error status */
-    char *file,		/* name of source code file where error occured */ 
+    const char *file,	/* name of source code file where error occured */ 
     int line,		/* line number in source code file where error occured*/
-    char *message,	/* error message */
+    const char *message,    /* error message */
     /* --------------- */
     cholmod_common *Common
 )
diff --git a/src/C/SuiteSparse/CHOLMOD/Core/cholmod_memory.c b/src/C/SuiteSparse/CHOLMOD/Core/cholmod_memory.c
index f6a1f00..92f9f8e 100644
--- a/src/C/SuiteSparse/CHOLMOD/Core/cholmod_memory.c
+++ b/src/C/SuiteSparse/CHOLMOD/Core/cholmod_memory.c
@@ -262,7 +262,7 @@ void *CHOLMOD(calloc)	/* returns pointer to the newly calloc'd block */
 	    Common->memory_inuse += (n * size) ;
 	    Common->memory_usage =
 		MAX (Common->memory_usage, Common->memory_inuse) ;
-	    PRINTM (("cholmod_calloc %p %d cnt: %d inuse %d\n",
+	    PRINTM (("cholmod_malloc %p %d cnt: %d inuse %d\n",
 		    p, n*size, Common->malloc_count, Common->memory_inuse)) ;
 	}
     }
@@ -352,8 +352,8 @@ void *CHOLMOD(realloc)	/* returns pointer to reallocated block */
 		 * CHOLMOD's notion of the size of the block, however. */
 		*n = nnew ;
 		PRINTM (("nnew <= nold failed, pretend to succeed\n")) ;
-		PRINTM (("cholmod_realloc_old: %p %d cnt: %d inuse %d\n"
-			 "cholmod_realloc_new: %p %d cnt: %d inuse %d\n",
+		PRINTM (("cholmod_free %p %d cnt: %d inuse %d\n"
+			 "cholmod_malloc %p %d cnt: %d inuse %d\n",
 		    p, nold*size, Common->malloc_count-1,
 				  Common->memory_inuse - nold*size,
 		    p, nnew*size, Common->malloc_count,
@@ -370,8 +370,8 @@ void *CHOLMOD(realloc)	/* returns pointer to reallocated block */
 	else
 	{
 	    /* success: return revised p and change the size of the block */
-	    PRINTM (("cholmod_realloc_old: %p %d cnt: %d inuse %d\n"
-		     "cholmod_realloc_new: %p %d cnt: %d inuse %d\n",
+	    PRINTM (("cholmod_free %p %d cnt: %d inuse %d\n"
+		     "cholmod_malloc %p %d cnt: %d inuse %d\n",
 		p, nold*size,    Common->malloc_count-1,
 				 Common->memory_inuse - nold*size,
 		pnew, nnew*size, Common->malloc_count,
diff --git a/src/C/SuiteSparse/CHOLMOD/Doc/ChangeLog b/src/C/SuiteSparse/CHOLMOD/Doc/ChangeLog
index 487ba9a..9d1c2e6 100644
--- a/src/C/SuiteSparse/CHOLMOD/Doc/ChangeLog
+++ b/src/C/SuiteSparse/CHOLMOD/Doc/ChangeLog
@@ -1,3 +1,20 @@
+Sept 20, 2008, version 1.7.0
+
+    * update for SuiteSparseQR:
+
+	- add SuiteSparseQR knobs and stats to CHOLMOD Common
+	- SuiteSparseQR uses CHOLMOD for its multifrontal symbolic analysis
+	- changed (char *) input parameters to (const char *), to avoid
+	    spurious compiler warnings when C++ calls C.
+	- minor change to the AMD backup strategy in cholmod_analyze, to better
+	    suit SuiteSparseQR
+	- modified cholmod_analyze and cholmod_super_symbolic,
+	    to add "for_cholesky" parameter; new function cholmod_analyze_p2.
+	    This parameter is false for SuiteSparseQR.
+	- minor correction to comments in cholmod_postorder.c
+	- performance enhancement to supernodal symbolic analysis, when
+	    A or AA' is fairly dense
+
 Nov 1, 2007, version 1.6.0
 
     * minor lint cleanup (no bugs)
diff --git a/src/C/SuiteSparse/CHOLMOD/Doc/UserGuide.tex b/src/C/SuiteSparse/CHOLMOD/Doc/UserGuide.tex
index 8a0d72e..20e93aa 100644
--- a/src/C/SuiteSparse/CHOLMOD/Doc/UserGuide.tex
+++ b/src/C/SuiteSparse/CHOLMOD/Doc/UserGuide.tex
@@ -21,7 +21,7 @@ Dept. of Computer and Information Science and Engineering \\
 Univ. of Florida, Gainesville, FL}
 \title{User Guide for CHOLMOD: a sparse Cholesky factorization and
 modification package}
-\date{Version 1.6, Nov 1, 2007}
+\date{Version 1.7, Sept 20, 2008}
 \maketitle
 
 %-------------------------------------------------------------------------------
@@ -43,7 +43,7 @@ modification package}
 \end{abstract}
 %-------------------------------------------------------------------------------
 
-CHOLMOD Copyright\copyright 2005-2006 by Timothy A. Davis.  Portions are also
+CHOLMOD Copyright\copyright 2005-2008 by Timothy A. Davis.  Portions are also
 copyrighted by William W. Hager (the {\tt Modify} Module),
 and the University of Florida (the {\tt Partition} and {\tt Core} Modules).
 All Rights Reserved.  Some of CHOLMOD's Modules are distributed under the GNU
diff --git a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_blas.h b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_blas.h
index 7957968..4d9d5e9 100644
--- a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_blas.h
+++ b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_blas.h
@@ -35,7 +35,8 @@
 #elif defined (_AIX) || defined (MIBM_RS) || defined (ARCH_IBM_RS)
 #define CHOLMOD_AIX
 #define CHOLMOD_ARCHITECTURE "IBM AIX"
-#define BLAS_NO_UNDERSCORE
+/* recent reports from IBM AIX seem to indicate that this is not needed: */
+/* #define BLAS_NO_UNDERSCORE */
 
 #elif defined (__alpha) || defined (MALPHA) || defined (ARCH_ALPHA)
 #define CHOLMOD_ALPHA
@@ -160,9 +161,10 @@
 #endif
 
 /* If the BLAS integer is smaller than the basic CHOLMOD integer, then we need
- * to check for integer overflow when converting from one to the other.  If
- * any integer overflows, the externally-defined blas_ok variable is set to
- * FALSE.  blas_ok should be set to TRUE before calling any BLAS_* macro.
+ * to check for integer overflow when converting from Int to BLAS_INT.  If
+ * any integer overflows, the externally-defined Common->blas_ok variable is
+ * set to FALSE.  Common->blas_ok should be set to TRUE before calling any
+ * BLAS_* macro.
  */
 
 #define CHECK_BLAS_INT (sizeof (BLAS_INT) < sizeof (Int))
@@ -179,12 +181,12 @@ void BLAS_DGEMV (char *trans, BLAS_INT *m, BLAS_INT *n, double *alpha,
 #define BLAS_dgemv(trans,m,n,alpha,A,lda,X,incx,beta,Y,incy) \
 { \
     BLAS_INT M = m, N = n, LDA = lda, INCX = incx, INCY = incy ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \
+        EQ (INCX,incx) && EQ (INCY,incy))) \
     { \
-	blas_ok &= EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && EQ (INCX,incx) \
-		&& EQ (INCY,incy) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_DGEMV (trans, &M, &N, alpha, A, &LDA, X, &INCX, beta, Y, &INCY) ; \
     } \
@@ -197,12 +199,12 @@ void BLAS_ZGEMV (char *trans, BLAS_INT *m, BLAS_INT *n, double *alpha,
 #define BLAS_zgemv(trans,m,n,alpha,A,lda,X,incx,beta,Y,incy) \
 { \
     BLAS_INT M = m, N = n, LDA = lda, INCX = incx, INCY = incy ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \
+        EQ (INCX,incx) && EQ (INCY,incy))) \
     { \
-	blas_ok &= EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && EQ (INCX,incx) \
-		&& EQ (INCY,incy) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_ZGEMV (trans, &M, &N, alpha, A, &LDA, X, &INCX, beta, Y, &INCY) ; \
     } \
@@ -214,11 +216,11 @@ void BLAS_DTRSV (char *uplo, char *trans, char *diag, BLAS_INT *n, double *A,
 #define BLAS_dtrsv(uplo,trans,diag,n,A,lda,X,incx) \
 { \
     BLAS_INT N = n, LDA = lda, INCX = incx ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (LDA,lda) && EQ (INCX,incx))) \
     { \
-	blas_ok &= EQ (N,n) && EQ (LDA,lda) && EQ (INCX,incx) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_DTRSV (uplo, trans, diag, &N, A, &LDA, X, &INCX) ; \
     } \
@@ -230,11 +232,11 @@ void BLAS_ZTRSV (char *uplo, char *trans, char *diag, BLAS_INT *n, double *A,
 #define BLAS_ztrsv(uplo,trans,diag,n,A,lda,X,incx) \
 { \
     BLAS_INT N = n, LDA = lda, INCX = incx ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (LDA,lda) && EQ (INCX,incx))) \
     { \
-	blas_ok &= EQ (N,n) && EQ (LDA,lda) && EQ (INCX,incx) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_ZTRSV (uplo, trans, diag, &N, A, &LDA, X, &INCX) ; \
     } \
@@ -247,11 +249,12 @@ void BLAS_DTRSM (char *side, char *uplo, char *transa, char *diag, BLAS_INT *m,
 #define BLAS_dtrsm(side,uplo,transa,diag,m,n,alpha,A,lda,B,ldb) \
 { \
     BLAS_INT M = m, N = n, LDA = lda, LDB = ldb ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \
+        EQ (LDB,ldb))) \
     { \
-	blas_ok &= EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && EQ (LDB,ldb) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_DTRSM (side, uplo, transa, diag, &M, &N, alpha, A, &LDA, B, &LDB);\
     } \
@@ -264,11 +267,12 @@ void BLAS_ZTRSM (char *side, char *uplo, char *transa, char *diag, BLAS_INT *m,
 #define BLAS_ztrsm(side,uplo,transa,diag,m,n,alpha,A,lda,B,ldb) \
 { \
     BLAS_INT M = m, N = n, LDA = lda, LDB = ldb ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \
+        EQ (LDB,ldb))) \
     { \
-	blas_ok &= EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && EQ (LDB,ldb) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_ZTRSM (side, uplo, transa, diag, &M, &N, alpha, A, &LDA, B, &LDB);\
     } \
@@ -281,12 +285,12 @@ void BLAS_DGEMM (char *transa, char *transb, BLAS_INT *m, BLAS_INT *n,
 #define BLAS_dgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta,C,ldc) \
 { \
     BLAS_INT M = m, N = n, K = k, LDA = lda, LDB = ldb, LDC = ldc ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (K,k) && \
+        EQ (LDA,lda) && EQ (LDB,ldb) && EQ (LDC,ldc))) \
     { \
-	blas_ok &= EQ (M,m) && EQ (N,n) && EQ (K,k) && EQ (LDA,lda) \
-		&& EQ (LDB,ldb) && EQ (LDC,ldc) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_DGEMM (transa, transb, &M, &N, &K, alpha, A, &LDA, B, &LDB, beta, \
 	    C, &LDC) ; \
@@ -300,12 +304,12 @@ void BLAS_ZGEMM (char *transa, char *transb, BLAS_INT *m, BLAS_INT *n,
 #define BLAS_zgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta,C,ldc) \
 { \
     BLAS_INT M = m, N = n, K = k, LDA = lda, LDB = ldb, LDC = ldc ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (K,k) && \
+        EQ (LDA,lda) && EQ (LDB,ldb) && EQ (LDC,ldc))) \
     { \
-	blas_ok &= EQ (M,m) && EQ (N,n) && EQ (K,k) && EQ (LDA,lda) \
-		&& EQ (LDB,ldb) && EQ (LDC,ldc) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_ZGEMM (transa, transb, &M, &N, &K, alpha, A, &LDA, B, &LDB, beta, \
 	    C, &LDC) ; \
@@ -319,11 +323,12 @@ void BLAS_DSYRK (char *uplo, char *trans, BLAS_INT *n, BLAS_INT *k,
 #define BLAS_dsyrk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc) \
 { \
     BLAS_INT N = n, K = k, LDA = lda, LDC = ldc ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (K,k) && EQ (LDA,lda) && \
+        EQ (LDC,ldc))) \
     { \
-	blas_ok &= EQ (N,n) && EQ (K,k) && EQ (LDA,lda) && EQ (LDC,ldc) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_DSYRK (uplo, trans, &N, &K, alpha, A, &LDA, beta, C, &LDC) ; \
     } \
@@ -336,11 +341,12 @@ void BLAS_ZHERK (char *uplo, char *trans, BLAS_INT *n, BLAS_INT *k,
 #define BLAS_zherk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc) \
 { \
     BLAS_INT N = n, K = k, LDA = lda, LDC = ldc ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (K,k) && EQ (LDA,lda) && \
+        EQ (LDC,ldc))) \
     { \
-	blas_ok &= EQ (N,n) && EQ (K,k) && EQ (LDA,lda) && EQ (LDC,ldc) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_ZHERK (uplo, trans, &N, &K, alpha, A, &LDA, beta, C, &LDC) ; \
     } \
@@ -352,11 +358,11 @@ void LAPACK_DPOTRF (char *uplo, BLAS_INT *n, double *A, BLAS_INT *lda,
 #define LAPACK_dpotrf(uplo,n,A,lda,info) \
 { \
     BLAS_INT N = n, LDA = lda, INFO = 1 ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (LDA,lda))) \
     { \
-	blas_ok &= EQ (N,n) && EQ (LDA,lda) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	LAPACK_DPOTRF (uplo, &N, A, &LDA, &INFO) ; \
     } \
@@ -369,11 +375,11 @@ void LAPACK_ZPOTRF (char *uplo, BLAS_INT *n, double *A, BLAS_INT *lda,
 #define LAPACK_zpotrf(uplo,n,A,lda,info) \
 { \
     BLAS_INT N = n, LDA = lda, INFO = 1 ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (LDA,lda))) \
     { \
-	blas_ok &= EQ (N,n) && EQ (LDA,lda) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	LAPACK_ZPOTRF (uplo, &N, A, &LDA, &INFO) ; \
     } \
@@ -387,11 +393,11 @@ void BLAS_DSCAL (BLAS_INT *n, double *alpha, double *Y, BLAS_INT *incy) ;
 #define BLAS_dscal(n,alpha,Y,incy) \
 { \
     BLAS_INT N = n, INCY = incy ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (INCY,incy))) \
     { \
-	blas_ok &= EQ (N,n) && EQ (INCY,incy) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_DSCAL (&N, alpha, Y, &INCY) ; \
     } \
@@ -402,11 +408,11 @@ void BLAS_ZSCAL (BLAS_INT *n, double *alpha, double *Y, BLAS_INT *incy) ;
 #define BLAS_zscal(n,alpha,Y,incy) \
 { \
     BLAS_INT N = n, INCY = incy ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (INCY,incy))) \
     { \
-	blas_ok &= EQ (N,n) && EQ (INCY,incy) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_ZSCAL (&N, alpha, Y, &INCY) ; \
     } \
@@ -419,12 +425,12 @@ void BLAS_DGER (BLAS_INT *m, BLAS_INT *n, double *alpha,
 #define BLAS_dger(m,n,alpha,X,incx,Y,incy,A,lda) \
 { \
     BLAS_INT M = m, N = n, LDA = lda, INCX = incx, INCY = incy ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \
+          EQ (INCX,incx) && EQ (INCY,incy))) \
     { \
-	blas_ok &= EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && EQ (INCX,incx) \
-		&& EQ (INCY,incy) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_DGER (&M, &N, alpha, X, &INCX, Y, &INCY, A, &LDA) ; \
     } \
@@ -437,12 +443,12 @@ void BLAS_ZGER (BLAS_INT *m, BLAS_INT *n, double *alpha,
 #define BLAS_zgeru(m,n,alpha,X,incx,Y,incy,A,lda) \
 { \
     BLAS_INT M = m, N = n, LDA = lda, INCX = incx, INCY = incy ; \
-    if (CHECK_BLAS_INT) \
+    if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \
+          EQ (INCX,incx) && EQ (INCY,incy))) \
     { \
-	blas_ok &= EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && EQ (INCX,incx) \
-		&& EQ (INCY,incy) ; \
+	Common->blas_ok = FALSE ; \
     } \
-    if (blas_ok) \
+    if (!CHECK_BLAS_INT || Common->blas_ok) \
     { \
 	BLAS_ZGER (&M, &N, alpha, X, &INCX, Y, &INCY, A, &LDA) ; \
     } \
diff --git a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_check.h b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_check.h
index 58e4ad0..fcc64e0 100644
--- a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_check.h
+++ b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_check.h
@@ -87,12 +87,12 @@ int cholmod_l_check_common (cholmod_common *) ;
 int cholmod_print_common
 (
     /* ---- input ---- */
-    char *name,		/* printed name of Common object */
+    const char *name,	/* printed name of Common object */
     /* --------------- */
     cholmod_common *Common
 ) ;
 
-int cholmod_l_print_common (char *, cholmod_common *) ;
+int cholmod_l_print_common (const char *, cholmod_common *) ;
 
 /* -------------------------------------------------------------------------- */
 /* cholmod_check_sparse:  check a sparse matrix */
@@ -116,12 +116,12 @@ int cholmod_print_sparse
 (
     /* ---- input ---- */
     cholmod_sparse *A,	/* sparse matrix to print */
-    char *name,		/* printed name of sparse matrix */
+    const char *name,	/* printed name of sparse matrix */
     /* --------------- */
     cholmod_common *Common
 ) ;
 
-int cholmod_l_print_sparse (cholmod_sparse *, char *, cholmod_common *) ;
+int cholmod_l_print_sparse (cholmod_sparse *, const char *, cholmod_common *) ;
 
 /* -------------------------------------------------------------------------- */
 /* cholmod_check_dense:  check a dense matrix */
@@ -145,12 +145,12 @@ int cholmod_print_dense
 (
     /* ---- input ---- */
     cholmod_dense *X,	/* dense matrix to print */
-    char *name,		/* printed name of dense matrix */
+    const char *name,	/* printed name of dense matrix */
     /* --------------- */
     cholmod_common *Common
 ) ;
 
-int cholmod_l_print_dense (cholmod_dense *, char *, cholmod_common *) ;
+int cholmod_l_print_dense (cholmod_dense *, const char *, cholmod_common *) ;
 
 /* -------------------------------------------------------------------------- */
 /* cholmod_check_factor:  check a factor */
@@ -174,12 +174,12 @@ int cholmod_print_factor
 (
     /* ---- input ---- */
     cholmod_factor *L,	/* factor to print */
-    char *name,		/* printed name of factor */
+    const char *name,	/* printed name of factor */
     /* --------------- */
     cholmod_common *Common
 ) ;
 
-int cholmod_l_print_factor (cholmod_factor *, char *, cholmod_common *) ;
+int cholmod_l_print_factor (cholmod_factor *, const char *, cholmod_common *) ;
 
 /* -------------------------------------------------------------------------- */
 /* cholmod_check_triplet:  check a sparse matrix in triplet form */
@@ -203,12 +203,12 @@ int cholmod_print_triplet
 (
     /* ---- input ---- */
     cholmod_triplet *T,	/* triplet matrix to print */
-    char *name,		/* printed name of triplet matrix */
+    const char *name,	/* printed name of triplet matrix */
     /* --------------- */
     cholmod_common *Common
 ) ;
 
-int cholmod_l_print_triplet (cholmod_triplet *, char *, cholmod_common *) ;
+int cholmod_l_print_triplet (cholmod_triplet *, const char *, cholmod_common *);
 
 /* -------------------------------------------------------------------------- */
 /* cholmod_check_subset:  check a subset */
@@ -236,12 +236,12 @@ int cholmod_print_subset
     int *Set,		/* Set [0:len-1] is a subset of 0:n-1.  Duplicates OK */
     UF_long len,	/* size of Set (an integer array) */
     size_t n,		/* 0:n-1 is valid range */
-    char *name,		/* printed name of Set */
+    const char *name,	/* printed name of Set */
     /* --------------- */
     cholmod_common *Common
 ) ;
 
-int cholmod_l_print_subset (UF_long *, UF_long, size_t, char *,
+int cholmod_l_print_subset (UF_long *, UF_long, size_t, const char *,
     cholmod_common *) ;
 
 /* -------------------------------------------------------------------------- */
@@ -270,12 +270,13 @@ int cholmod_print_perm
     int *Perm,		/* Perm [0:len-1] is a permutation of subset of 0:n-1 */
     size_t len,		/* size of Perm (an integer array) */
     size_t n,		/* 0:n-1 is valid range */
-    char *name,		/* printed name of Perm */
+    const char *name,	/* printed name of Perm */
     /* --------------- */
     cholmod_common *Common
 ) ;
 
-int cholmod_l_print_perm (UF_long *, size_t, size_t, char *, cholmod_common *) ;
+int cholmod_l_print_perm (UF_long *, size_t, size_t, const char *,
+    cholmod_common *) ;
 
 /* -------------------------------------------------------------------------- */
 /* cholmod_check_parent:  check an elimination tree */
@@ -301,12 +302,12 @@ int cholmod_print_parent
     /* ---- input ---- */
     int *Parent,	/* Parent [0:n-1] is an elimination tree */
     size_t n,		/* size of Parent */
-    char *name,		/* printed name of Parent */
+    const char *name,	/* printed name of Parent */
     /* --------------- */
     cholmod_common *Common
 ) ;
 
-int cholmod_l_print_parent (UF_long *, size_t, char *, cholmod_common *) ;
+int cholmod_l_print_parent (UF_long *, size_t, const char *, cholmod_common *) ;
 
 /* -------------------------------------------------------------------------- */
 /* cholmod_read_sparse: read a sparse matrix from a file */
@@ -390,13 +391,13 @@ int cholmod_write_sparse
     FILE *f,		    /* file to write to, must already be open */
     cholmod_sparse *A,	    /* matrix to print */
     cholmod_sparse *Z,	    /* optional matrix with pattern of explicit zeros */
-    char *comments,	    /* optional filename of comments to include */
+    const char *comments,    /* optional filename of comments to include */
     /* --------------- */
     cholmod_common *Common
 ) ;
 
 int cholmod_l_write_sparse (FILE *, cholmod_sparse *, cholmod_sparse *,
-    char *c, cholmod_common *) ;
+    const char *c, cholmod_common *) ;
 
 /* -------------------------------------------------------------------------- */
 /* cholmod_write_dense: write a dense matrix to a file */
@@ -407,11 +408,11 @@ int cholmod_write_dense
     /* ---- input ---- */
     FILE *f,		    /* file to write to, must already be open */
     cholmod_dense *X,	    /* matrix to print */
-    char *comments,	    /* optional filename of comments to include */
+    const char *comments,    /* optional filename of comments to include */
     /* --------------- */
     cholmod_common *Common
 ) ;
 
-int cholmod_l_write_dense (FILE *, cholmod_dense *, char *, cholmod_common *) ;
-
+int cholmod_l_write_dense (FILE *, cholmod_dense *, const char *,
+    cholmod_common *) ;
 #endif
diff --git a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_cholesky.h b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_cholesky.h
index 7388afb..feb22a7 100644
--- a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_cholesky.h
+++ b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_cholesky.h
@@ -106,6 +106,25 @@ cholmod_factor *cholmod_l_analyze_p (cholmod_sparse *, UF_long *, UF_long *,
     size_t, cholmod_common *) ;
 
 /* -------------------------------------------------------------------------- */
+/* cholmod_analyze_p2:  analyze for sparse Cholesky or sparse QR */
+/* -------------------------------------------------------------------------- */
+
+cholmod_factor *cholmod_analyze_p2
+(
+    /* ---- input ---- */
+    int for_cholesky,   /* if TRUE, then analyze for Cholesky; else for QR */
+    cholmod_sparse *A,	/* matrix to order and analyze */
+    int *UserPerm,	/* user-provided permutation, size A->nrow */
+    int *fset,		/* subset of 0:(A->ncol)-1 */
+    size_t fsize,	/* size of fset */
+    /* --------------- */
+    cholmod_common *Common
+) ;
+
+cholmod_factor *cholmod_l_analyze_p2 (int, cholmod_sparse *, UF_long *,
+    UF_long *, size_t, cholmod_common *) ;
+
+/* -------------------------------------------------------------------------- */
 /* cholmod_factorize:  simplicial or supernodal Cholesky factorization */
 /* -------------------------------------------------------------------------- */
 
diff --git a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_core.h b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_core.h
index 523f505..b0a1e80 100644
--- a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_core.h
+++ b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_core.h
@@ -244,10 +244,10 @@
  *	#endif
  */
 
-#define CHOLMOD_DATE "Nov 1, 2007"
+#define CHOLMOD_DATE "Sept 20, 2008"
 #define CHOLMOD_VER_CODE(main,sub) ((main) * 1000 + (sub))
 #define CHOLMOD_MAIN_VERSION 1
-#define CHOLMOD_SUB_VERSION 6
+#define CHOLMOD_SUB_VERSION 7
 #define CHOLMOD_SUBSUB_VERSION 0
 #define CHOLMOD_VERSION \
     CHOLMOD_VER_CODE(CHOLMOD_MAIN_VERSION,CHOLMOD_SUB_VERSION)
@@ -356,8 +356,8 @@
  */
 
 /* Definitions for cholmod_common: */
-#define CHOLMOD_MAXMETHODS 9	/* maximum number of different methods that
-				 * cholmod_analyze can try. Must be >= 9. */
+#define CHOLMOD_MAXMETHODS 9	/* maximum number of different methods that */
+				/* cholmod_analyze can try. Must be >= 9. */
 
 /* Common->status values.  zero means success, negative means a fatal error,
  * positive is a warning. */
@@ -367,16 +367,16 @@
 #define CHOLMOD_TOO_LARGE (-3)		/* failure: integer overflow occured */
 #define CHOLMOD_INVALID (-4)		/* failure: invalid input */
 #define CHOLMOD_NOT_POSDEF (1)		/* warning: matrix not pos. def. */
-#define CHOLMOD_DSMALL (2)		/* warning: D for LDL'  or diag(L) or
-					 * LL' has tiny absolute value */
+#define CHOLMOD_DSMALL (2)		/* warning: D for LDL'  or diag(L) or */
+					/* LL' has tiny absolute value */
 
 /* ordering method (also used for L->ordering) */
 #define CHOLMOD_NATURAL 0	/* use natural ordering */
 #define CHOLMOD_GIVEN 1		/* use given permutation */
 #define CHOLMOD_AMD 2		/* use minimum degree (AMD) */
 #define CHOLMOD_METIS 3		/* use METIS' nested dissection */
-#define CHOLMOD_NESDIS 4	/* use CHOLMOD's version of nested dissection:
-				 * node bisector applied recursively, followed
+#define CHOLMOD_NESDIS 4	/* use CHOLMOD's version of nested dissection:*/
+				/* node bisector applied recursively, followed
 				 * by constrained minimum degree (CSYMAMD or
 				 * CCOLAMD) */
 #define CHOLMOD_COLAMD 5	/* use AMD for A, COLAMD for A*A' */
@@ -531,7 +531,8 @@ typedef struct cholmod_common_struct
 			 * of a try/catch block.  No error message is printed
 	 * and the Common->error_handler function is not called. */
 
-    void (*error_handler) (int status, char *file, int line, char *message) ;
+    void (*error_handler) (int status, const char *file,
+        int line, const char *message) ;
 
 	/* Common->error_handler is the user's error handling routine.  If not
 	 * NULL, this routine is called if an error occurs in CHOLMOD.  status
@@ -901,9 +902,21 @@ typedef struct cholmod_common_struct
      * v1.1 to the current version are binary compatible.
      */
 
-    double  other1 [16] ;
-    UF_long other2 [16] ;
-    int     other3 [13] ;   /* reduced from size 16 in v1.1. */
+    /* ---------------------------------------------------------------------- */
+    double other1 [12] ;        /* reduced from size 16 in v1.6 */
+
+    double SPQR_xstat [2] ;     /* for SuiteSparseQR statistics */
+
+    /* SuiteSparseQR control parameters: */
+    double SPQR_grain ;         /* task size is >= max (total flops / grain) */
+    double SPQR_small ;         /* task size is >= small */
+
+    /* ---------------------------------------------------------------------- */
+    UF_long SPQR_istat [10] ;   /* for SuiteSparseQR statistics */
+    UF_long other2 [6] ;        /* reduced from size 16 in v1.6 */
+
+    /* ---------------------------------------------------------------------- */
+    int other3 [10] ;       /* reduced from size 16 in v1.1. */
 
     int prefer_binary ;	    /* cholmod_read_triplet converts a symmetric
 			     * pattern-only matrix into a real matrix.  If
@@ -925,7 +938,16 @@ typedef struct cholmod_common_struct
     int called_nd ;	    /* TRUE if the last call to
 			     * cholmod_analyze called NESDIS or METIS. */
 
+    int blas_ok ;           /* FALSE if BLAS int overflow; TRUE otherwise */
+
+    /* SuiteSparseQR control parameters: */
+    int SPQR_shrink ;        /* controls stack realloc method */
+    int SPQR_nthreads ;      /* number of TBB threads, 0 = auto */
+
+    /* ---------------------------------------------------------------------- */
     size_t  other4 [16] ;
+
+    /* ---------------------------------------------------------------------- */
     void   *other5 [16] ;
 
 } cholmod_common ;
@@ -1034,14 +1056,14 @@ int cholmod_error
 (
     /* ---- input ---- */
     int status,		/* error status */
-    char *file,		/* name of source code file where error occured */
+    const char *file,	/* name of source code file where error occured */
     int line,		/* line number in source code file where error occured*/
-    char *message,	/* error message */
+    const char *message,/* error message */
     /* --------------- */
     cholmod_common *Common
 ) ;
 
-int cholmod_l_error (int, char *, int, char *, cholmod_common *) ;
+int cholmod_l_error (int, const char *, int, const char *, cholmod_common *) ;
 
 /* -------------------------------------------------------------------------- */
 /* cholmod_dbound:  for internal use in CHOLMOD only */
diff --git a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_internal.h b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_internal.h
index 383eecb..d27bf9b 100644
--- a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_internal.h
+++ b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_internal.h
@@ -315,16 +315,17 @@ size_t cholmod_l_mult_size_t (size_t a, size_t k, int *ok) ;
 /* double, int */
 EXTERN int cholmod_dump ;
 EXTERN int cholmod_dump_malloc ;
-UF_long cholmod_dump_sparse (cholmod_sparse  *, char *, cholmod_common *) ;
-int  cholmod_dump_factor (cholmod_factor  *, char *, cholmod_common *) ;
-int  cholmod_dump_triplet (cholmod_triplet *, char *, cholmod_common *) ;
-int  cholmod_dump_dense (cholmod_dense   *, char *, cholmod_common *) ;
-int  cholmod_dump_subset (int *, size_t, size_t, char *, cholmod_common *) ;
-int  cholmod_dump_perm (int *, size_t, size_t, char *, cholmod_common *) ;
-int  cholmod_dump_parent (int *, size_t, char *, cholmod_common *) ;
-void cholmod_dump_init (char *, cholmod_common *) ;
-int  cholmod_dump_mem (char *, UF_long, cholmod_common *) ;
-void cholmod_dump_real (char *, Real *, UF_long, UF_long, int, int,
+UF_long cholmod_dump_sparse (cholmod_sparse  *, const char *, cholmod_common *);
+int  cholmod_dump_factor (cholmod_factor  *, const char *, cholmod_common *) ;
+int  cholmod_dump_triplet (cholmod_triplet *, const char *, cholmod_common *) ;
+int  cholmod_dump_dense (cholmod_dense   *, const char *, cholmod_common *) ;
+int  cholmod_dump_subset (int *, size_t, size_t, const char *,
+    cholmod_common *) ;
+int  cholmod_dump_perm (int *, size_t, size_t, const char *, cholmod_common *) ;
+int  cholmod_dump_parent (int *, size_t, const char *, cholmod_common *) ;
+void cholmod_dump_init (const char *, cholmod_common *) ;
+int  cholmod_dump_mem (const char *, UF_long, cholmod_common *) ;
+void cholmod_dump_real (const char *, Real *, UF_long, UF_long, int, int,
 	cholmod_common *) ;
 void cholmod_dump_super (UF_long, int *, int *, int *, int *, double *, int,
 	cholmod_common *) ;
@@ -335,17 +336,19 @@ int  cholmod_dump_work(int, int, UF_long, cholmod_common *) ;
 /* double, UF_long */
 EXTERN int cholmod_l_dump ;
 EXTERN int cholmod_l_dump_malloc ;
-UF_long cholmod_l_dump_sparse (cholmod_sparse  *, char *, cholmod_common *) ;
-int  cholmod_l_dump_factor (cholmod_factor  *, char *, cholmod_common *) ;
-int  cholmod_l_dump_triplet (cholmod_triplet *, char *, cholmod_common *) ;
-int  cholmod_l_dump_dense (cholmod_dense   *, char *, cholmod_common *) ;
-int  cholmod_l_dump_subset (UF_long *, size_t, size_t, char *,
+UF_long cholmod_l_dump_sparse (cholmod_sparse  *, const char *,
+    cholmod_common *) ;
+int  cholmod_l_dump_factor (cholmod_factor  *, const char *, cholmod_common *) ;
+int  cholmod_l_dump_triplet (cholmod_triplet *, const char *, cholmod_common *);
+int  cholmod_l_dump_dense (cholmod_dense   *, const char *, cholmod_common *) ;
+int  cholmod_l_dump_subset (UF_long *, size_t, size_t, const char *,
+    cholmod_common *) ;
+int  cholmod_l_dump_perm (UF_long *, size_t, size_t, const char *,
     cholmod_common *) ;
-int  cholmod_l_dump_perm (UF_long *, size_t, size_t, char *, cholmod_common *) ;
-int  cholmod_l_dump_parent (UF_long *, size_t, char *, cholmod_common *) ;
-void cholmod_l_dump_init (char *, cholmod_common *) ;
-int  cholmod_l_dump_mem (char *, UF_long, cholmod_common *) ;
-void cholmod_l_dump_real (char *, Real *, UF_long, UF_long, int, int,
+int  cholmod_l_dump_parent (UF_long *, size_t, const char *, cholmod_common *) ;
+void cholmod_l_dump_init (const char *, cholmod_common *) ;
+int  cholmod_l_dump_mem (const char *, UF_long, cholmod_common *) ;
+void cholmod_l_dump_real (const char *, Real *, UF_long, UF_long, int, int,
 	cholmod_common *) ;
 void cholmod_l_dump_super (UF_long, UF_long *, UF_long *, UF_long *, UF_long *,
         double *, int, cholmod_common *) ;
diff --git a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_supernodal.h b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_supernodal.h
index cfb483e..1a49973 100644
--- a/src/C/SuiteSparse/CHOLMOD/Include/cholmod_supernodal.h
+++ b/src/C/SuiteSparse/CHOLMOD/Include/cholmod_supernodal.h
@@ -75,6 +75,31 @@ int cholmod_l_super_symbolic (cholmod_sparse *, cholmod_sparse *, UF_long *,
     cholmod_factor *, cholmod_common *) ;
 
 /* -------------------------------------------------------------------------- */
+/* cholmod_super_symbolic2 */
+/* -------------------------------------------------------------------------- */
+
+/* Analyze for supernodal Cholesky or multifrontal QR.  CHOLMOD itself always
+ * analyzes for supernodal Cholesky, of course.  This "for_cholesky = TRUE"
+ * option is used by SuiteSparseQR only.   Added for V1.7 */
+
+int cholmod_super_symbolic2
+(
+    /* ---- input ---- */
+    int for_cholesky,   /* Cholesky if TRUE, QR if FALSE */
+    cholmod_sparse *A,	/* matrix to analyze */
+    cholmod_sparse *F,	/* F = A' or A(:,f)' */
+    int *Parent,	/* elimination tree */
+    /* ---- in/out --- */
+    cholmod_factor *L,	/* simplicial symbolic on input,
+			 * supernodal symbolic on output */
+    /* --------------- */
+    cholmod_common *Common
+) ;
+
+int cholmod_l_super_symbolic2 (int, cholmod_sparse *, cholmod_sparse *,
+    UF_long *, cholmod_factor *, cholmod_common *) ;
+
+/* -------------------------------------------------------------------------- */
 /* cholmod_super_numeric */
 /* -------------------------------------------------------------------------- */
 
diff --git a/src/C/SuiteSparse/CHOLMOD/Makefile b/src/C/SuiteSparse/CHOLMOD/Makefile
index 1916688..62595b7 100644
--- a/src/C/SuiteSparse/CHOLMOD/Makefile
+++ b/src/C/SuiteSparse/CHOLMOD/Makefile
@@ -23,7 +23,7 @@ purge:
 	( cd MATLAB ; $(MAKE) purge )
 	( cd Tcov ; $(MAKE) purge )
 	( cd Lib ; $(MAKE) purge )
-#	( cd Valgrind ; $(MAKE) purge )
+	( cd Valgrind ; $(MAKE) dopurge )
 	( cd Demo ; $(MAKE) purge )
 	( cd Doc ; $(MAKE) purge )
 
diff --git a/src/C/SuiteSparse/CHOLMOD/Supernodal/cholmod_super_solve.c b/src/C/SuiteSparse/CHOLMOD/Supernodal/cholmod_super_solve.c
index 2d81222..52a870c 100644
--- a/src/C/SuiteSparse/CHOLMOD/Supernodal/cholmod_super_solve.c
+++ b/src/C/SuiteSparse/CHOLMOD/Supernodal/cholmod_super_solve.c
@@ -54,8 +54,6 @@ int CHOLMOD(super_lsolve)   /* TRUE if OK, FALSE if BLAS overflow occured */
     cholmod_common *Common
 )
 {
-    int blas_ok = TRUE ;
-
     /* ---------------------------------------------------------------------- */
     /* check inputs */
     /* ---------------------------------------------------------------------- */
@@ -108,19 +106,19 @@ int CHOLMOD(super_lsolve)   /* TRUE if OK, FALSE if BLAS overflow occured */
     {
 
 	case CHOLMOD_REAL:
-	    blas_ok = r_cholmod_super_lsolve (L, X, E, Common) ;
+	    r_cholmod_super_lsolve (L, X, E, Common) ;
 	    break ;
 
 	case CHOLMOD_COMPLEX:
-	    blas_ok = c_cholmod_super_lsolve (L, X, E, Common) ;
+	    c_cholmod_super_lsolve (L, X, E, Common) ;
 	    break ;
     }
 
-    if (CHECK_BLAS_INT && !blas_ok)
+    if (CHECK_BLAS_INT && !Common->blas_ok)
     {
 	ERROR (CHOLMOD_TOO_LARGE, "problem too large for the BLAS") ;
     }
-    return (blas_ok) ;
+    return (Common->blas_ok) ;
 }
 
 
@@ -149,8 +147,6 @@ int CHOLMOD(super_ltsolve)  /* TRUE if OK, FALSE if BLAS overflow occured */
     cholmod_common *Common
 )
 {
-    int blas_ok = TRUE ;
-
     /* ---------------------------------------------------------------------- */
     /* check inputs */
     /* ---------------------------------------------------------------------- */
@@ -203,19 +199,18 @@ int CHOLMOD(super_ltsolve)  /* TRUE if OK, FALSE if BLAS overflow occured */
     {
 
 	case CHOLMOD_REAL:
-	    blas_ok = r_cholmod_super_ltsolve (L, X, E, Common) ;
+	    r_cholmod_super_ltsolve (L, X, E, Common) ;
 	    break ;
 
 	case CHOLMOD_COMPLEX:
-	    blas_ok = c_cholmod_super_ltsolve (L, X, E, Common) ;
+	    c_cholmod_super_ltsolve (L, X, E, Common) ;
 	    break ;
     }
 
-    if (CHECK_BLAS_INT && !blas_ok)
+    if (CHECK_BLAS_INT && !Common->blas_ok)
     {
 	ERROR (CHOLMOD_TOO_LARGE, "problem too large for the BLAS") ;
     }
-
-    return (blas_ok) ;
+    return (Common->blas_ok) ;
 }
 #endif
diff --git a/src/C/SuiteSparse/CHOLMOD/Supernodal/cholmod_super_symbolic.c b/src/C/SuiteSparse/CHOLMOD/Supernodal/cholmod_super_symbolic.c
index c7cd0c7..e8f1b5d 100644
--- a/src/C/SuiteSparse/CHOLMOD/Supernodal/cholmod_super_symbolic.c
+++ b/src/C/SuiteSparse/CHOLMOD/Supernodal/cholmod_super_symbolic.c
@@ -51,13 +51,28 @@
 /* ========================================================================== */
 
 /* In the symmetric case, traverse the kth row subtree from the nonzeros in
- * A (0:k,k) and add the new entries found to the pattern of the kth row of L.
+ * A (0:k1-1,k) and add the new entries found to the pattern of the kth row
+ * of L.  The current supernode s contains the diagonal block k1:k2-1, so it
+ * can be skipped.
  *
- * In the unsymmetric case, the nonzero pattern of A*F is computed one column at
- * one column at a time.  The kth column is A*F(:,k), or the set union of all
- * columns A(:,j) for which F(j,k) is nonzero.  This routine is called once
- * for each entry j.  Only the upper triangular part is needed, so only
- * A (0:k,j) is accessed.
+ * In the unsymmetric case, the nonzero pattern of A*F is computed one column
+ * at a time (thus, the total time spent in this function is bounded below by
+ * the time taken to multiply A*F, which can be high if A is tall and thin).
+ * The kth column is A*F(:,k), or the set union of all columns A(:,j) for which
+ * F(j,k) is nonzero.  This routine is called once for each entry j.  Only the
+ * upper triangular part is needed, so only A (0:k1-1,j) is accessed, where
+ * k1:k2-1 are the columns of the current supernode s (k is in the range k1 to
+ * k2-1).
+ *
+ * If A is sorted, then the total time taken by this function is proportional
+ * to the number of nonzeros in the strictly block upper triangular part of A,
+ * plus the number of entries in the strictly block lower triangular part of
+ * the supernodal part of L.  This excludes entries in the diagonal blocks
+ * corresponding to the columns in each supernode.  That is, if k1:k2-1 are
+ * in a single supernode, then only A (0:k1-1,k1:k2-1) are accessed.
+ *
+ * For the unsymmetric case, only the strictly block upper triangular part
+ * of A*F is constructed.
  *
  * Only adds column indices corresponding to the leading columns of each
  * relaxed supernode.
@@ -74,6 +89,8 @@ static void subtree
     Int SuperMap [ ],
     Int Sparent [ ],
     Int mark,
+    Int sorted,         /* true if the columns of A are sorted */
+    Int k1,             /* only consider A (0:k1-1,k) */
 
     /* input/output: */
     Int Flag [ ],
@@ -84,10 +101,11 @@ static void subtree
     Int p, pend, i, si ;
     p = Ap [j] ;
     pend = (Anz == NULL) ? (Ap [j+1]) : (p + Anz [j]) ;
+
     for ( ; p < pend ; p++)
     {
 	i = Ai [p] ;
-	if (i < k)
+	if (i < k1)
 	{
 	    /* (i,k) is an entry in the upper triangular part of A or A*F'.
 	     * symmetric case:   A(i,k) is nonzero (j=k).
@@ -106,6 +124,10 @@ static void subtree
 		Flag [si] = mark ;
 	    }
 	}
+        else if (sorted)
+        {
+            break ;
+        }
     }
 }
 
@@ -124,21 +146,17 @@ static void subtree
 
 
 /* ========================================================================== */
-/* === cholmod_super_symbolic =============================================== */
+/* === cholmod_super_symbolic2 ============================================== */
 /* ========================================================================== */
 
-/* Analyzes A, AA', or A(:,f)*A(:,f)' in preparation for a supernodal numeric
- * factorization.  The user need not call this directly; cholmod_analyze is
- * a "simple" wrapper for this routine.
- *
- * workspace: Flag (nrow), Head (nrow), Iwork (2*nrow),
- * and temporary space of size 3*nfsuper*sizeof(Int), where nfsuper <= n
- * is the number of fundamental supernodes.
- */
+/* Analyze for supernodal Cholesky or multifrontal QR.  CHOLMOD itself always
+ * analyzes for supernodal Cholesky, of course.  The "for_cholesky = TRUE"
+ * option is used by SuiteSparseQR only. */
 
-int CHOLMOD(super_symbolic)
+int CHOLMOD(super_symbolic2)
 (
     /* ---- input ---- */
+    int for_cholesky,   /* Cholesky if TRUE, QR if FALSE */
     cholmod_sparse *A,	/* matrix to analyze */
     cholmod_sparse *F,	/* F = A' or A(:,f)' */
     Int *Parent,	/* elimination tree */
@@ -156,7 +174,7 @@ int CHOLMOD(super_symbolic)
     Int nsuper, d, n, j, k, s, mark, parent, p, pend, k1, k2, packed, nscol,
 	nsrow, ndrow1, ndrow2, stype, ssize, xsize, sparent, plast, slast,
 	csize, maxcsize, ss, nscol0, nscol1, ns, nfsuper, newzeros, totzeros,
-	merge, snext, esize, maxesize, nrelax0, nrelax1, nrelax2 ;
+	merge, snext, esize, maxesize, nrelax0, nrelax1, nrelax2, Asorted ;
     size_t w ;
     int ok = TRUE ;
 
@@ -528,17 +546,25 @@ int CHOLMOD(super_symbolic)
 	nsrow = Snz [s] ;
 	ASSERT (nscol > 0) ;
 	ssize += nsrow ;
-	xsize += nscol * nsrow ;
-	/* also compute xsize in double to guard against Int overflow */
-	xxsize += ((double) nscol) * ((double) nsrow) ;
-	if (xxsize > Int_max)
+        if (for_cholesky)
+        {
+            xsize += nscol * nsrow ;
+            /* also compute xsize in double to guard against Int overflow */
+            xxsize += ((double) nscol) * ((double) nsrow) ;
+        }
+	if (ssize < 0 || (for_cholesky && xxsize > Int_max))
 	{
-	    /* Int overflow, clear workspace and return */
+	    /* Int overflow, clear workspace and return.
+               QR factorization will not use xxsize, so that error is ignored.
+               For Cholesky factorization, however, memory of space xxsize
+               will be allocated, so this is a failure.  Both QR and Cholesky
+               fail if ssize overflows. */
 	    ERROR (CHOLMOD_TOO_LARGE, "problem too large") ;
 	    FREE_WORKSPACE ;
 	    return (FALSE) ;
 	}
-	ASSERT (ssize > 0 && xsize > 0) ;
+	ASSERT (ssize > 0) ;
+        ASSERT (IMPLIES (for_cholesky, xsize > 0)) ;
     }
     xsize = MAX (1, xsize) ;
     ssize = MAX (1, ssize) ;
@@ -568,6 +594,7 @@ int CHOLMOD(super_symbolic)
     Lpx = L->px ;
     Ls = L->s ;
     Ls [0] = 0 ;    /* flag for cholmod_check_factor; supernodes are defined */
+    Lpx [0] = for_cholesky ? 0 : 123456 ;   /* magic number for sparse QR */
     Lsuper = L->super ;
 
     /* copy the list of relaxed supernodes into the final list in L */
@@ -599,16 +626,21 @@ int CHOLMOD(super_symbolic)
     /* construct pointers for supernodal values (L->px) */
     /* ---------------------------------------------------------------------- */
 
-    p = 0 ;
-    for (s = 0 ; s < nsuper ; s++)
+    if (for_cholesky)
     {
-	nscol = Super [s+1] - Super [s] ;   /* number of columns in s */
-	nsrow = Snz [s] ;		    /* # of rows, incl triangular part*/
-	Lpx [s] = p ;			    /* pointer to numerical part of s */
-	p += nscol * nsrow ;
+        /* L->px is not needed for QR factorization (it may lead to Int
+           overflow, anyway, if xsize caused Int overflow above) */
+        p = 0 ;
+        for (s = 0 ; s < nsuper ; s++)
+        {
+            nscol = Super [s+1] - Super [s] ;   /* number of columns in s */
+            nsrow = Snz [s] ;           /* # of rows, incl triangular part*/
+            Lpx [s] = p ;               /* pointer to numerical part of s */
+            p += nscol * nsrow ;
+        }
+        Lpx [s] = p ;
+        ASSERT ((Int) (L->xsize) == MAX (1,p)) ;
     }
-    Lpx [s] = p ;
-    ASSERT ((Int) (L->xsize) == MAX (1,p)) ;
 
     /* Snz no longer needed ] */
 
@@ -622,6 +654,8 @@ int CHOLMOD(super_symbolic)
 	Lpi2 [s] = Lpi [s] ;
     }
 
+    Asorted = A->sorted ;
+
     for (s = 0 ; s < nsuper ; s++)
     {
 	/* sth supernode is in columns k1 to k2-1.
@@ -657,22 +691,21 @@ int CHOLMOD(super_symbolic)
 	    if (stype != 0)
 	    {
 		subtree (k, k, Ap, Ai, Anz, SuperMap, Sparent, mark,
-			Flag, Ls, Lpi2) ;
+                        Asorted, k1, Flag, Ls, Lpi2) ;
 	    }
 	    else
 	    {
-		/* for each nonzero in F (k,:) do */
+		/* for each j nonzero in F (:,k) do */
 		p = Fp [k] ;
 		pend = (packed) ? (Fp [k+1]) : (p + Fnz [k]) ;
 		for ( ; p < pend ; p++)
 		{
 		    subtree (Fj [p], k, Ap, Ai, Anz, SuperMap, Sparent, mark,
-			    Flag, Ls, Lpi2) ;
+			    Asorted, k1, Flag, Ls, Lpi2) ;
 		}
 	    }
 	}
     }
-
 #ifndef NDEBUG
     for (s = 0 ; s < nsuper ; s++)
     {
@@ -747,35 +780,39 @@ int CHOLMOD(super_symbolic)
     maxcsize = 1 ;
     maxesize = 1 ;
 
-    /* do not need to guard csize against Int overflow if xsize is OK */
+    /* Do not need to guard csize against Int overflow since xsize is OK. */
 
-    for (d = 0 ; d < nsuper ; d++)
+    if (for_cholesky)
     {
-	nscol = Super [d+1] - Super [d] ;
-	p = Lpi [d] + nscol ;
-	plast = p ;
-	pend = Lpi [d+1] ;
-	esize = pend - p ;
-	maxesize = MAX (maxesize, esize) ;
-	slast = (p == pend) ? (EMPTY) : (SuperMap [Ls [p]]) ;
-	for ( ; p <= pend ; p++)
-	{
-	    s = (p == pend) ? (EMPTY) : (SuperMap [Ls [p]]) ;
-	    if (s != slast)
-	    {
-		/* row i is the start of a new supernode */
-		ndrow1 = p - plast ;
-		ndrow2 = pend - plast ;
-		csize = ndrow2 * ndrow1 ;
-		PRINT1 (("Supernode "ID" ancestor "ID" C: "ID"-by-"ID"  csize "
-			""ID"\n", d, slast, ndrow1, ndrow2, csize)) ;
-		maxcsize = MAX (maxcsize, csize) ;
-		plast = p ;
-		slast = s ;
-	    }
-	}
+        /* this is not needed for QR factorization */
+        for (d = 0 ; d < nsuper ; d++)
+        {
+            nscol = Super [d+1] - Super [d] ;
+            p = Lpi [d] + nscol ;
+            plast = p ;
+            pend = Lpi [d+1] ;
+            esize = pend - p ;
+            maxesize = MAX (maxesize, esize) ;
+            slast = (p == pend) ? (EMPTY) : (SuperMap [Ls [p]]) ;
+            for ( ; p <= pend ; p++)
+            {
+                s = (p == pend) ? (EMPTY) : (SuperMap [Ls [p]]) ;
+                if (s != slast)
+                {
+                    /* row i is the start of a new supernode */
+                    ndrow1 = p - plast ;
+                    ndrow2 = pend - plast ;
+                    csize = ndrow2 * ndrow1 ;
+                    PRINT1 (("Supernode "ID" ancestor "ID" C: "ID"-by-"ID
+                        "  csize "ID"\n", d, slast, ndrow1, ndrow2, csize)) ;
+                    maxcsize = MAX (maxcsize, csize) ;
+                    plast = p ;
+                    slast = s ;
+                }
+            }
+        }
+        PRINT1 (("max csize "ID"\n", maxcsize)) ;
     }
-    PRINT1 (("max csize "ID"\n", maxcsize)) ;
 
     /* Wj no longer needed for SuperMap } */
 
@@ -791,4 +828,35 @@ int CHOLMOD(super_symbolic)
     FREE_WORKSPACE ;
     return (TRUE) ;
 }
+
+/* ========================================================================== */
+/* === cholmod_super_symbolic =============================================== */
+/* ========================================================================== */
+
+/* Analyzes A, AA', or A(:,f)*A(:,f)' in preparation for a supernodal numeric
+ * factorization.  The user need not call this directly; cholmod_analyze is
+ * a "simple" wrapper for this routine.
+ * 
+ * This function does all the analysis for a supernodal Cholesky factorization.
+ *
+ * workspace: Flag (nrow), Head (nrow), Iwork (2*nrow),
+ * and temporary space of size 3*nfsuper*sizeof(Int), where nfsuper <= n
+ * is the number of fundamental supernodes.
+ */
+
+int CHOLMOD(super_symbolic)
+(
+    /* ---- input ---- */
+    cholmod_sparse *A,	/* matrix to analyze */
+    cholmod_sparse *F,	/* F = A' or A(:,f)' */
+    Int *Parent,	/* elimination tree */
+    /* ---- in/out --- */
+    cholmod_factor *L,	/* simplicial symbolic on input,
+			 * supernodal symbolic on output */
+    /* --------------- */
+    cholmod_common *Common
+)
+{
+    return (CHOLMOD(super_symbolic2) (TRUE, A, F, Parent, L, Common)) ;
+}
 #endif
diff --git a/src/C/SuiteSparse/CHOLMOD/Supernodal/t_cholmod_super_numeric.c b/src/C/SuiteSparse/CHOLMOD/Supernodal/t_cholmod_super_numeric.c
index 4757721..50313a4 100644
--- a/src/C/SuiteSparse/CHOLMOD/Supernodal/t_cholmod_super_numeric.c
+++ b/src/C/SuiteSparse/CHOLMOD/Supernodal/t_cholmod_super_numeric.c
@@ -119,7 +119,7 @@ static int TEMPLATE (cholmod_super_numeric)
 
     /* If integer overflow occurs in the BLAS, Common->status is set to
      * CHOLMOD_TOO_LARGE, and the contents of Lx are undefined. */
-    int blas_ok = TRUE ;
+    Common->blas_ok = TRUE ;
 
     /* ---------------------------------------------------------------------- */
     /* get inputs */
@@ -599,7 +599,7 @@ static int TEMPLATE (cholmod_super_numeric)
 
 	/* info is set to one in LAPACK_*potrf if blas_ok is FALSE.  It is
 	 * set to zero in dpotrf/zpotrf if the factorization was successful. */
-	if (CHECK_BLAS_INT && !blas_ok)
+	if (CHECK_BLAS_INT && !Common->blas_ok)
 	{
 	    ERROR (CHOLMOD_TOO_LARGE, "problem too large for the BLAS") ;
 	}
@@ -695,7 +695,7 @@ static int TEMPLATE (cholmod_super_numeric)
 		nsrow) ;
 #endif
 
-	    if (CHECK_BLAS_INT && !blas_ok)
+	    if (CHECK_BLAS_INT && !Common->blas_ok)
 	    {
 		ERROR (CHOLMOD_TOO_LARGE, "problem too large for the BLAS") ;
 	    }
diff --git a/src/C/SuiteSparse/CHOLMOD/Supernodal/t_cholmod_super_solve.c b/src/C/SuiteSparse/CHOLMOD/Supernodal/t_cholmod_super_solve.c
index 2f5e792..6509381 100644
--- a/src/C/SuiteSparse/CHOLMOD/Supernodal/t_cholmod_super_solve.c
+++ b/src/C/SuiteSparse/CHOLMOD/Supernodal/t_cholmod_super_solve.c
@@ -14,7 +14,7 @@
 
 #include "cholmod_template.h"
 
-static int TEMPLATE (cholmod_super_lsolve)
+static void TEMPLATE (cholmod_super_lsolve)
 (
     /* ---- input ---- */
     cholmod_factor *L,	/* factor to use for the forward solve */
@@ -32,10 +32,6 @@ static int TEMPLATE (cholmod_super_lsolve)
     Int nsuper, k1, k2, psi, psend, psx, nsrow, nscol, ii, s,
 	nsrow2, n, ps2, j, i, d, nrhs ;
 
-    /* If integer overflow occurs in the BLAS, Common->status is set to
-     * CHOLMOD_TOO_LARGE in the caller, and the contents of X are undefined. */
-    int blas_ok = TRUE ;
-
     /* ---------------------------------------------------------------------- */
     /* get inputs */
     /* ---------------------------------------------------------------------- */
@@ -223,13 +219,10 @@ static int TEMPLATE (cholmod_super_lsolve)
 	    }
 	}
     }
-
-    Common->status = CHOLMOD_OK ;
-    return (blas_ok) ;
 }
 
 
-static int TEMPLATE (cholmod_super_ltsolve)
+static void TEMPLATE (cholmod_super_ltsolve)
 (
     /* ---- input ---- */
     cholmod_factor *L,	/* factor to use for the forward solve */
@@ -246,7 +239,6 @@ static int TEMPLATE (cholmod_super_ltsolve)
     Int *Lpi, *Lpx, *Ls, *Super ;
     Int nsuper, k1, k2, psi, psend, psx, nsrow, nscol, ii, s,
 	nsrow2, n, ps2, j, i, d, nrhs ;
-    int blas_ok = TRUE ;
 
     /* ---------------------------------------------------------------------- */
     /* get inputs */
@@ -419,9 +411,6 @@ static int TEMPLATE (cholmod_super_ltsolve)
 
 	}
     }
-
-    Common->status = CHOLMOD_OK ;
-    return (blas_ok) ;
 }
 
 #undef PATTERN
diff --git a/src/C/SuiteSparse/Contents.m b/src/C/SuiteSparse/Contents.m
index 5f6f306..ed86758 100644
--- a/src/C/SuiteSparse/Contents.m
+++ b/src/C/SuiteSparse/Contents.m
@@ -114,6 +114,14 @@
 %   luflop            - given L and U, computes # of flops required
 %
 %-------------------------------------------------------------------------------
+% SuiteSparseQR: multifrontal rank-revealing sparse QR
+%-------------------------------------------------------------------------------
+%
+%   spqr            - sparse QR
+%   spqr_solve      - x=A\b using SuiteSparseQR
+%   spqr_qmult      - y=Q*x, Q'*x, x*Q, or x*Q' using Q in Householder form
+%
+%-------------------------------------------------------------------------------
 % Other packages:
 %-------------------------------------------------------------------------------
 %
@@ -128,7 +136,7 @@
 % please see the help for each individual package.   UFcollection and RBio
 % are two additional toolboxes, for managing the UF Sparse Matrix Collection.
 %
-% Copyright 2007, Timothy A. Davis
+% Copyright 2008, Timothy A. Davis
 % http://www.cise.ufl.edu/research/sparse
 
 help SuiteSparse
diff --git a/src/C/SuiteSparse/Makefile b/src/C/SuiteSparse/Makefile
index b56e0c8..197e8aa 100644
--- a/src/C/SuiteSparse/Makefile
+++ b/src/C/SuiteSparse/Makefile
@@ -19,24 +19,28 @@ default:
 	( cd CHOLMOD ; $(MAKE) )
 	( cd CSparse ; $(MAKE) )
 	( cd CXSparse ; $(MAKE) )
+	( cd SPQR ; $(MAKE) )
 #	( cd LPDASA ; $(MAKE) )
 #	( cd PARAKLETE ; $(MAKE) )
 
 library: default
 
 # Compile the MATLAB mexFunctions (except RBio and UFcollection)
+# CHOLMOD and KLU will fail if you don't have METIS (use SuiteSparse_install.m
+# in the MATLAB Command Window instead to compile them without METIS)
 mex:
 	( cd AMD ; $(MAKE) mex )
 	( cd CAMD ; $(MAKE) mex )
 	( cd COLAMD ; $(MAKE) mex )
 	( cd BTF ; $(MAKE) mex )
-	( cd KLU ; $(MAKE) mex )
 	( cd LDL ; $(MAKE) mex )
 	( cd CCOLAMD ; $(MAKE) mex )
-	( cd CHOLMOD ; $(MAKE) mex )
-	( cd UMFPACK ; $(MAKE) mex )
 	( cd CXSparse ; $(MAKE) mex )
 	( cd CSparse ; $(MAKE) mex )
+	( cd UMFPACK ; $(MAKE) mex )
+	( cd SPQR ; $(MAKE) mex )
+	( cd CHOLMOD ; $(MAKE) mex )
+	( cd KLU ; $(MAKE) mex )
 
 # Remove all files not in the original distribution
 purge:
@@ -56,6 +60,8 @@ purge:
 	( cd RBio ; $(RM) *.mex* )
 	( cd UFcollection ; $(RM) *.mex* )
 	( cd SSMULT ; $(RM) *.mex* )
+	( cd SPQR ; $(MAKE) purge )
+	- $(RM) MATLAB_Tools/spok/*.mex* MATLAB_Tools/spok/private/*.mex*
 #	( cd LPDASA ; $(MAKE) purge )
 #	( cd PARAKLETE ; $(MAKE) purge )
 
@@ -74,6 +80,7 @@ clean:
 	( cd CHOLMOD ; $(MAKE) clean )
 	( cd CSparse ; $(MAKE) clean )
 	( cd CXSparse ; $(MAKE) clean )
+	( cd SPQR ; $(MAKE) clean )
 #	( cd LPDASA ; $(MAKE) clean )
 #	( cd PARAKLETE ; $(MAKE) clean )
 
@@ -97,3 +104,4 @@ cov:
 	( cd KLU ; $(MAKE) cov )
 	( cd CHOLMOD ; $(MAKE) cov )
 	( cd UMFPACK ; $(MAKE) cov )
+	( cd SPQR ; $(MAKE) cov )
diff --git a/src/C/SuiteSparse/README.txt b/src/C/SuiteSparse/README.txt
index f0d7ec4..c6f8f45 100644
--- a/src/C/SuiteSparse/README.txt
+++ b/src/C/SuiteSparse/README.txt
@@ -5,13 +5,14 @@ SuiteSparse/README
 ------------------
 
 ================================================================================
-QUICK START FOR MATLAB USERS:  unzip the SuiteSparse.zip file, then in the
+QUICK START FOR MATLAB USERS:  uncompress the SuiteSparse.zip or
+SuiteSparse.tar.gz archive file (they contain the same thing), then in the
 MATLAB Command Window, cd to the SuiteSparse directory and type
 SuiteSparse_install.  All packages will be compiled, and several demos will be
 run.
 ================================================================================
 
-Nov 1, 2007.  SuiteSparse version 3.1
+Sept 20, 2008.  SuiteSparse version 3.2.0
 
     AMD		approximate minimum degree ordering
 
@@ -58,11 +59,14 @@ Nov 1, 2007.  SuiteSparse version 3.1
 
     MATLAB_Tools    various simple m-files for use in MATLAB
 
+    SuiteSparseQR   sparse QR factorization
+
 CHOLMOD optionally uses METIS 4.0.1
 (http://www-users.cs.umn.edu/~karypis/metis).  To use METIS, place a copy of
-the metis-4.0 directory in the same directory (CHOLMOD_ACM_TOMS) containing
-this README file.  The use of METIS will improve the ordering quality in
-CHOLMOD.
+the metis-4.0 directory in the same directory containing this README file.
+Be sure that you do not have a nested metis-4.0/metis-4.0 directory; SuiteSparse
+won't find METIS if you do this, which can happen with a zip file of metis-4.0
+on Windows.  The use of METIS will improve the ordering quality in CHOLMOD.
 
 Refer to each package for license, copyright, and author information.  All
 codes are authored or co-authored by Timothy A. Davis, CISE Dept., Univ. of
@@ -88,25 +92,36 @@ To use "make" in Unix/Linux:
     otherwise UMFPACK and CHOLMOD will be slow.  Change -lblas to -l(your BLAS
     library here) in the UFconfig/UFconfig.mk file.
 
-(2) Configure METIS (or don't use METIS)
+(2) Install Intel's Threading Building Blocks (TBB)
+
+    This is optionally used by SuiteSparseQR.  Refer to the User Guide in 
+    SuiteSparse/SPQR/Doc/spqr_user_guide.pdf for details.
+
+(3) Configure METIS (or don't use METIS)
 
     cd to metis-4.0 and edit the Makefile.in file.  I recommend making these
     changes to metis-4.0/Makefile.in:
 
         CC = gcc
         OPTFLAGS = -O3
+
+    And, if you want to use METIS in MATLAB and compile with "make" instead
+    of using SuiteSparse_install.m:
+
         COPTIONS = -fexceptions -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
 
     Next, cd to metis-4.0 and type "make".
 
     If you do not wish to use METIS, then edit the UFconfig/UFconfig.mk file,
-    and change the line
+    and change the lines
 
         CHOLMOD_CONFIG =
+        SPQR_CONFIG =
 
     to
 
         CHOLMOD_CONFIG = -DNPARTITION
+        SPQR_CONFIG = -DNPARTITION
 
     Also change the line
 
@@ -116,14 +131,19 @@ To use "make" in Unix/Linux:
 
         METIS =
 
-(3) Make other changes to UFconfig/UFconfig.mk as needed
+(4) Make other changes to UFconfig/UFconfig.mk as needed
 
     Edit the UFconfig/UFconfig.mk file as needed.  Directions are in that file.
     If you have compiled SuiteSparse already (partially or completely), then
     whenever you edit the UFconfig/UFconfig.mk file, you should then type
     "make purge" (or "make realclean") in this directory.
 
-(4) Type "make" in this directory.  All packages will be be compiled.  METIS
+    If you want to use "make" to compile mexFunctions, I recommend adding
+    these options to the CFLAGS = line:
+
+        -fexceptions -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
+
+(5) Type "make" in this directory.  All packages will be be compiled.  METIS
     will be compiled if you have it.  Several demos will be run.
 
     The libraries will appear in */Lib/*.a.  Include files, as needed by user
@@ -133,3 +153,10 @@ To use "make" in Unix/Linux:
     The METIS library is in metis-4.0/libmetis.a.  METIS Include files (not
     needed by the end user of SuiteSparse) are in located in metis-4.0/Lib/*.h.
 
+
+In a future version, I will include a "make install" that will create *.so
+libraries and place them in /usr/lib.  The libraries should be called
+libPACKAGE.so.VERSION.SUBVERSION.SUBSUBVERSION.  For example, 
+libcolamd.so.2.7.1 should be the library name for COLAMD version 2.7.1.
+The version numbers are located in UFconfig.h (in comments) and in each
+package (as a #define).
diff --git a/src/C/SuiteSparse/README_cvxopt b/src/C/SuiteSparse/README_cvxopt
index 6e99bc1..740dccf 100644
--- a/src/C/SuiteSparse/README_cvxopt
+++ b/src/C/SuiteSparse/README_cvxopt
@@ -1,6 +1,6 @@
-This is the Nov 1, 2007 (version 3.1.0) distribution of the SuiteSparse 
-package from www.cise.ufl.edu/research/sparse/SuiteSparse/, with the 
-following files and directories removed.
+This is the September 20, 2008 (version 3.2.0) distribution of the 
+SuiteSparse package from www.cise.ufl.edu/research/sparse/SuiteSparse/, 
+with the following files and directories removed.
 
 AMD/Demo
 AMD/MATLAB
@@ -26,6 +26,7 @@ LINFACTOR
 MATLAB_Tools
 MESHND
 RBio
+SPQR
 SSMULT
 SuiteSparse_demo.m
 SuiteSparse_install.m
diff --git a/src/C/SuiteSparse/UFconfig/README.txt b/src/C/SuiteSparse/UFconfig/README.txt
index eb26fe1..d9edc23 100644
--- a/src/C/SuiteSparse/UFconfig/README.txt
+++ b/src/C/SuiteSparse/UFconfig/README.txt
@@ -15,6 +15,7 @@ do not require UFconfig.
   BTF	   permutation to block triangular form
   LDL	   concise sparse LDL'
   LPDASA   LP Dual Active Set Algorithm
+  SuiteSparseQR     sparse QR factorization
 
 UFconfig is not required by:
 
diff --git a/src/C/SuiteSparse/UFconfig/UFconfig.h b/src/C/SuiteSparse/UFconfig/UFconfig.h
index 36c1bd3..54208d5 100644
--- a/src/C/SuiteSparse/UFconfig/UFconfig.h
+++ b/src/C/SuiteSparse/UFconfig/UFconfig.h
@@ -76,13 +76,13 @@ extern "C" {
  * version of SuiteSparse, with another package from another version of
  * SuiteSparse, may or may not work.
  *
- * SuiteSparse Version 3.1.0 contains the following packages:
+ * SuiteSparse Version 3.2.0 contains the following packages:
  *
  *  AMD		    version 2.2.0
  *  CAMD	    version 2.2.0
  *  COLAMD	    version 2.7.1
  *  CCOLAMD	    version 2.7.1
- *  CHOLMOD	    version 1.6.0
+ *  CHOLMOD	    version 1.7.0
  *  CSparse	    version 2.2.1
  *  CXSparse	    version 2.2.1
  *  KLU		    version 1.0.1
@@ -96,6 +96,7 @@ extern "C" {
  *  MESHND          version 1.1.0
  *  SSMULT          version 1.1.0
  *  MATLAB_Tools    no specific version number
+ *  SuiteSparseQR   version 1.1.0
  *
  * Other package dependencies:
  *  BLAS	    required by CHOLMOD and UMFPACK
@@ -103,10 +104,10 @@ extern "C" {
  *  METIS 4.0.1	    required by CHOLMOD (optional) and KLU (optional)
  */
 
-#define SUITESPARSE_DATE "Nov 1, 2007"
+#define SUITESPARSE_DATE "Sept 20, 2008"
 #define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub))
 #define SUITESPARSE_MAIN_VERSION 3
-#define SUITESPARSE_SUB_VERSION 1
+#define SUITESPARSE_SUB_VERSION 2
 #define SUITESPARSE_SUBSUB_VERSION 0
 #define SUITESPARSE_VERSION \
     SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION)
diff --git a/src/C/SuiteSparse/UFconfig/UFconfig.mk b/src/C/SuiteSparse/UFconfig/UFconfig.mk
index 5f4f8e2..f2924a0 100644
--- a/src/C/SuiteSparse/UFconfig/UFconfig.mk
+++ b/src/C/SuiteSparse/UFconfig/UFconfig.mk
@@ -18,6 +18,7 @@
 # LDL	  1.2 or later	concise sparse LDL'
 # LPDASA  any		linear program solve (dual active set algorithm)
 # CXSparse any		extended version of CSparse (int/long, real/complex)
+# SuiteSparseQR	any	sparse QR factorization
 #
 # The UFconfig directory and the above packages should all appear in a single
 # directory, in order for the Makefile's within each package to find this file.
@@ -33,7 +34,10 @@
 # performance.  You should select the optimization parameters that are best
 # for your system.  On Linux, use "CFLAGS = -O3 -fexceptions" for example.
 CC = cc
-CFLAGS = -O
+# CFLAGS = -O   (for example; see below for details)
+
+# C++ compiler (also uses CFLAGS)
+CPLUSPLUS = g++
 
 # ranlib, and ar, for generating libraries
 RANLIB = ranlib
@@ -51,7 +55,7 @@ F77LIB =
 # C and Fortran libraries
 LIB = -lm
 
-# For compiling MATLAB mexFunctions (MATLAB 7.5)
+# For compiling MATLAB mexFunctions (MATLAB 7.5 or later)
 MEX = mex -O -largeArrayDims -lmwlapack -lmwblas
 
 # For compiling MATLAB mexFunctions (MATLAB 7.3 and 7.4)
@@ -82,12 +86,16 @@ MEX = mex -O -largeArrayDims -lmwlapack -lmwblas
 # naming the BLAS and LAPACK library (*.a or *.so) files.
 
 # Using the Goto BLAS:
-# BLAS = -lgoto -lgfortran -lgfortranbegin
+# BLAS = -lgoto -lgfortran -lgfortranbegin -lg2c
 
 # This is probably slow ... it might connect to the Standard Reference BLAS:
-BLAS = -lblas -lgfortran -lgfortranbegin
+BLAS = -lblas -lgfortran -lgfortranbegin -lg2c
 LAPACK = -llapack
 
+# Using non-optimized versions:
+# BLAS = -lblas_plain -lgfortran -lgfortranbegin -lg2c
+# LAPACK = -llapack_plain
+
 # The BLAS might not contain xerbla, an error-handling routine for LAPACK and
 # the BLAS.  Also, the standard xerbla requires the Fortran I/O library, and
 # stops the application program if an error occurs.  A C version of xerbla
@@ -179,12 +187,40 @@ UMFPACK_CONFIG =
 CHOLMOD_CONFIG =
 
 #------------------------------------------------------------------------------
+# SuiteSparseQR configuration:
+#------------------------------------------------------------------------------
+
+# The SuiteSparseQR library can be compiled with the following options:
+#
+# -DNPARTITION      do not include the CHOLMOD partition module
+# -DNEXPERT         do not include the functions in SuiteSparseQR_expert.cpp
+# -DTIMING          enable timing and flop counts
+# -DHAVE_TBB        enable the use of Intel's Threading Building Blocks (TBB)
+
+# default, without timing, without TBB:
+SPQR_CONFIG =
+# with timing and TBB:
+# SPQR_CONFIG = -DTIMING -DHAVE_TBB
+# with timing
+# SPQR_CONFIG = -DTIMING
+
+# with TBB, you must select this:
+# TBB = -ltbb
+# without TBB:
+TBB =
+
+# with timing, you must include the timing library:
+# RTLIB = -lrt
+# without timing
+RTLIB =
+
+#------------------------------------------------------------------------------
 # Linux
 #------------------------------------------------------------------------------
 
 # Using default compilers:
 # CC = gcc
-CFLAGS = -O3
+CFLAGS = -O3 -fexceptions
 
 # alternatives:
 # CFLAGS = -g -fexceptions \
@@ -195,6 +231,7 @@ CFLAGS = -O3
     	-Wredundant-decls -Wnested-externs -Wdisabled-optimization -ansi
 # CFLAGS = -O3 -fexceptions -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
 # CFLAGS = -O3
+# CFLAGS = -O3 -g -fexceptions
 
 # consider:
 # -fforce-addr -fmove-all-movables -freduce-all-givs -ftsp-ordering
@@ -231,14 +268,18 @@ CFLAGS = -O3
 #------------------------------------------------------------------------------
 
 # 32-bit
-# CFLAGS = -KPIC -dalign -xc99=%none -Xc -xlibmieee -xO5 -xlibmil
+# CFLAGS = -KPIC -dalign -xc99=%none -Xc -xlibmieee -xO5 -xlibmil -m32
 
 # 64-bit
-# CFLAGS = -KPIC -dalign -xc99=%none -Xc -xlibmieee -xO5 -xlibmil -xarch=v9
+# CFLAGS = -fast -KPIC -xc99=%none -xlibmieee -xlibmil -m64 -Xc
 
+# FFLAGS = -fast -KPIC -dalign -xlibmil -m64
+
+# The Sun Performance Library includes both LAPACK and the BLAS:
 # BLAS = -xlic_lib=sunperf
 # LAPACK =
 
+
 #------------------------------------------------------------------------------
 # Compaq Alpha
 #------------------------------------------------------------------------------
@@ -307,4 +348,4 @@ CFLAGS = -O3
 # remove object files and profile output
 #------------------------------------------------------------------------------
 
-CLEAN = *.o *.obj *.ln *.bb *.bbg *.da *.tcov *.gcov gmon.out *.bak *.d
+CLEAN = *.o *.obj *.ln *.bb *.bbg *.da *.tcov *.gcov gmon.out *.bak *.d *.gcda *.gcno
diff --git a/src/C/SuiteSparse/UMFPACK/Doc/ChangeLog b/src/C/SuiteSparse/UMFPACK/Doc/ChangeLog
index 82f50aa..c5bafd9 100644
--- a/src/C/SuiteSparse/UMFPACK/Doc/ChangeLog
+++ b/src/C/SuiteSparse/UMFPACK/Doc/ChangeLog
@@ -1,3 +1,7 @@
+Sept 22, 2008
+
+    * minor update to documentation; no change to code
+
 Nov 1, 2007, version 5.2.0
 
     * change of license to GNU GPL from GNU LGPL.  This is the primary change to
diff --git a/src/C/SuiteSparse/UMFPACK/Doc/License b/src/C/SuiteSparse/UMFPACK/Doc/License
index 19f2fa9..085bf62 100644
--- a/src/C/SuiteSparse/UMFPACK/Doc/License
+++ b/src/C/SuiteSparse/UMFPACK/Doc/License
@@ -1,4 +1,4 @@
-UMFPACK Version 5.1.1, Copyright 1995-2007 by Timothy A. Davis.
+UMFPACK Version 5.2.0, Copyright 1995-2007 by Timothy A. Davis.
 All Rights Reserved.
 UMFPACK is available under alternate licenses, contact T. Davis for details.
 
diff --git a/src/C/SuiteSparse/UMFPACK/Doc/QuickStart.tex b/src/C/SuiteSparse/UMFPACK/Doc/QuickStart.tex
index e210577..8ed941e 100644
--- a/src/C/SuiteSparse/UMFPACK/Doc/QuickStart.tex
+++ b/src/C/SuiteSparse/UMFPACK/Doc/QuickStart.tex
@@ -220,12 +220,12 @@ for more details.
     double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], Ax [nz], X [n], B [n] ;
     void *Symbolic, *Numeric ;
 
+    umfpack_di_defaults (Control) ;
     status = umfpack_di_symbolic (m, n, Ap, Ai, Ax, &Symbolic, Control, Info) ;
     status = umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric, Control, Info) ;
     status = umfpack_di_solve (sys, Ap, Ai, Ax, X, B, Numeric, Control, Info) ;
     umfpack_di_free_symbolic (&Symbolic) ;
     umfpack_di_free_numeric (&Numeric) ;
-    umfpack_di_defaults (Control) ;
 \end{verbatim}
 }
 
diff --git a/src/C/SuiteSparse/UMFPACK/Doc/UserGuide.stex b/src/C/SuiteSparse/UMFPACK/Doc/UserGuide.stex
index 271419c..cc5f40b 100644
--- a/src/C/SuiteSparse/UMFPACK/Doc/UserGuide.stex
+++ b/src/C/SuiteSparse/UMFPACK/Doc/UserGuide.stex
@@ -643,7 +643,8 @@ and {\tt m} $<$ {\tt n}, then {\tt umfpack} might not find a factor
 {\tt U} with a structurally zero-free diagonal.
 Unless the matrix ill-conditioned or
 poorly scaled, factorizing {\tt A'} in this case will guarantee that both
-factors will have zero-free diagonals.  Note that there is no guarantee
+factors will have zero-free diagonals (in the structural sense; they may
+be numerically zero).  Note that there is no guarantee
 as to the size of the diagonal entries of {\tt U}; UMFPACK does not do a
 rank-revealing factorization.  Here's how you can factorize {\tt A'}
 and get the factors of {\tt A} instead:
diff --git a/src/C/amd.c b/src/C/amd.c
index ccda063..a82427f 100644
--- a/src/C/amd.c
+++ b/src/C/amd.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/C/base.c b/src/C/base.c
index 3ceeba3..7dbc02e 100644
--- a/src/C/base.c
+++ b/src/C/base.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -36,7 +36,7 @@ matrix * Matrix_NewFromArrayStruct(PyObject *, int, int *) ;
 extern PyTypeObject spmatrix_tp ;
 spmatrix * SpMatrix_New(int_t, int_t, int, int ) ;
 spmatrix * SpMatrix_NewFromMatrix(matrix *, int) ;
-spmatrix * SpMatrix_NewFromSpMatrix(spmatrix *, int, int) ; 
+spmatrix * SpMatrix_NewFromSpMatrix(spmatrix *, int) ; 
 spmatrix * SpMatrix_NewFromIJV(matrix *, matrix *, matrix *, 
     int_t, int_t, int, int) ;
 void free_ccs(ccs *) ;
@@ -58,8 +58,6 @@ extern int (*sp_syrk[])(char, char, number, void *, number,
 const int  E_SIZE[] = { sizeof(int_t), sizeof(double), sizeof(complex) };
 const char TC_CHAR[][2] = {"i","d","z"} ;
 
-PyObject *base_mod;
-
 /* 
  *  Helper routines and definitions to implement type transparency.
  */
@@ -348,7 +346,7 @@ PyObject * base_axpy(PyObject *self, PyObject *args, PyObject *kwrds)
   number a;
   char *kwlist[] = {"x", "y", "alpha", "partial", NULL};
   
-  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OO|OO", kwlist, 
+  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OO|OO:axpy", kwlist, 
 	  &x, &y, &ao, &partial)) return NULL;
   
   if (!Matrix_Check(x) && !SpMatrix_Check(x)) err_mtrx("x");
@@ -369,7 +367,7 @@ PyObject * base_axpy(PyObject *self, PyObject *args, PyObject *kwrds)
 	MAT_BUF(y), (int *)&One[INT]);
   }
   else {
-    
+
     void *z = NULL;
     if (sp_axpy[id]((ao ? a : One[id]), 
 	    Matrix_Check(x) ? MAT_BUF(x): ((spmatrix *)x)->obj,  
@@ -427,7 +425,7 @@ PyObject* base_gemm(PyObject *self, PyObject *args, PyObject *kwrds)
   char *kwlist[] = {"A", "B", "C", "transA", "transB", "alpha", "beta",
 		    "partial", NULL};
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OOO|ccOOO", 
+  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OOO|ccOOO:gemm", 
 	  kwlist, &A, &B, &C, &transA, &transB, &ao, &bo, &partial))
     return NULL;
 
@@ -534,7 +532,7 @@ static PyObject* base_gemv(PyObject *self, PyObject *args, PyObject *kwrds)
 		    "incx", "incy", "offsetA", "offsetx",
 		    "offsety", NULL};
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OOO|cOOiiiiiii", 
+  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OOO|cOOiiiiiii:gemv", 
 	  kwlist, &A, &x, &y, &trans, &ao, &bo, &m, &n, &ix, &iy, 
 	  &oA, &ox, &oy))
     return NULL;
@@ -629,7 +627,7 @@ static PyObject* base_syrk(PyObject *self, PyObject *args, PyObject *kwrds)
   char *kwlist[] = {"A", "C", "uplo", "trans", "alpha", "beta", "partial",
 		    NULL};
   
-  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OO|ccOOO", kwlist, 
+  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OO|ccOOO:syrk", kwlist, 
 	  &A, &C, &uplo, &trans, &ao, &bo, &partial))
     return NULL;
   
@@ -722,8 +720,8 @@ static PyObject* base_symv(PyObject *self, PyObject *args, PyObject *kwrds)
   char *kwlist[] = {"A", "x", "y", "uplo", "alpha", "beta", "n",
 		    "incx", "incy", "offsetA", "offsetx", "offsety", NULL};
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OOO|cOOiiiiii", kwlist, &A, 
-	  &x, &y, &uplo, &ao, &bo, &n, &ix, &iy, &oA, &ox, &oy))
+  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OOO|cOOiiiiii:symv", 
+	  kwlist, &A, &x, &y, &uplo, &ao, &bo, &n, &ix, &iy, &oA, &ox, &oy))
     return NULL;
 
   if (!Matrix_Check(A) && !SpMatrix_Check(A)) 
@@ -782,6 +780,24 @@ static PyObject* base_symv(PyObject *self, PyObject *args, PyObject *kwrds)
 
 spmatrix * sparse_concat(PyObject *L, int id_arg) ;
 
+static char doc_sparse[] =
+  "Constructs a sparse block matrix.\n\n"
+  "sparse(x, tc = None)\n\n"
+  "PURPOSE\n"
+  "Constructs a sparse block matrix from a list of block matrices.  If a\n"
+  "single matrix is given as argument,  then the matrix is converted to\n"
+  "sparse format, optionally with a different typcode.  If a single list\n"
+  "of subblocks is specified, then a block column matrix is created;\n"
+  "otherwise when a list of lists is specified, then the inner lists\n"
+  "specify the different block-columns.  Each block element must be either\n"
+  "a dense or sparse matrix, or a scalar,  and dense matrices are converted\n"
+  "to sparse format by removing 0 elements.\n\n"
+  "ARGUMENTS\n"
+  "x       a single matrix, or a list of matrices and scalars, or a list of\n"
+  "        lists of matrices and scalars\n\n"
+  "tc      typecode character 'd' or 'z'.";
+  
+
 static PyObject *
 sparse(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
@@ -789,7 +805,7 @@ sparse(PyTypeObject *type, PyObject *args, PyObject *kwds)
   static char *kwlist[] = { "x", "tc", NULL};
    char tc = 0;
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|c:matrix", kwlist, 
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|c:sparse", kwlist, 
 	  &Objx, &tc))
     return NULL;
 
@@ -864,13 +880,15 @@ spdiag(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
   PyObject *diag = NULL, *Dk;
   static char *kwlist[] = { "diag", NULL};
-
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &diag))
+  
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:spdiag", kwlist, &diag))
     return NULL;
   
-  if ((!PyList_Check(diag) && !Matrix_Check(diag)) ||
+  if ((!PyList_Check(diag) && !Matrix_Check(diag) && !SpMatrix_Check(diag)) ||
       (Matrix_Check(diag) && 
-	  (MAT_LGT(diag) != MAX(MAT_NROWS(diag),MAT_NCOLS(diag)))))
+	  (MAT_LGT(diag) != MAX(MAT_NROWS(diag),MAT_NCOLS(diag)) )) || 
+      (SpMatrix_Check(diag) && 
+	  (SP_LGT(diag) != MAX(SP_NROWS(diag),SP_NCOLS(diag)) )) )
     PY_ERR_TYPE("invalid diag argument");
   
   if (Matrix_Check(diag)) {
@@ -892,6 +910,29 @@ spdiag(PyTypeObject *type, PyObject *args, PyObject *kwds)
 	SP_VALZ(ret)[j] = MAT_BUFZ(diag)[j];
     }
     return (PyObject *)ret;
+  } 
+  else if (SpMatrix_Check(diag)) {
+
+    int k, id = MAX(DOUBLE, SP_ID(diag)), n = SP_LGT(diag);
+
+    spmatrix *ret = SpMatrix_New(n, n, SP_NNZ(diag), id);
+    if (!ret) return PyErr_NoMemory();
+    SP_COL(ret)[0] = 0;
+    
+    for (k=0; k<SP_NNZ(diag); k++) {
+      
+      SP_COL(ret)[SP_ROW(diag)[k]+1] = 1;
+      SP_ROW(ret)[k] = SP_ROW(diag)[k];
+      if (SP_ID(diag) == DOUBLE)
+	SP_VALD(ret)[k] = SP_VALD(diag)[k];
+      else
+	SP_VALZ(ret)[k] = SP_VALZ(diag)[k];	
+    }
+
+    for (k=0; k<n; k++) SP_COL(ret)[k+1] += SP_COL(ret)[k];
+      
+    return (PyObject *)ret;
+
   }
 
   int j, k, l, idx, id=DOUBLE, n=0, nnz=0;
@@ -975,13 +1016,666 @@ spdiag(PyTypeObject *type, PyObject *args, PyObject *kwds)
   return (PyObject *)ret;
 }
 
+matrix * dense(spmatrix *self);
+
+PyObject * matrix_elem_max(PyObject *self, PyObject *args, PyObject *kwrds)
+{
+  PyObject *A, *B;
+  if (!PyArg_ParseTuple(args, "OO:emax", &A, &B)) return NULL;
+
+  if (!(X_Matrix_Check(A) || PyNumber_Check(A)) ||
+      !(X_Matrix_Check(B) || PyNumber_Check(B))) 
+    PY_ERR_TYPE("arguments must be either matrices or python numbers");
+  
+  if (PyComplex_Check(A) || (X_Matrix_Check(A) && X_ID(A)==COMPLEX))
+    PY_ERR_TYPE("ordering not defined for complex numbers");
+
+  if (PyComplex_Check(B) || (X_Matrix_Check(B) && X_ID(B)==COMPLEX)) 
+    PY_ERR_TYPE("ordering not defined for complex numbers");
+ 
+  int a_is_number = PyNumber_Check(A) || 
+    (Matrix_Check(A) && MAT_LGT(A) == 1) || 
+    (SpMatrix_Check(A) && SP_LGT(A) == 1);
+  int b_is_number = PyNumber_Check(B) || 
+    (Matrix_Check(B) && MAT_LGT(B) == 1) ||
+    (SpMatrix_Check(B) && SP_LGT(B) == 1);
+  
+  int ida = PyNumber_Check(A) ? PyFloat_Check(A) : X_ID(A);
+  int idb = PyNumber_Check(B) ? PyFloat_Check(B) : X_ID(B);
+  int id  = MAX( ida, idb );
+
+  number a, b;
+  if (a_is_number) {
+    if (PyNumber_Check(A) || Matrix_Check(A))
+      convert_num[id](&a, A, PyNumber_Check(A), 0);
+    else 
+      a.d = (SP_LGT(A) ? SP_VALD(A)[0] : 0.0);
+  }
+  if (b_is_number) {
+    if (PyNumber_Check(B) || Matrix_Check(B))
+      convert_num[id](&b, B, PyNumber_Check(B), 0);
+    else 
+      b.d = (SP_LGT(B) ? SP_VALD(B)[0] : 0.0);
+  }
+  
+  if ((a_is_number && b_is_number) &&
+      (!X_Matrix_Check(A) && !X_Matrix_Check(B))) {
+    if (id == INT) 
+      return Py_BuildValue("i", MAX(a.i, b.i) );
+    else
+      return Py_BuildValue("d", MAX(a.d, b.d) );
+  }
+
+  if (!(a_is_number || b_is_number)) {
+    if (X_NROWS(A) != X_NROWS(B) || X_NCOLS(A) != X_NCOLS(B))
+      PY_ERR_TYPE("incompatible dimensions");
+  }
+
+  int m = ( !a_is_number ? X_NROWS(A) : (!b_is_number ? X_NROWS(B) : 1));
+  int n = ( !a_is_number ? X_NCOLS(A) : (!b_is_number ? X_NCOLS(B) : 1));
+  
+  if ((Matrix_Check(A) || a_is_number) || (Matrix_Check(B) || b_is_number)) {
+
+    int freeA = SpMatrix_Check(A) && (SP_LGT(A) > 1);
+    int freeB = SpMatrix_Check(B) && (SP_LGT(B) > 1);
+    if (freeA) { 
+      if (!(A = (PyObject *)dense((spmatrix *)A)) ) return PyErr_NoMemory();
+    }
+    if (freeB) { 
+      if (!(B = (PyObject *)dense((spmatrix *)B)) ) return PyErr_NoMemory();
+    }
+
+    PyObject *ret = (PyObject *)Matrix_New(m, n, id);
+    if (!ret) {
+      if (freeA) { Py_DECREF(A); }
+      if (freeB) { Py_DECREF(B); }
+      return PyErr_NoMemory();
+    }
+    int i;
+    for (i=0; i<m*n; i++) {
+      if (!a_is_number) convert_num[id](&a, A, 0, i);
+      if (!b_is_number) convert_num[id](&b, B, 0, i);
+      
+      if (id == INT)
+	MAT_BUFI(ret)[i] = MAX(a.i, b.i);
+      else
+	MAT_BUFD(ret)[i] = MAX(a.d, b.d);
+    }
+
+    if (freeA) { Py_DECREF(A); }
+    if (freeB) { Py_DECREF(B); }    
+    return ret;
+
+  } else {
+    
+    spmatrix *ret = SpMatrix_New(m, n, 0, DOUBLE);
+    if (!ret) return PyErr_NoMemory();
+
+    int j, ka = 0, kb = 0, kret = 0;
+    for (j=0; j<n; j++) {
+      
+      while (ka < SP_COL(A)[j+1] && kb < SP_COL(B)[j+1]) {
+	
+	if (SP_ROW(A)[ka] < SP_ROW(B)[kb]) {
+	  if (SP_VALD(A)[ka++] > 0.0) SP_COL(ret)[j+1]++;
+	}
+	else if (SP_ROW(A)[ka] > SP_ROW(B)[kb]) {
+	  if (SP_VALD(B)[kb++] > 0.0) SP_COL(ret)[j+1]++;
+	}
+	else {
+	  SP_COL(ret)[j+1]++; ka++; kb++;
+	}
+      }
+
+      while (ka < SP_COL(A)[j+1]) {
+	if (SP_VALD(A)[ka++] > 0.0) SP_COL(ret)[j+1]++;       
+      }
+
+      while (kb < SP_COL(B)[j+1]) {
+	if (SP_VALD(B)[kb++] > 0.0) SP_COL(ret)[j+1]++;       
+      }
+	
+    }
+
+    for (j=0; j<n; j++) SP_COL(ret)[j+1] += SP_COL(ret)[j];
+
+    int_t *newrow = malloc( sizeof(int_t)*SP_COL(ret)[n] );
+    double *newval = malloc( sizeof(double)*SP_COL(ret)[n] );
+    if (!newrow || !newval) {
+      free(newrow); free(newval); Py_DECREF(ret);
+      return PyErr_NoMemory();
+    }
+    free( ret->obj->rowind );
+    free( ret->obj->values );
+    ret->obj->rowind = newrow;
+    ret->obj->values = newval;
+
+    ka = 0; kb = 0;
+    for (j=0; j<n; j++) {
+      
+      while (ka < SP_COL(A)[j+1] && kb < SP_COL(B)[j+1]) {
+	
+	if (SP_ROW(A)[ka] < SP_ROW(B)[kb]) {
+	  if (SP_VALD(A)[ka] > 0.0) { 
+	    SP_ROW(ret)[kret] = SP_ROW(A)[ka];
+	    SP_VALD(ret)[kret++] = SP_VALD(A)[ka];
+	  }
+	  ka++;
+	}
+	else if (SP_ROW(A)[ka] > SP_ROW(B)[kb]) {
+	  if (SP_VALD(B)[kb] > 0.0) {
+	    SP_ROW(ret)[kret] = SP_ROW(B)[kb];
+	    SP_VALD(ret)[kret++] = SP_VALD(B)[kb];
+	  }
+	  kb++;
+	}
+	else {
+	  SP_ROW(ret)[kret] = SP_ROW(A)[ka];
+	  SP_VALD(ret)[kret] = MAX(SP_VALD(A)[ka], SP_VALD(B)[kb]);
+	  kret++; ka++; kb++;
+	}
+      }
+
+      while (ka < SP_COL(A)[j+1]) {
+	if (SP_VALD(A)[ka] > 0.0) {
+	    SP_ROW(ret)[kret] = SP_ROW(A)[ka];
+	    SP_VALD(ret)[kret++] = SP_VALD(A)[ka];
+	}
+	ka++;
+      }
+
+      while (kb < SP_COL(B)[j+1]) {
+	if (SP_VALD(B)[kb] > 0.0) {
+	  SP_ROW(ret)[kret] = SP_ROW(B)[kb];
+	  SP_VALD(ret)[kret++] = SP_VALD(B)[kb];
+	}
+	kb++;
+      }	
+    }
+
+    return (PyObject *)ret;
+  }
+}
+
+PyObject * matrix_elem_min(PyObject *self, PyObject *args, PyObject *kwrds)
+{
+  PyObject *A, *B;
+  if (!PyArg_ParseTuple(args, "OO:emin", &A, &B)) return NULL;
+
+  if (!(X_Matrix_Check(A) || PyNumber_Check(A)) ||
+      !(X_Matrix_Check(B) || PyNumber_Check(B))) 
+    PY_ERR_TYPE("arguments must be either matrices or python numbers");
+  
+  if (PyComplex_Check(A) || (X_Matrix_Check(A) && X_ID(A)==COMPLEX))
+    PY_ERR_TYPE("ordering not defined for complex numbers");
+
+  if (PyComplex_Check(B) || (X_Matrix_Check(B) && X_ID(B)==COMPLEX)) 
+    PY_ERR_TYPE("ordering not defined for complex numbers");
+ 
+  int a_is_number = PyNumber_Check(A) || 
+    (Matrix_Check(A) && MAT_LGT(A) == 1) || 
+    (SpMatrix_Check(A) && SP_LGT(A) == 1);
+  int b_is_number = PyNumber_Check(B) || 
+    (Matrix_Check(B) && MAT_LGT(B) == 1) ||
+    (SpMatrix_Check(B) && SP_LGT(B) == 1);
+  
+  int ida = PyNumber_Check(A) ? PyFloat_Check(A) : X_ID(A);
+  int idb = PyNumber_Check(B) ? PyFloat_Check(B) : X_ID(B);
+  int id  = MAX( ida, idb );
+
+  number a, b;
+  if (a_is_number) {
+    if (PyNumber_Check(A) || Matrix_Check(A))
+      convert_num[id](&a, A, PyNumber_Check(A), 0);
+    else 
+      a.d = (SP_LGT(A) ? SP_VALD(A)[0] : 0.0);
+  }
+  if (b_is_number) {
+    if (PyNumber_Check(B) || Matrix_Check(B))
+      convert_num[id](&b, B, PyNumber_Check(B), 0);
+    else 
+      b.d = (SP_LGT(B) ? SP_VALD(B)[0] : 0.0);
+  }
+
+  if ((a_is_number && b_is_number) &&
+      (!X_Matrix_Check(A) && !X_Matrix_Check(B))) {
+    if (id == INT) 
+      return Py_BuildValue("i", MIN(a.i, b.i) );
+    else
+      return Py_BuildValue("d", MIN(a.d, b.d) );
+  }
+
+  if (!(a_is_number || b_is_number)) {
+    if (X_NROWS(A) != X_NROWS(B) || X_NCOLS(A) != X_NCOLS(B))
+      PY_ERR_TYPE("incompatible dimensions");
+  }
+
+  int m = ( !a_is_number ? X_NROWS(A) : (!b_is_number ? X_NROWS(B) : 1));
+  int n = ( !a_is_number ? X_NCOLS(A) : (!b_is_number ? X_NCOLS(B) : 1));
+  
+  if ((Matrix_Check(A) || a_is_number) || (Matrix_Check(B) || b_is_number)) {
+
+    int freeA = SpMatrix_Check(A) && (SP_LGT(A) > 1);
+    int freeB = SpMatrix_Check(B) && (SP_LGT(B) > 1);
+    if (freeA) { 
+      if (!(A = (PyObject *)dense((spmatrix *)A)) ) return PyErr_NoMemory();
+    }
+    if (freeB) { 
+      if (!(B = (PyObject *)dense((spmatrix *)B)) ) return PyErr_NoMemory();
+    }
+
+    PyObject *ret = (PyObject *)Matrix_New(m, n, id);
+    if (!ret) {
+      if (freeA) { Py_DECREF(A); }
+      if (freeB) { Py_DECREF(B); }
+      return PyErr_NoMemory();
+    }
+    int i;
+    for (i=0; i<m*n; i++) {
+      if (!a_is_number) convert_num[id](&a, A, 0, i);
+      if (!b_is_number) convert_num[id](&b, B, 0, i);
+      
+      if (id == INT)
+	MAT_BUFI(ret)[i] = MIN(a.i, b.i);
+      else
+	MAT_BUFD(ret)[i] = MIN(a.d, b.d);
+    }
+
+    if (freeA) { Py_DECREF(A); }
+    if (freeB) { Py_DECREF(B); }    
+    return ret;
+
+  } else {
+    
+    spmatrix *ret = SpMatrix_New(m, n, 0, DOUBLE);
+    if (!ret) return PyErr_NoMemory();
+
+    int j, ka = 0, kb = 0, kret = 0;
+    for (j=0; j<n; j++) {
+      
+      while (ka < SP_COL(A)[j+1] && kb < SP_COL(B)[j+1]) {
+	
+	if (SP_ROW(A)[ka] < SP_ROW(B)[kb]) {
+	  if (SP_VALD(A)[ka++] < 0.0) SP_COL(ret)[j+1]++;
+	}
+	else if (SP_ROW(A)[ka] > SP_ROW(B)[kb]) {
+	  if (SP_VALD(B)[kb++] < 0.0) SP_COL(ret)[j+1]++;
+	}
+	else {
+	  SP_COL(ret)[j+1]++; ka++; kb++;
+	}
+      }
+
+      while (ka < SP_COL(A)[j+1]) {
+	if (SP_VALD(A)[ka++] < 0.0) SP_COL(ret)[j+1]++;       
+      }
+
+      while (kb < SP_COL(B)[j+1]) {
+	if (SP_VALD(B)[kb++] < 0.0) SP_COL(ret)[j+1]++;       
+      }
+	
+    }
+
+    for (j=0; j<n; j++) SP_COL(ret)[j+1] += SP_COL(ret)[j];
+
+    int_t *newrow = malloc( sizeof(int_t)*SP_COL(ret)[n] );
+    double *newval = malloc( sizeof(double)*SP_COL(ret)[n] );
+    if (!newrow || !newval) {
+      free(newrow); free(newval); Py_DECREF(ret);
+      return PyErr_NoMemory();
+    }
+    free( ret->obj->rowind );
+    free( ret->obj->values );
+    ret->obj->rowind = newrow;
+    ret->obj->values = newval;
+
+    ka = 0; kb = 0;
+    for (j=0; j<n; j++) {
+      
+      while (ka < SP_COL(A)[j+1] && kb < SP_COL(B)[j+1]) {
+	
+	if (SP_ROW(A)[ka] < SP_ROW(B)[kb]) {
+	  if (SP_VALD(A)[ka] < 0.0) { 
+	    SP_ROW(ret)[kret] = SP_ROW(A)[ka];
+	    SP_VALD(ret)[kret++] = SP_VALD(A)[ka];
+	  }
+	  ka++;
+	}
+	else if (SP_ROW(A)[ka] > SP_ROW(B)[kb]) {
+	  if (SP_VALD(B)[kb] < 0.0) {
+	    SP_ROW(ret)[kret] = SP_ROW(B)[kb];
+	    SP_VALD(ret)[kret++] = SP_VALD(B)[kb];
+	  }
+	  kb++;
+	}
+	else {
+	  SP_ROW(ret)[kret] = SP_ROW(A)[ka];
+	  SP_VALD(ret)[kret] = MIN(SP_VALD(A)[ka], SP_VALD(B)[kb]);
+	  kret++; ka++; kb++;
+	}
+      }
+
+      while (ka < SP_COL(A)[j+1]) {
+	if (SP_VALD(A)[ka] < 0.0) {
+	    SP_ROW(ret)[kret] = SP_ROW(A)[ka];
+	    SP_VALD(ret)[kret++] = SP_VALD(A)[ka];
+	}
+	ka++;
+      }
+
+      while (kb < SP_COL(B)[j+1]) {
+	if (SP_VALD(B)[kb] < 0.0) {
+	  SP_ROW(ret)[kret] = SP_ROW(B)[kb];
+	  SP_VALD(ret)[kret++] = SP_VALD(B)[kb];
+	}
+	kb++;
+      }	
+    }
+
+    return (PyObject *)ret;
+  }
+}
+
+PyObject * matrix_elem_mul(matrix *self, PyObject *args, PyObject *kwrds)
+{
+  PyObject *A, *B;
+  if (!PyArg_ParseTuple(args, "OO:emul", &A, &B)) return NULL;
+
+  if (!(X_Matrix_Check(A) || PyNumber_Check(A)) ||
+      !(X_Matrix_Check(B) || PyNumber_Check(B))) 
+    PY_ERR_TYPE("arguments must be either matrices or python numbers");
+   
+  int a_is_number = PyNumber_Check(A) || (Matrix_Check(A) && MAT_LGT(A) == 1);
+  int b_is_number = PyNumber_Check(B) || (Matrix_Check(B) && MAT_LGT(B) == 1);
+  
+  int ida, idb;
+  if (PyInt_Check(A)) { ida = INT; }
+  else if (PyFloat_Check(A)) { ida = DOUBLE; }
+  else if (PyComplex_Check(A)) { ida = COMPLEX; }
+  else { ida = X_ID(A); }
+
+  if (PyInt_Check(B)) { idb = INT; }
+  else if (PyFloat_Check(B)) { idb = DOUBLE; }
+  else if (PyComplex_Check(B)) { idb = COMPLEX; }
+  else { idb = X_ID(B); }
+
+  int id  = MAX( ida, idb );
+
+  number a, b;
+  if (a_is_number) convert_num[id](&a, A, PyNumber_Check(A), 0);
+  if (b_is_number) convert_num[id](&b, B, PyNumber_Check(B), 0);
+
+  if (a_is_number && b_is_number &&
+      (!X_Matrix_Check(A) && !X_Matrix_Check(B))) {
+    if (!X_Matrix_Check(A) && !X_Matrix_Check(B)) {
+      if (id == INT) 
+	return Py_BuildValue("i", a.i*b.i );
+      else if (id == DOUBLE)
+	return Py_BuildValue("d", a.d*b.d );
+      else {
+	number c;
+	c.z = a.z*b.z;
+	return znum2PyObject(&c, 0);
+      }
+    }
+  }
+
+  if (!(a_is_number || b_is_number)) {
+    if (X_NROWS(A) != X_NROWS(B) || X_NCOLS(A) != X_NCOLS(B))
+      PY_ERR_TYPE("incompatible dimensions");
+  }
+
+  int m = ( !a_is_number ? X_NROWS(A) : (!b_is_number ? X_NROWS(B) : 1));
+  int n = ( !a_is_number ? X_NCOLS(A) : (!b_is_number ? X_NCOLS(B) : 1));
+  
+  if ((Matrix_Check(A) || a_is_number) && (Matrix_Check(B) || b_is_number)) {
+
+    PyObject *ret = (PyObject *)Matrix_New(m, n, id);
+    if (!ret) return PyErr_NoMemory();
+
+    int i;
+    for (i=0; i<m*n; i++) {
+      if (!a_is_number) convert_num[id](&a, A, 0, i);
+      if (!b_is_number) convert_num[id](&b, B, 0, i);
+      
+      if (id == INT)
+	MAT_BUFI(ret)[i] = a.i*b.i;
+      else if (id == DOUBLE)
+	MAT_BUFD(ret)[i] = a.d*b.d;
+      else
+	MAT_BUFZ(ret)[i] = a.z*b.z;
+    }
+
+    return ret;
+
+  } 
+  else if (SpMatrix_Check(A) && !SpMatrix_Check(B)) {
+    
+    PyObject *ret = (PyObject *)SpMatrix_NewFromSpMatrix((spmatrix *)A, id);
+    if (!ret) return PyErr_NoMemory();
+
+    int j, k;
+    for (j=0; j<SP_NCOLS(A); j++) {
+      for (k=SP_COL(A)[j]; k<SP_COL(A)[j+1]; k++) {
+	if (!b_is_number) convert_num[id](&b, B, 0, j*m + SP_ROW(A)[k]);
+      
+	if (id == DOUBLE)
+	  SP_VALD(ret)[k] *= b.d;
+	else
+	  SP_VALZ(ret)[k] *= b.z;
+      }
+    }
+
+    return ret;
+  }
+  else if (SpMatrix_Check(B) && !SpMatrix_Check(A)) {
+    
+    PyObject *ret = (PyObject *)SpMatrix_NewFromSpMatrix((spmatrix *)B, id);
+    if (!ret) return PyErr_NoMemory();
+
+    int j, k;
+    for (j=0; j<SP_NCOLS(B); j++) {
+      for (k=SP_COL(B)[j]; k<SP_COL(B)[j+1]; k++) {
+	if (!a_is_number) convert_num[id](&a, A, 0, j*m + SP_ROW(B)[k]);
+      
+	if (id == DOUBLE)
+	  SP_VALD(ret)[k] *= a.d;
+	else
+	  SP_VALZ(ret)[k] *= a.z;
+      }
+    }
+
+    return ret;
+  }
+
+  else {
+    
+    spmatrix *ret = SpMatrix_New(m, n, 0, id);
+    if (!ret) return PyErr_NoMemory();
+
+    int j, ka = 0, kb = 0, kret = 0;
+    for (j=0; j<n; j++) {
+      
+      while (ka < SP_COL(A)[j+1] && kb < SP_COL(B)[j+1]) {
+	
+	if (SP_ROW(A)[ka] < SP_ROW(B)[kb]) {
+	  ka++;
+	}
+	else if (SP_ROW(A)[ka] > SP_ROW(B)[kb]) {
+	  kb++;
+	}
+	else {
+	  SP_COL(ret)[j+1]++; ka++; kb++;
+	}
+      }
+      
+      ka = SP_COL(A)[j+1];
+      kb = SP_COL(B)[j+1];	
+    }
+
+    for (j=0; j<n; j++) SP_COL(ret)[j+1] += SP_COL(ret)[j];
+
+    int_t *newrow = malloc( sizeof(int_t)*SP_COL(ret)[n] );
+    double *newval = malloc( E_SIZE[id]*SP_COL(ret)[n] );
+    if (!newrow || !newval) {
+      free(newrow); free(newval); Py_DECREF(ret);
+      return PyErr_NoMemory();
+    }
+    free( ret->obj->rowind );
+    free( ret->obj->values );
+    ret->obj->rowind = newrow;
+    ret->obj->values = newval;
+
+    ka = 0; kb = 0;
+    for (j=0; j<n; j++) {
+      
+      while (ka < SP_COL(A)[j+1] && kb < SP_COL(B)[j+1]) {
+	
+	if (SP_ROW(A)[ka] < SP_ROW(B)[kb]) {
+	  ka++;
+	}
+	else if (SP_ROW(A)[ka] > SP_ROW(B)[kb]) {
+	  kb++;
+	}
+	else {
+	  SP_ROW(ret)[kret] = SP_ROW(A)[ka];
+	  if (id == DOUBLE)
+	    SP_VALD(ret)[kret] = SP_VALD(A)[ka]*SP_VALD(B)[kb];
+	  else
+	    SP_VALZ(ret)[kret] = 
+	      (X_ID(A) == DOUBLE ? SP_VALD(A)[ka] : SP_VALZ(A)[ka])*
+	      (X_ID(B) == DOUBLE ? SP_VALD(B)[kb] : SP_VALZ(B)[kb]);
+	    
+	  kret++; ka++; kb++;
+	}
+      }
+
+      ka = SP_COL(A)[j+1];
+      kb = SP_COL(B)[j+1];	
+    }
+    
+    return (PyObject *)ret;
+  }
+}
+
+PyObject * matrix_elem_div(matrix *self, PyObject *args, PyObject *kwrds)
+{
+  PyObject *A, *B, *ret;
+  if (!PyArg_ParseTuple(args, "OO:ediv", &A, &B)) return NULL;
+
+  if (!(X_Matrix_Check(A) || PyNumber_Check(A)) ||
+      !(X_Matrix_Check(B) || PyNumber_Check(B))) 
+    PY_ERR_TYPE("arguments must be either matrices or python numbers");
+  
+  if (SpMatrix_Check(B))
+    PY_ERR_TYPE("elementwise division with sparse matrix\n");
+
+  int a_is_number = PyNumber_Check(A) || (Matrix_Check(A) && MAT_LGT(A) == 1);
+  int b_is_number = PyNumber_Check(B) || (Matrix_Check(B) && MAT_LGT(B) == 1);
+    
+  int ida, idb;
+  if (PyInt_Check(A)) { ida = INT; }
+  else if (PyFloat_Check(A)) { ida = DOUBLE; }
+  else if (PyComplex_Check(A)) { ida = COMPLEX; }
+  else { ida = X_ID(A); }
+
+  if (PyInt_Check(B)) { idb = INT; }
+  else if (PyFloat_Check(B)) { idb = DOUBLE; }
+  else if (PyComplex_Check(B)) { idb = COMPLEX; }
+  else { idb = X_ID(B); }
+
+  int id  = MAX( ida, idb );
+
+  number a, b;
+  if (a_is_number) convert_num[id](&a, A, PyNumber_Check(A), 0);
+  if (b_is_number) convert_num[id](&b, B, PyNumber_Check(B), 0);
+
+  if ((a_is_number && b_is_number) && 
+      (!X_Matrix_Check(A) && !Matrix_Check(B))) {
+    if (id == INT) {
+      if (b.i == 0) PY_ERR(PyExc_ArithmeticError, "division by zero");
+      return Py_BuildValue("i", a.i/b.i );
+    }
+    else if (id == DOUBLE) {
+      if (b.d == 0.0) PY_ERR(PyExc_ArithmeticError, "division by zero");
+      return Py_BuildValue("d", a.d/b.d );
+    }
+    else {
+      if (b.z == 0.0) PY_ERR(PyExc_ArithmeticError, "division by zero");
+      number c;
+      c.z = a.z/b.z;
+      return znum2PyObject(&c, 0);
+    }
+  }
+
+  if (!(a_is_number || b_is_number)) {
+    if (X_NROWS(A) != MAT_NROWS(B) || X_NCOLS(A) != MAT_NCOLS(B))
+      PY_ERR_TYPE("incompatible dimensions");
+  }
+
+  int m = ( !a_is_number ? X_NROWS(A) : (!b_is_number ? X_NROWS(B) : 1));
+  int n = ( !a_is_number ? X_NCOLS(A) : (!b_is_number ? X_NCOLS(B) : 1));
+  
+  if ((Matrix_Check(A) || a_is_number) && (Matrix_Check(B) || b_is_number)) {
+
+    if (!(ret = (PyObject *)Matrix_New(m, n, id)))
+      return PyErr_NoMemory();
+
+    int i;
+    for (i=0; i<m*n; i++) {
+      if (!a_is_number) convert_num[id](&a, A, 0, i);
+      if (!b_is_number) convert_num[id](&b, B, 0, i);
+      
+      if (id == INT) {
+	if (b.i == 0) goto divzero;
+	MAT_BUFI(ret)[i] = a.i/b.i;
+      }
+      else if (id == DOUBLE) {
+	if (b.d == 0) goto divzero;
+	MAT_BUFD(ret)[i] = a.d/b.d;
+      }
+      else {
+	if (b.z == 0) goto divzero;
+	MAT_BUFZ(ret)[i] = a.z/b.z;
+      }
+    }
+
+    return ret;
+  } 
+  else { // (SpMatrix_Check(A) && !SpMatrix_Check(B)) {
+    
+    if (!(ret = (PyObject *)SpMatrix_NewFromSpMatrix((spmatrix *)A, id)))
+      return PyErr_NoMemory();
+
+    int j, k;
+    for (j=0; j<SP_NCOLS(A); j++) {
+      for (k=SP_COL(A)[j]; k<SP_COL(A)[j+1]; k++) {
+	if (!b_is_number) convert_num[id](&b, B, 0, j*m + SP_ROW(A)[k]);
+      
+	if (id == DOUBLE) {
+	  if (b.d == 0.0) goto divzero;
+	  SP_VALD(ret)[k] /= b.d;
+	}
+	else {
+	  if (b.z == 0.0) goto divzero;
+	  SP_VALZ(ret)[k] /= b.z;
+	}
+      }
+    }
+
+    return ret;
+  }
+
+ divzero:
+  Py_DECREF(ret);
+  PY_ERR(PyExc_ArithmeticError, "division by zero");
+}
+
 extern PyObject * matrix_exp(matrix *, PyObject *, PyObject *) ;
 extern PyObject * matrix_log(matrix *, PyObject *, PyObject *) ;
 extern PyObject * matrix_sqrt(matrix *, PyObject *, PyObject *) ;
 extern PyObject * matrix_cos(matrix *, PyObject *, PyObject *) ;
 extern PyObject * matrix_sin(matrix *, PyObject *, PyObject *) ;
-extern PyObject * matrix_elem_mul(matrix *, PyObject *, PyObject *) ;
-extern PyObject * matrix_elem_div(matrix *, PyObject *, PyObject *) ;
 
 static PyMethodDef base_functions[] = {
   {"exp", (PyCFunction)matrix_exp, METH_VARARGS|METH_KEYWORDS, 
@@ -999,12 +1693,15 @@ static PyMethodDef base_functions[] = {
   {"gemv", (PyCFunction)base_gemv, METH_VARARGS|METH_KEYWORDS, doc_gemv},
   {"syrk", (PyCFunction)base_syrk, METH_VARARGS|METH_KEYWORDS, doc_syrk},
   {"symv", (PyCFunction)base_symv, METH_VARARGS|METH_KEYWORDS, doc_symv},
-  {"mul", (PyCFunction)matrix_elem_mul, METH_VARARGS|METH_KEYWORDS, 
+  {"emul", (PyCFunction)matrix_elem_mul, METH_VARARGS|METH_KEYWORDS, 
    "elementwise product of two matrices"},
-  {"div", (PyCFunction)matrix_elem_div, METH_VARARGS|METH_KEYWORDS, 
+  {"ediv", (PyCFunction)matrix_elem_div, METH_VARARGS|METH_KEYWORDS, 
    "elementwise division between two matrices"},
-  {"sparse", (PyCFunction)sparse, METH_VARARGS|METH_KEYWORDS, 
-   "convenience function for creating sparse matrices"},
+  {"emin", (PyCFunction)matrix_elem_min, METH_VARARGS|METH_KEYWORDS, 
+   "elementwise minimum between two matrices"},
+  {"emax", (PyCFunction)matrix_elem_max, METH_VARARGS|METH_KEYWORDS, 
+   "elementwise maximum between two matrices"},
+  {"sparse", (PyCFunction)sparse, METH_VARARGS|METH_KEYWORDS, doc_sparse},
   {"spdiag", (PyCFunction)spdiag, METH_VARARGS|METH_KEYWORDS, doc_spdiag},
   {NULL}		/* sentinel */
 };
@@ -1013,7 +1710,7 @@ PyMODINIT_FUNC
 initbase(void)
 {  
   static void *base_API[8];
-  PyObject *c_api_object;
+  PyObject *base_mod, *c_api_object;
   
   if (!(base_mod = Py_InitModule3("base", base_functions, base__doc__)))
     return;
diff --git a/src/C/blas.c b/src/C/blas.c
index 0a38fc3..fdd202d 100644
--- a/src/C/blas.c
+++ b/src/C/blas.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -239,11 +239,15 @@ static PyObject* swap(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(x)){
         case DOUBLE: 
+            Py_BEGIN_ALLOW_THREADS
             dswap_(&n, MAT_BUFD(x)+ox, &ix, MAT_BUFD(y)+oy, &iy);
+            Py_END_ALLOW_THREADS
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS
             zswap_(&n, MAT_BUFZ(x)+ox, &ix, MAT_BUFZ(y)+oy, &iy);
+            Py_END_ALLOW_THREADS
             break;
 
         default:
@@ -289,14 +293,20 @@ static PyObject* scal(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE: 
             if (number_from_pyobject(ao, &a, MAT_ID(x))) 
                 err_type("alpha");
+            Py_BEGIN_ALLOW_THREADS
             dscal_(&n, &a.d, MAT_BUFD(x)+ox, &ix);
+            Py_END_ALLOW_THREADS
 	    break;
 
         case COMPLEX:
             if (!number_from_pyobject(ao, &a, DOUBLE)) 
+                Py_BEGIN_ALLOW_THREADS	    
                 zdscal_(&n, &a.d, MAT_BUFZ(x)+ox, &ix);
+                Py_END_ALLOW_THREADS
             else if (!number_from_pyobject(ao, &a, COMPLEX)) 
+                Py_BEGIN_ALLOW_THREADS	    
                 zscal_(&n, &a.z, MAT_BUFZ(x)+ox, &ix);
+                Py_END_ALLOW_THREADS
             else
                 err_type("alpha");
 	    break;
@@ -352,11 +362,15 @@ static PyObject* copy(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(x)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dcopy_(&n, MAT_BUFD(x)+ox, &ix, MAT_BUFD(y)+oy, &iy);
+            Py_END_ALLOW_THREADS
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zcopy_(&n, MAT_BUFZ(x)+ox, &ix, MAT_BUFZ(y)+oy, &iy);
+            Py_END_ALLOW_THREADS
             break;
 
         default:
@@ -418,12 +432,16 @@ static PyObject* axpy(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(x)){
         case DOUBLE:
             if (!ao) a.d=1.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             daxpy_(&n, &a.d, MAT_BUFD(x)+ox, &ix, MAT_BUFD(y)+oy, &iy);
+            Py_END_ALLOW_THREADS
             break;
 
         case COMPLEX:
             if (!ao) a.z=1.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             zaxpy_(&n, &a.z, MAT_BUFZ(x)+ox, &ix, MAT_BUFZ(y)+oy, &iy);
+            Py_END_ALLOW_THREADS
             break;
 
         default: 
@@ -487,8 +505,10 @@ static PyObject* dot(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(x)){
         case DOUBLE: 
+            Py_BEGIN_ALLOW_THREADS
             val.d = (n==0) ? 0.0 : ddot_(&n, MAT_BUFD(x)+ox, &ix, 
                 MAT_BUFD(y)+oy, &iy);
+            Py_END_ALLOW_THREADS
             return Py_BuildValue("d", val.d);
 
         case COMPLEX: 
@@ -500,6 +520,7 @@ static PyObject* dot(PyObject *self, PyObject *args, PyObject *kwrds)
 #else
                 ix *= 2;
                 iy *= 2;
+                Py_BEGIN_ALLOW_THREADS
                 val.z = (ddot_(&n, MAT_BUFD(x)+2*ox, &ix,
                     MAT_BUFD(y)+2*oy, &iy) + 
                     ddot_(&n, MAT_BUFD(x)+2*ox + 1, &ix, 
@@ -508,6 +529,7 @@ static PyObject* dot(PyObject *self, PyObject *args, PyObject *kwrds)
                     MAT_BUFD(y)+2*oy + 1, &iy) - 
                     ddot_(&n, MAT_BUFD(x)+2*ox + 1, &ix, 
                     MAT_BUFD(y)+2*oy, &iy));
+                Py_END_ALLOW_THREADS
 #endif
 	    return PyComplex_FromDoubles(creal(val.z),cimag(val.z));
 
@@ -570,19 +592,24 @@ static PyObject* dotu(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(x)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS
             val.d = (n==0) ? 0.0 : ddot_(&n, MAT_BUFD(x)+ox, &ix, 
                 MAT_BUFD(y)+oy, &iy);
+            Py_END_ALLOW_THREADS
             return Py_BuildValue("d", val.d);
 
         case COMPLEX:
 	    if (n==0) val.z = 0.0;
 	    else
 #if USE_CBLAS_ZDOT
+                Py_BEGIN_ALLOW_THREADS
                 cblas_zdotu_sub(n, MAT_BUFZ(x)+ox, ix, MAT_BUFZ(y)+oy, 
                     iy, &val.z);
+                Py_END_ALLOW_THREADS
 #else
                 ix *= 2;
                 iy *= 2;
+                Py_BEGIN_ALLOW_THREADS
                 val.z = (ddot_(&n, MAT_BUFD(x)+2*ox, &ix,
                     MAT_BUFD(y)+2*oy, &iy) - 
                     ddot_(&n, MAT_BUFD(x)+2*ox + 1, &ix, 
@@ -591,6 +618,7 @@ static PyObject* dotu(PyObject *self, PyObject *args, PyObject *kwrds)
                     MAT_BUFD(y)+2*oy + 1, &iy) + 
                     ddot_(&n, MAT_BUFD(x)+2*ox + 1, &ix, 
                     MAT_BUFD(y)+2*oy, &iy));
+                Py_END_ALLOW_THREADS
 #endif
 	    return PyComplex_FromDoubles(creal(val.z),cimag(val.z));
 
@@ -671,12 +699,19 @@ static PyObject* asum(PyObject *self, PyObject *args, PyObject *kwrds)
     if (n == 0) return Py_BuildValue("d", 0.0);
     if (len(x) < ox + 1+(n-1)*ix) err_buf_len("x");
 
+    double val;
     switch (MAT_ID(x)){
         case DOUBLE:
-            return Py_BuildValue("d", dasum_(&n, MAT_BUFD(x)+ox, &ix));
+            Py_BEGIN_ALLOW_THREADS
+	    val = dasum_(&n, MAT_BUFD(x)+ox, &ix);
+            Py_END_ALLOW_THREADS
+            return Py_BuildValue("d", val);
 
         case COMPLEX:
-            return Py_BuildValue("d", dzasum_(&n, MAT_BUFZ(x)+ox, &ix));
+            Py_BEGIN_ALLOW_THREADS
+	    val = dzasum_(&n, MAT_BUFZ(x)+ox, &ix);
+            Py_END_ALLOW_THREADS
+            return Py_BuildValue("d", val);
 
         default:
             err_invalid_id;
@@ -715,14 +750,19 @@ static PyObject* iamax(PyObject *self, PyObject *args, PyObject *kwrds)
     if (n == 0) return Py_BuildValue("i", 0);
     if (len(x) < ox + 1+(n-1)*ix) err_buf_len("x");
 
+    double val;
     switch (MAT_ID(x)){
         case DOUBLE:
-            return Py_BuildValue("i", 
-                idamax_(&n, MAT_BUFD(x)+ox, &ix)-1);
+            Py_BEGIN_ALLOW_THREADS
+      	    val = idamax_(&n, MAT_BUFD(x)+ox, &ix)-1;
+            Py_END_ALLOW_THREADS
+            return Py_BuildValue("i", val);
 
         case COMPLEX:
-            return Py_BuildValue("i", 
-                izamax_(&n, MAT_BUFZ(x)+ox, &ix)-1);
+            Py_BEGIN_ALLOW_THREADS
+	    val = izamax_(&n, MAT_BUFZ(x)+ox, &ix)-1;
+            Py_END_ALLOW_THREADS
+	    return Py_BuildValue("i", val);
 
         default:
             err_invalid_id;
@@ -821,24 +861,36 @@ static PyObject* gemv(PyObject *self, PyObject *args, PyObject *kwrds)
             if (!ao) a.d=1.0; 
             if (!bo) b.d=0.0;
             if (trans == 'N' && n == 0)
+                Py_BEGIN_ALLOW_THREADS	    
                 dscal_(&m, &b.d, MAT_BUFD(y)+oy, &iy);
+                Py_END_ALLOW_THREADS	    
             else if ((trans == 'T' || trans == 'C') && m == 0)
+                Py_BEGIN_ALLOW_THREADS	    
                 dscal_(&n, &b.d, MAT_BUFD(y)+oy, &iy);
+                Py_END_ALLOW_THREADS	    
             else 
+                Py_BEGIN_ALLOW_THREADS	    
                 dgemv_(&trans, &m, &n, &a.d, MAT_BUFD(A)+oA, &ldA, 
                     MAT_BUFD(x)+ox, &ix, &b.d, MAT_BUFD(y)+oy, &iy);
+                Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z=1.0; 
             if (!bo) b.z=0.0;
             if (trans == 'N' && n == 0)
+                Py_BEGIN_ALLOW_THREADS	    
                 zscal_(&m, &b.z, MAT_BUFZ(y)+oy, &iy);
+                Py_END_ALLOW_THREADS	    
             else if ((trans == 'T' || trans == 'C') && m == 0)
+                Py_BEGIN_ALLOW_THREADS	    
                 zscal_(&n, &b.z, MAT_BUFZ(y)+oy, &iy);
+                Py_END_ALLOW_THREADS	    
             else 
+                Py_BEGIN_ALLOW_THREADS	    
                 zgemv_(&trans, &m, &n, &a.z, MAT_BUFZ(A)+oA, &ldA, 
                     MAT_BUFZ(x)+ox, &ix, &b.z, MAT_BUFZ(y)+oy, &iy);
+                Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -947,26 +999,36 @@ static PyObject* gbmv(PyObject *self, PyObject *args, PyObject *kwrds)
             if (!ao) a.d=1.0; 
             if (!bo) b.d=0.0;
             if (trans == 'N' && n == 0)
+                Py_BEGIN_ALLOW_THREADS	    
                 dscal_(&m, &b.d, MAT_BUFD(y)+oy, &iy);
+                Py_END_ALLOW_THREADS	    
             else if ((trans == 'T' || trans == 'C') && m == 0)
+                Py_BEGIN_ALLOW_THREADS	    
                 dscal_(&n, &b.d, MAT_BUFD(y)+oy, &iy);
+                Py_END_ALLOW_THREADS	    
             else 
+                Py_BEGIN_ALLOW_THREADS	    
                 dgbmv_(&trans, &m, &n, &kl, &ku, &a.d, MAT_BUFD(A)+oA, 
-                    &ldA, MAT_BUFD(x)+ox, &ix, &b.d, MAT_BUFD(y)+oy, 
-                    &iy);
+                    &ldA, MAT_BUFD(x)+ox, &ix, &b.d, MAT_BUFD(y)+oy, &iy);
+	        Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z=1.0; 
             if (!bo) b.z=0.0;
             if (trans == 'N' && n == 0)
+                Py_BEGIN_ALLOW_THREADS	    
                 zscal_(&m, &b.z, MAT_BUFZ(y)+oy, &iy);
+                Py_END_ALLOW_THREADS	    
             else if ((trans == 'T' || trans == 'C') && m == 0)
+                Py_BEGIN_ALLOW_THREADS	    
                 zscal_(&n, &b.z, MAT_BUFZ(y)+oy, &iy);
+                Py_END_ALLOW_THREADS	    
             else 
+                Py_BEGIN_ALLOW_THREADS	    
                 zgbmv_(&trans, &m, &n, &kl, &ku, &a.z, MAT_BUFZ(A)+oA, 
-                    &ldA, MAT_BUFZ(x)+ox, &ix, &b.z, MAT_BUFZ(y)+oy, 
-                    &iy);
+                    &ldA, MAT_BUFZ(x)+ox, &ix, &b.z, MAT_BUFZ(y)+oy, &iy);
+                Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1056,8 +1118,10 @@ static PyObject* symv(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             if (!ao) a.d=1.0; 
             if (!bo) b.d=0.0;
+            Py_BEGIN_ALLOW_THREADS	    
             dsymv_(&uplo, &n, &a.d, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(x)+ox, &ix, &b.d, MAT_BUFD(y)+oy, &iy);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1152,15 +1216,19 @@ static PyObject* hemv(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             if (!ao) a.d=1.0; 
             if (!bo) b.d=0.0;
+            Py_BEGIN_ALLOW_THREADS	    
             dsymv_(&uplo, &n, &a.d, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(x)+ox, &ix, &b.d, MAT_BUFD(y)+oy, &iy);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z=1.0; 
             if (!bo) b.z=0.0;
+            Py_BEGIN_ALLOW_THREADS	    
             zhemv_(&uplo, &n, &a.z, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(x)+ox, &ix, &b.z, MAT_BUFZ(y)+oy, &iy);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1246,8 +1314,10 @@ static PyObject* sbmv(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             if (!ao) a.d=1.0; 
             if (!bo) b.d=0.0;
+            Py_BEGIN_ALLOW_THREADS	    
             dsbmv_(&uplo, &n, &k, &a.d, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(x)+ox, &ix, &b.d, MAT_BUFD(y)+oy, &iy);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1336,15 +1406,19 @@ static PyObject* hbmv(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             if (!ao) a.d=1.0; 
             if (!bo) b.d=0.0;
+            Py_BEGIN_ALLOW_THREADS	    
             dsbmv_(&uplo, &n, &k, &a.d, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(x)+ox, &ix, &b.d, MAT_BUFD(y)+oy, &iy);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z=1.0; 
             if (!bo) b.z=0.0;
+            Py_BEGIN_ALLOW_THREADS	    
             zhbmv_(&uplo, &n, &k, &a.z, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(x)+ox, &ix, &b.z, MAT_BUFZ(y)+oy, &iy);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1420,13 +1494,17 @@ static PyObject* trmv(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(x)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dtrmv_(&uplo, &trans, &diag, &n, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(x)+ox, &ix);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             ztrmv_(&uplo, &trans, &diag, &n, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(x)+ox, &ix);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1501,13 +1579,17 @@ static PyObject* tbmv(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(x)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dtbmv_(&uplo, &trans, &diag, &n, &k, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(x)+ox, &ix);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             ztbmv_(&uplo, &trans, &diag, &n, &k, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(x)+ox, &ix);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1586,13 +1668,17 @@ static PyObject* trsv(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(x)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dtrsv_(&uplo, &trans, &diag, &n, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(x)+ox, &ix);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             ztrsv_(&uplo, &trans, &diag, &n, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(x)+ox, &ix);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1667,13 +1753,17 @@ static PyObject* tbsv(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(x)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dtbsv_(&uplo, &trans, &diag, &n, &k, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(x)+ox, &ix);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             ztbsv_(&uplo, &trans, &diag, &n, &k, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(x)+ox, &ix);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1749,14 +1839,18 @@ static PyObject* ger(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(x)){
         case DOUBLE:
             if (!ao) a.d = 1.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             dger_(&m, &n, &a.d, MAT_BUFD(x)+ox, &ix, MAT_BUFD(y)+oy, 
                 &iy, MAT_BUFD(A)+oA, &ldA);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z = 1.0; 
-            zgerc_(&m, &n, &a.z, MAT_BUFZ(x)+ox, &ix, MAT_BUFZ(y)+oy, 
-                &iy, MAT_BUFZ(A)+oA, &ldA);
+            Py_BEGIN_ALLOW_THREADS	    
+            zgerc_(&m, &n, &a.z, MAT_BUFZ(x)+ox, &ix, MAT_BUFZ(y)+oy, &iy,
+                MAT_BUFZ(A)+oA, &ldA);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1832,14 +1926,18 @@ static PyObject* geru(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(x)){
         case DOUBLE:
             if (!ao) a.d = 1.0; 
-            dger_(&m, &n, &a.d, MAT_BUFD(x)+ox, &ix, MAT_BUFD(y)+oy, 
-                &iy, MAT_BUFD(A)+oA, &ldA);
+            Py_BEGIN_ALLOW_THREADS	    
+            dger_(&m, &n, &a.d, MAT_BUFD(x)+ox, &ix, MAT_BUFD(y)+oy, &iy, 
+                MAT_BUFD(A)+oA, &ldA);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z = 1.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             zgeru_(&m, &n, &a.z, MAT_BUFZ(x)+ox, &ix, MAT_BUFZ(y)+oy, 
                 &iy, MAT_BUFZ(A)+oA, &ldA);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1914,8 +2012,10 @@ static PyObject* syr(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(x)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dsyr_(&uplo, &n, &a.d, MAT_BUFD(x)+ox, &ix, MAT_BUFD(A)+oA,
                 &ldA);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1990,13 +2090,17 @@ static PyObject* her(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(x)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dsyr_(&uplo, &n, &a.d, MAT_BUFD(x)+ox, &ix, MAT_BUFD(A)+oA,
                 &ldA);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zher_(&uplo, &n, &a.d, MAT_BUFZ(x)+ox, &ix, MAT_BUFZ(A)+oA,
                 &ldA);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -2079,8 +2183,10 @@ static PyObject* syr2(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(x)){
         case DOUBLE:
             if (!ao) a.d = 1.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             dsyr2_(&uplo, &n, &a.d, MAT_BUFD(x)+ox, &ix, MAT_BUFD(y)+oy,
                 &iy, MAT_BUFD(A)+oA, &ldA);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -2164,14 +2270,18 @@ static PyObject* her2(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(x)){
         case DOUBLE:
             if (!ao) a.d = 1.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             dsyr2_(&uplo, &n, &a.d, MAT_BUFD(x)+ox, &ix, MAT_BUFD(y)+oy,
                 &iy, MAT_BUFD(A)+oA, &ldA);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z = 1.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             zher2_(&uplo, &n, &a.z, MAT_BUFZ(x)+ox, &ix, MAT_BUFZ(y)+oy,
                 &iy, MAT_BUFZ(A)+oA, &ldA);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -2299,17 +2409,21 @@ static PyObject* gemm(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             if (!ao) a.d = 1.0; 
             if (!bo) b.d = 0.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             dgemm_(&transA, &transB, &m, &n, &k, &a.d, 
                 MAT_BUFD(A)+oA, &ldA, MAT_BUFD(B)+oB, &ldB, &b.d, 
                 MAT_BUFD(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z = 1.0; 
             if (!bo) b.z = 0.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             zgemm_(&transA, &transB, &m, &n, &k, &a.z, 
                 MAT_BUFZ(A)+oA, &ldA, MAT_BUFZ(B)+oB, &ldB, &b.z, 
                 MAT_BUFZ(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -2427,15 +2541,19 @@ static PyObject* symm(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             if (!ao) a.d = 1.0; 
             if (!bo) b.d = 0.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             dsymm_(&side, &uplo, &m, &n, &a.d, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(B)+oB, &ldB, &b.d, MAT_BUFD(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z = 1.0; 
             if (!bo) b.z = 0.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             zsymm_(&side, &uplo, &m, &n, &a.z, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(B)+oB, &ldB, &b.z, MAT_BUFZ(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -2553,15 +2671,19 @@ static PyObject* hemm(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             if (!ao) a.d = 1.0; 
             if (!bo) b.d = 0.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             dsymm_(&side, &uplo, &m, &n, &a.d, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(B)+oB, &ldB, &b.d, MAT_BUFD(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z = 1.0; 
             if (!bo) b.z = 0.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             zhemm_(&side, &uplo, &m, &n, &a.z, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(B)+oB, &ldB, &b.z, MAT_BUFZ(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -2657,15 +2779,19 @@ static PyObject* syrk(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             if (!ao) a.d = 1.0; 
             if (!bo) b.d = 0.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             dsyrk_(&uplo, &trans, &n, &k, &a.d, MAT_BUFD(A)+oA, &ldA, 
                 &b.d, MAT_BUFD(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z = 1.0; 
             if (!bo) b.z = 0.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             zsyrk_(&uplo, &trans, &n, &k, &a.z, MAT_BUFZ(A)+oA, &ldA, 
                 &b.z, MAT_BUFZ(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -2756,13 +2882,17 @@ static PyObject* herk(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dsyrk_(&uplo, &trans, &n, &k, &a.d, MAT_BUFD(A)+oA, &ldA, 
                 &b.d, MAT_BUFD(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zherk_(&uplo, &trans, &n, &k, &a.d, MAT_BUFZ(A)+oA, &ldA, 
                 &b.d, MAT_BUFZ(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -2893,15 +3023,19 @@ static PyObject* syr2k(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             if (!ao) a.d = 1.0; 
             if (!bo) b.d = 0.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             dsyr2k_(&uplo, &trans, &n, &k, &a.d, MAT_BUFD(A)+oA, &ldA,
                 MAT_BUFD(B)+oB, &ldB, &b.d, MAT_BUFD(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
             if (!ao) a.z = 1.0; 
             if (!bo) b.z = 0.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             zsyr2k_(&uplo, &trans, &n, &k, &a.z, MAT_BUFZ(A)+oA, &ldA,
                 MAT_BUFZ(B)+oB, &ldB, &b.z, MAT_BUFZ(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -3032,14 +3166,18 @@ static PyObject* her2k(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             if (!ao) a.d = 1.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             dsyr2k_(&uplo, &trans, &n, &k, &a.d, MAT_BUFD(A)+oA, &ldA,
                 MAT_BUFD(B)+oB, &ldB, &b.d, MAT_BUFD(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
 	    if (!ao) a.z = 1.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             zher2k_(&uplo, &trans, &n, &k, &a.z, MAT_BUFZ(A)+oA, &ldA,
                 MAT_BUFZ(B)+oB, &ldB, &b.d, MAT_BUFZ(C)+oC, &ldC);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -3148,14 +3286,18 @@ static PyObject* trmm(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             if (!ao) a.d = 1.0; 
+            Py_BEGIN_ALLOW_THREADS	    
             dtrmm_(&side, &uplo, &transA, &diag, &m, &n, &a.d, 
                 MAT_BUFD(A)+oA, &ldA, MAT_BUFD(B)+oB, &ldB);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
    	    if (!ao) a.z = 1.0;
+            Py_BEGIN_ALLOW_THREADS	    
             ztrmm_(&side, &uplo, &transA, &diag, &m, &n, &a.z, 
                 MAT_BUFZ(A)+oA, &ldA, MAT_BUFZ(B)+oB, &ldB);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -3267,14 +3409,18 @@ static PyObject* trsm(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             if (!ao) a.d = 1.0; 
+             Py_BEGIN_ALLOW_THREADS	    
             dtrsm_(&side, &uplo, &transA, &diag, &m, &n, &a.d, 
                 MAT_BUFD(A)+oA, &ldA, MAT_BUFD(B)+oB, &ldB);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
   	    if (!ao) a.z = 1.0;
+            Py_BEGIN_ALLOW_THREADS	    
             ztrsm_(&side, &uplo, &transA, &diag, &m, &n, &a.z, 
                 MAT_BUFZ(A)+oA, &ldA, MAT_BUFZ(B)+oB, &ldB);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
diff --git a/src/C/blas_redefines.h b/src/C/blas_redefines.h
index 7b60c44..e07d11e 100644
--- a/src/C/blas_redefines.h
+++ b/src/C/blas_redefines.h
@@ -136,5 +136,10 @@
 #define zpbtrs_ zpbtrs
 #define dpttrs_ dpttrs
 #define zpttrs_ zpttrs
+#define dlacpy_ dlacpy
+#define dgees_ dgees
+#define zgees_ zgees
+#define dgges_ dgges
+#define zgges_ zgges
 
 #endif
diff --git a/src/C/cholmod.c b/src/C/cholmod.c
index 92f57a0..8684840 100644
--- a/src/C/cholmod.c
+++ b/src/C/cholmod.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/C/cvxopt.h b/src/C/cvxopt.h
index 61f6161..490c7f1 100644
--- a/src/C/cvxopt.h
+++ b/src/C/cvxopt.h
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/C/dense.c b/src/C/dense.c
index 2ebf343..c31d221 100644
--- a/src/C/dense.c
+++ b/src/C/dense.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -40,7 +40,6 @@ typedef struct {
 static const char PY_ARRAY_TC[3] = { 'i', 'f', 'c' }; 
 
 /* prototyping and forward declarations */
-extern PyObject *base_mod;
 extern void (*axpy[])(int *, number *, void *, int *, void *, int *) ;
 extern void (*scal[])(int *, number *, void *, int *) ;
 extern void (*gemm[])(char *, char *, int *, int *, int *, void *, void *, 
@@ -524,14 +523,111 @@ matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
   return (PyObject *)ret;
 }
 
+PyObject *matrix_sub(PyObject *, PyObject *);
+
+static PyObject * 
+matrix_richcompare(PyObject *self, PyObject *other, int op) {
+
+  PY_ERR(PyExc_NotImplementedError, "matrix comparison not implemented");  
+}
+
+/*
+static PyObject * 
+matrix_richcompare(PyObject *self, PyObject *other, int op) {
+
+  if (((Matrix_Check(self) || SpMatrix_Check(self)) && X_ID(self)==COMPLEX) ||
+      ((Matrix_Check(other) || SpMatrix_Check(other)) && X_ID(other)==COMPLEX))
+    PY_ERR_TYPE("complex comparison not defined");
+    
+  if (PyComplex_Check(self) || PyComplex_Check(other))
+    PY_ERR_TYPE("complex comparison not defined");
+    
+  if (!Matrix_Check(self) && !SpMatrix_Check(self) && !PyNumber_Check(self)) 
+  {
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+  }
+
+  if (!Matrix_Check(other) && !SpMatrix_Check(other) && !PyNumber_Check(other))
+  {
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+  }
+      
+  if ( (Matrix_Check(self) || SpMatrix_Check(other)) &&
+      (Matrix_Check(other) || SpMatrix_Check(other)) && 
+      ((X_NROWS(self)*X_NCOLS(self) > 1) &&
+	  (X_NROWS(other)*X_NCOLS(other) > 1)) &&
+      (X_NROWS(self) != X_NROWS(other) || X_NCOLS(self) != X_NCOLS(other)) )
+    PY_ERR_TYPE("matrices in comparison must have same dimensions");
+
+  PyObject *tmp = matrix_sub(self, other);
+  if (!tmp) return PyErr_NoMemory();
+
+  PyObject *ret = (PyObject *)Matrix_New(X_NROWS(self), X_NCOLS(self), INT);
+  if (!ret) {
+    Py_DECREF(tmp); 
+    return PyErr_NoMemory();
+  }
+
+  int i;
+  switch (op) {
+  case Py_LT :
+    for (i=0; i<MAT_LGT(ret); i++) 
+      MAT_BUFI(ret)[i] = (MAT_ID(tmp) == INT ? MAT_BUFI(tmp)[i] < 0:
+	  MAT_BUFD(tmp)[i] < 0);
+    break;
+    
+  case Py_LE :
+    for (i=0; i<MAT_LGT(ret); i++) 
+      MAT_BUFI(ret)[i] = (MAT_ID(tmp) == INT ? MAT_BUFI(tmp)[i] <= 0:
+	  MAT_BUFD(tmp)[i] <= 0);
+    break;
+    
+  case Py_GT :
+    for (i=0; i<MAT_LGT(ret); i++) 
+      MAT_BUFI(ret)[i] = (MAT_ID(tmp) == INT ? MAT_BUFI(tmp)[i] > 0:
+	  MAT_BUFD(tmp)[i] > 0);
+    break;
+      
+  case Py_GE :
+    for (i=0; i<MAT_LGT(ret); i++) 
+      MAT_BUFI(ret)[i] = (MAT_ID(tmp) == INT ? MAT_BUFI(tmp)[i] >= 0:
+	  MAT_BUFD(tmp)[i] >= 0);
+    break;
+
+  case Py_EQ :
+    for (i=0; i<MAT_LGT(ret); i++) 
+      MAT_BUFI(ret)[i] = (MAT_ID(tmp) == INT ? MAT_BUFI(tmp)[i] == 0:
+	  MAT_BUFD(tmp)[i] == 0);
+    break;
+    
+  case Py_NE :
+    for (i=0; i<MAT_LGT(ret); i++) 
+      MAT_BUFI(ret)[i] = (MAT_ID(tmp) == INT ? MAT_BUFI(tmp)[i] != 0:
+	  MAT_BUFD(tmp)[i] != 0);
+    break;
+      
+  default: break;
+  }
+  
+  Py_DECREF(tmp);
+  return ret;
+}
+*/
+
 static PyObject *
 matrix_str(matrix *self) {
 
+  PyObject *cvxopt = PyImport_ImportModule("cvxopt");
   PyObject *str, *ret;
-   
-  if (!(str = PyObject_GetAttrString(base_mod, "matrix_str")))
-    PY_ERR(PyExc_KeyError, "missing 'matrix_str' in 'base_options'");
+  
+  if (!(str = PyObject_GetAttrString(cvxopt, "matrix_str"))) {
+    Py_DECREF(cvxopt);
+    PY_ERR(PyExc_KeyError, "missing 'matrix_str' in 'cvxopt'");
+  }
 
+  Py_DECREF(cvxopt);
   if (!PyCallable_Check(str)) PY_ERR_TYPE("'matrix_str' is not callable");
 
   ret = PyObject_CallFunctionObjArgs(str, (PyObject *)self, NULL);
@@ -543,17 +639,21 @@ matrix_str(matrix *self) {
 static PyObject *
 matrix_repr(matrix *self) {
 
+  PyObject *cvxopt = PyImport_ImportModule("cvxopt");
   PyObject *repr, *ret;
-   
-  if (!(repr = PyObject_GetAttrString(base_mod, "matrix_repr")))
-    PY_ERR(PyExc_KeyError, "missing 'matrix_repr' in 'base_options'");
+  
+  if (!(repr = PyObject_GetAttrString(cvxopt, "matrix_repr"))) {
+    Py_DECREF(cvxopt);
+    PY_ERR(PyExc_KeyError, "missing 'matrix_repr' in 'cvxopt'");
+  }
 
+  Py_DECREF(cvxopt);
   if (!PyCallable_Check(repr)) PY_ERR_TYPE("'matrix_repr' is not callable");
 
   ret = PyObject_CallFunctionObjArgs(repr, (PyObject *)self, NULL);
   Py_DECREF(repr);
 
-  return ret;
+  return ret;  
 }
 
 /*
@@ -1182,7 +1282,7 @@ PyTypeObject matrix_tp = {
 	0,					/* tp_doc */
 	0,					/* tp_traverse */
 	0,					/* tp_clear */
-	0,	      		                /* tp_richcompare */
+	(richcmpfunc)matrix_richcompare,        /* tp_richcompare */
 	0,					/* tp_weaklistoffset */
 	(getiterfunc)matrix_iter,		/* tp_iter */
 	0,	       	                        /* tp_iternext */
@@ -1416,7 +1516,6 @@ static PyObject * matrix_isub(PyObject *self,PyObject *other)
 static PyObject * 
 matrix_mul_generic(PyObject *self, PyObject *other, int inplace) 
 {
-
   if (!(Matrix_Check(self) || PY_NUMBER(self)) || 
       !(Matrix_Check(other) || PY_NUMBER(other))) {
     Py_INCREF(Py_NotImplemented);
@@ -1680,6 +1779,17 @@ static PyObject * matrix_pow(PyObject *self, PyObject *other)
   return (PyObject *)Y;
 }
 
+static int matrix_nonzero(matrix *self)
+{
+  int i, res = 0;
+  for (i=0; i<MAT_LGT(self); i++) {
+    if ((MAT_ID(self) == INT) && (MAT_BUFI(self)[i] != 0)) res = 1;
+    else if ((MAT_ID(self) == DOUBLE) && (MAT_BUFD(self)[i] != 0)) res = 1;    
+    else if ((MAT_ID(self) == COMPLEX) && (MAT_BUFZ(self)[i] != 0)) res = 1;
+  }
+  
+  return res;
+}
 
 static PyNumberMethods matrix_as_number = {
 	(binaryfunc)matrix_add,	/*nb_add*/
@@ -1692,7 +1802,7 @@ static PyNumberMethods matrix_as_number = {
 	(unaryfunc)matrix_neg,	/*nb_negative*/
 	(unaryfunc)matrix_pos,	/*nb_positive*/
 	(unaryfunc)matrix_abs,	/*nb_absolute*/
-	0,	                /*nb_nonzero*/
+	(inquiry)matrix_nonzero,/*nb_nonzero*/
 	0,	                /*nb_invert*/
 	0,	                /*nb_lshift*/
 	0,	                /*nb_rshift*/
@@ -2050,67 +2160,4 @@ PyObject * matrix_sin(matrix *self, PyObject *args, PyObject *kwrds)
   else PY_ERR_TYPE("argument must a be a number or dense matrix");
 }
 
-PyObject * matrix_elem_mul(matrix *self, PyObject *args, PyObject *kwrds)
-{
-  matrix *A, *B;
-
-  if (!PyArg_ParseTuple(args, "OO", &A, &B)) return NULL;
-
-  if (!Matrix_Check(A) || !Matrix_Check(B) || MAT_ID(A) != MAT_ID(B))
-    PY_ERR_TYPE("arguments must be matrices of same type");
-
-  if (MAT_NROWS(A) != MAT_NROWS(B) || MAT_NCOLS(A) != MAT_NCOLS(B))
-    PY_ERR_TYPE("arguments must have same dimensions");
-
-  matrix *ret = Matrix_New(MAT_NROWS(A), MAT_NCOLS(A), MAT_ID(A));
-  if (!ret) return PyErr_NoMemory();
-
-  int i;
-  for (i=0; i<MAT_LGT(A); i++) {
-    switch (MAT_ID(A)) {
-    case INT: MAT_BUFI(ret)[i] = MAT_BUFI(A)[i]*MAT_BUFI(B)[i]; break;
-    case DOUBLE: MAT_BUFD(ret)[i] = MAT_BUFD(A)[i]*MAT_BUFD(B)[i]; break;
-    case COMPLEX: MAT_BUFZ(ret)[i] = MAT_BUFZ(A)[i]*MAT_BUFZ(B)[i]; break;    
-    }    
-  }
-  
-  return (PyObject *)ret;
-}
-
-PyObject * matrix_elem_div(matrix *self, PyObject *args, PyObject *kwrds)
-{
-  matrix *A, *B;
-
-  if (!PyArg_ParseTuple(args, "OO", &A, &B)) return NULL;
-
-  if (!Matrix_Check(A) || !Matrix_Check(B) || MAT_ID(A) != MAT_ID(B))
-    PY_ERR_TYPE("arguments must be matrices of same type");
-
-  if (MAT_NROWS(A) != MAT_NROWS(B) || MAT_NCOLS(A) != MAT_NCOLS(B))
-    PY_ERR_TYPE("arguments must have same dimensions");
-
-  matrix *ret = Matrix_New(MAT_NROWS(A), MAT_NCOLS(A), MAT_ID(A));
-  if (!ret) return PyErr_NoMemory();
-
-  int i;
-  for (i=0; i<MAT_LGT(A); i++) {
-    switch (MAT_ID(A)) {
-    case INT: 
-      if (MAT_BUFI(B)[i] == 0) goto divzero;
-      MAT_BUFI(ret)[i] = MAT_BUFI(A)[i]/MAT_BUFI(B)[i]; break;
-    case DOUBLE: 
-      if (MAT_BUFD(B)[i] == 0.0) goto divzero;
-      MAT_BUFD(ret)[i] = MAT_BUFD(A)[i]/MAT_BUFD(B)[i]; break;
-    case COMPLEX: 
-      if (MAT_BUFZ(B)[i] == 0.0) goto divzero;
-      MAT_BUFZ(ret)[i] = MAT_BUFZ(A)[i]/MAT_BUFZ(B)[i]; break;    
-    }    
-  }
-  
-  return (PyObject *)ret;
-
- divzero:
-  Py_DECREF(ret);
-  PY_ERR(PyExc_ArithmeticError, "division by zero");
-}
 
diff --git a/src/C/dsdp.c b/src/C/dsdp.c
index fefa332..c51c542 100644
--- a/src/C/dsdp.c
+++ b/src/C/dsdp.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -360,7 +360,9 @@ static PyObject* solvesdp(PyObject *self, PyObject *args,
 	goto done;
     }
     DSDPStopReason(sdp, &info);
-    if (info != DSDP_CONVERGED){
+    if (info != DSDP_CONVERGED && info != DSDP_SMALL_STEPS && 
+        info != DSDP_INDEFINITE_SCHUR_MATRIX && info != DSDP_MAX_IT 
+        && info != DSDP_NUMERICAL_ERROR && info != DSDP_UPPERBOUND ){
         PyErr_SetObject(PyExc_ArithmeticError, Py_BuildValue("i",info));
         t = NULL;
         goto done;
@@ -376,23 +378,28 @@ static PyObject* solvesdp(PyObject *self, PyObject *args,
     }
     DSDPGetSolutionType(sdp, &status);
 
-    switch (status){
-        case DSDP_PDFEASIBLE:
-            PyTuple_SET_ITEM(t, 0, (PyObject *) 
-                PyString_FromString("DSDP_PDFEASIBLE"));
-	    break;
-        case DSDP_UNBOUNDED:
-            PyTuple_SET_ITEM(t, 0, (PyObject *) 
-                PyString_FromString("DSDP_UNBOUNDED"));
-	    break;
-        case DSDP_INFEASIBLE:
-            PyTuple_SET_ITEM(t, 0, (PyObject *) 
-                PyString_FromString("DSDP_INFEASIBLE"));
-	    break;
-        case DSDP_PDUNKNOWN:
-            PyTuple_SET_ITEM(t, 0, (PyObject *) 
-                PyString_FromString("DSDP_UNKNOWN"));
-	    break;
+    if (info == DSDP_CONVERGED) {
+        switch (status){
+            case DSDP_PDFEASIBLE:
+                PyTuple_SET_ITEM(t, 0, (PyObject *) 
+                    PyString_FromString("DSDP_PDFEASIBLE"));
+	        break;
+            case DSDP_UNBOUNDED:
+                PyTuple_SET_ITEM(t, 0, (PyObject *) 
+                    PyString_FromString("DSDP_UNBOUNDED"));
+	        break;
+            case DSDP_INFEASIBLE:
+                PyTuple_SET_ITEM(t, 0, (PyObject *) 
+                    PyString_FromString("DSDP_INFEASIBLE"));
+	        break;
+            case DSDP_PDUNKNOWN:
+                PyTuple_SET_ITEM(t, 0, (PyObject *) 
+                    PyString_FromString("DSDP_UNKNOWN"));
+	        break;
+        }
+    } else {
+        PyTuple_SET_ITEM(t, 0, (PyObject *) 
+        PyString_FromString("DSDP_UNKNOWN"));
     }
 
     DSDPGetY(sdp, MAT_BUFD(x), n);
diff --git a/src/C/fftw.c b/src/C/fftw.c
index de7eea9..c7b1bc3 100644
--- a/src/C/fftw.c
+++ b/src/C/fftw.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -53,37 +53,106 @@ static PyObject *dft(PyObject *self, PyObject *args, PyObject *kwrds)
       X->buffer, &m, 1, m,
       FFTW_FORWARD, FFTW_ESTIMATE);
 
+  Py_BEGIN_ALLOW_THREADS
   fftw_execute(p);   
+  Py_END_ALLOW_THREADS
   fftw_destroy_plan(p);  
   return Py_BuildValue("");
 }
 
-static char doc_dft2[] = 
-    "2D-DFT of a matrix,  X := dft2(X)\n\n" 
+static char doc_dftn[] = 
+    "N-dimensional DFT of a matrix.\n"
+    "X := dftn(X, dims)\n\n" 
     "PURPOSE\n"
-    "Computes the 2D-DFT of a dense matrix X.\n\n"
+    "Computes the DFT of an N-dimensional array represented by a dense\n"
+    "matrix X. The shape of the matrix X does not matter, but the data\n"
+    "must be arranged in row-major-order.  The total dimension (defined\n"
+    "as mul(dims)) must equal the length of the array X.\n\n"
     "ARGUMENTS\n"
-    "X         A dense matrix of typecode 'z'.";
+    "X         A dense matrix of typecode 'd'.\n\n"
+    "dims      a tuple with the dimensions of the array.";
 
-static PyObject *dft2(PyObject *self, PyObject *args, PyObject *kwrds)
+static PyObject *dftn(PyObject *self, PyObject *args, PyObject *kwrds)
 {
   matrix *X;
-  char *kwlist[] = {"X", NULL};
+  PyObject *dims = NULL;
+  char *kwlist[] = {"X", "dims", NULL};
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O", kwlist, &X)) 
+  int *dimarr;
+  int free_dims = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|O:dftn", kwlist, &X, &dims)) 
     return NULL;
 
   if (!(Matrix_Check(X) && MAT_ID(X) == COMPLEX))     
-    PY_ERR(PyExc_ValueError, "X must be a dense matrix with type 'z'");
+    PY_ERR_TYPE("X must be a dense matrix with type 'z'");
+   
+  if (!dims) {
+    dims = PyTuple_New(2);
+    if (!dims) return PyErr_NoMemory();
 
-  int m = X->nrows, n = X->ncols; 
-  if (m == 0 || n == 0) return Py_BuildValue("");
+    PyTuple_SET_ITEM(dims, 0, PyInt_FromLong(MAT_NCOLS(X)));
+    PyTuple_SET_ITEM(dims, 1, PyInt_FromLong(MAT_NROWS(X)));
+    free_dims = 1;
+  }
+
+  if (!PyTuple_Check(dims)) {
+    if (free_dims) { Py_DECREF(dims); }
+    PY_ERR_TYPE("invalid dimension tuple");
+  }
+
+  int len = PySequence_Size(dims);  
+  PyObject *seq = PySequence_Fast(dims, "list is not iterable");
+
+  if (!(dimarr = malloc(len*sizeof(int)))) {
+    if (free_dims) { Py_DECREF(dims); }
+    Py_DECREF(seq);
+    return PyErr_NoMemory();
+  }
+
+  int i, proddim = 1;
+  for (i=0; i<len; i++) {
+    PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+	
+    if (!PyInt_Check(item)) {
+      if (free_dims) { Py_DECREF(dims); }
+      Py_DECREF(seq);
+      free(dimarr); 
+      PY_ERR_TYPE("non-integer in dimension tuple");
+    }
+    
+    dimarr[len-i-1] = PyInt_AS_LONG(item);
+    if (dimarr[len-i-1] < 0) {
+      if (free_dims) { Py_DECREF(dims); }
+      Py_DECREF(seq);
+      free(dimarr); 
+      PY_ERR(PyExc_ValueError, "negative dimension");
+    }        
+    proddim *= dimarr[len-i-1];
+  }
+
+  if (free_dims) { Py_DECREF(dims); }
 
-  fftw_plan p = fftw_plan_dft_2d(n, m,
+  Py_DECREF(seq);
+
+  if (proddim != MAT_LGT(X)) {
+    free(dimarr); 
+    PY_ERR_TYPE("length of X does not match dimensions");
+  }
+    
+  if (proddim == 0) {
+    free(dimarr); 
+    return Py_BuildValue("");
+  }
+
+  fftw_plan p = fftw_plan_dft(len, dimarr,
       X->buffer, X->buffer, FFTW_FORWARD, FFTW_ESTIMATE);
 
+  Py_BEGIN_ALLOW_THREADS
   fftw_execute(p);   
+  Py_END_ALLOW_THREADS
   fftw_destroy_plan(p);  
+  free(dimarr); 
   return Py_BuildValue("");
 }
 
@@ -113,7 +182,9 @@ static PyObject *idft(PyObject *self, PyObject *args, PyObject *kwrds)
       X->buffer, &m, 1, m,
       FFTW_BACKWARD, FFTW_ESTIMATE);
   
+  Py_BEGIN_ALLOW_THREADS
   fftw_execute(p); 
+  Py_END_ALLOW_THREADS
   
   number a;
   a.z = 1.0/m;
@@ -124,39 +195,105 @@ static PyObject *idft(PyObject *self, PyObject *args, PyObject *kwrds)
   return Py_BuildValue("");  
 }
 
-static char doc_idft2[] = 
-    "2D-IDFT of a matrix,  X := idft2(X)\n\n" 
+static char doc_idftn[] = 
+    "Inverse N-dimensional DFT of a matrix.\n"
+    "X := idftn(X, dims)\n\n" 
     "PURPOSE\n"
-    "Computes the inverse 2D-DFT of a dense matrix X.\n\n"
+    "Computes the IDFT of an N-dimensional array represented by a dense\n"
+    "matrix X. The shape of the matrix X does not matter, but the data\n"
+    "must be arranged in row-major-order.  The total dimension (defined\n"
+    "as mul(dims)) must equal the length of the array X.\n\n"
     "ARGUMENTS\n"
-    "X         A dense matrix of typecode 'z'.";
+    "X         A dense matrix of typecode 'd'.\n\n"
+    "dims      a tuple with the dimensions of the array.";
 
-static PyObject *idft2(PyObject *self, PyObject *args, PyObject *kwrds)
+static PyObject *idftn(PyObject *self, PyObject *args, PyObject *kwrds)
 {
   matrix *X;
-  char *kwlist[] = {"X", NULL};
+  PyObject *dims = NULL;
+  char *kwlist[] = {"X", "dims", NULL};
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O", kwlist, &X)) 
+  int *dimarr;
+  int free_dims = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|O:idftn", 
+	  kwlist, &X, &dims)) 
     return NULL;
 
   if (!(Matrix_Check(X) && MAT_ID(X) == COMPLEX))     
-    PY_ERR(PyExc_ValueError, "X must be a dense matrix with type 'z'");
+    PY_ERR_TYPE("X must be a dense matrix with type 'z'");
 
-  int m = X->nrows, n = X->ncols; 
-  if (m == 0 || n == 0) return Py_BuildValue("");
+  if (!dims) {
+    dims = PyTuple_New(2);
+    if (!dims) return PyErr_NoMemory();
 
-  fftw_plan p = fftw_plan_dft_2d(n, m,
-      X->buffer, X->buffer, FFTW_BACKWARD, FFTW_ESTIMATE);
+    PyTuple_SET_ITEM(dims, 0, PyInt_FromLong(MAT_NCOLS(X)));
+    PyTuple_SET_ITEM(dims, 1, PyInt_FromLong(MAT_NROWS(X)));
+    free_dims = 1;
+  }
+
+  if (!PyTuple_Check(dims)) 
+    PY_ERR_TYPE("invalid dimension tuple");
+
+  int len = PySequence_Size(dims);  
+  PyObject *seq = PySequence_Fast(dims, "list is not iterable");
+
+  if (!(dimarr = malloc(len*sizeof(int)))) {
+    if (free_dims) { Py_DECREF(dims); }
+    Py_DECREF(seq);
+    return PyErr_NoMemory();
+  }
+
+  int i, proddim = 1;
+  for (i=0; i<len; i++) {
+    PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+	
+    if (!PyInt_Check(item)) {
+      if (free_dims) { Py_DECREF(dims); }
+      Py_DECREF(seq);
+      free(dimarr); 
+      PY_ERR_TYPE("non-integer in dimension tuple");
+    }
+    
+    dimarr[len-i-1] = PyInt_AS_LONG(item);
+    if (dimarr[len-i-1] < 0) {
+      if (free_dims) { Py_DECREF(dims); }
+      Py_DECREF(seq);
+      free(dimarr); 
+      PY_ERR(PyExc_ValueError, "negative dimension");
+    }        
+    proddim *= dimarr[len-i-1];
+  }
+
+  Py_DECREF(seq);
+
+  if (free_dims) { Py_DECREF(dims); }
+
+  if (proddim != MAT_LGT(X)) {
+    free(dimarr); 
+    PY_ERR_TYPE("length of X does not match dimensions");
+  }
+    
+  if (proddim == 0) {
+    free(dimarr); 
+    return Py_BuildValue("");
+  }
 
-  fftw_execute(p); 
-  
   number a;
-  a.z = 1.0/(m*n);
-  int mn = m*n, ix = 1;
-  zscal_(&mn, &a.z, MAT_BUFZ(X), &ix);
-  
-  fftw_destroy_plan(p);
-  return Py_BuildValue("");  
+  a.z = 1.0/proddim;
+
+  int ix = 1;
+  zscal_(&proddim, &a.z, MAT_BUFZ(X), &ix);
+
+  fftw_plan p = fftw_plan_dft(len, dimarr,
+      X->buffer, X->buffer, FFTW_BACKWARD, FFTW_ESTIMATE);
+
+  Py_BEGIN_ALLOW_THREADS
+  fftw_execute(p);   
+  Py_END_ALLOW_THREADS
+  fftw_destroy_plan(p);  
+  free(dimarr); 
+  return Py_BuildValue("");
 }
 
 static char doc_dct[] = 
@@ -200,66 +337,178 @@ static PyObject *dct(PyObject *self, PyObject *args, PyObject *kwrds)
       X->buffer, &m, 1, m,
       &kind, FFTW_ESTIMATE);
 
+  Py_BEGIN_ALLOW_THREADS
   fftw_execute(p);   
+  Py_END_ALLOW_THREADS
   fftw_destroy_plan(p);  
   return Py_BuildValue("");
 }
 
-static char doc_dct2[] = 
-    "2D-DCT of a matrix.\n"
-    "X := dct2(X, type=2)\n\n" 
+static char doc_dctn[] = 
+    "N-dimensional DCT of a matrix.\n"
+    "X := dctn(X, dims, type=2)\n\n" 
     "PURPOSE\n"
-    "Computes the 2D-DCT of a dense matrix X.\n\n"
+    "Computes the DCT of an N-dimensional array represented by a dense\n"
+    "matrix X. The shape of the matrix X does not matter, but the data\n"
+    "must be arranged in row-major-order.  The total dimension (defined\n"
+    "as mul(dims)) must equal the length of the array X.\n\n"
     "ARGUMENTS\n"
     "X         A dense matrix of typecode 'd'.\n\n"
+    "dims      a tuple with the dimensions of the array.\n\n"
     "type      integer from 1 to 4; chooses either DCT-I, DCT-II, \n"
     "          DCT-III or DCT-IV.";
 
-
-static PyObject *dct2(PyObject *self, PyObject *args, PyObject *kwrds)
+static PyObject *dctn(PyObject *self, PyObject *args, PyObject *kwrds)
 {
   matrix *X;
-  int type = 2;
-  char *kwlist[] = {"X", "type", NULL};
+  PyObject *dims = NULL, *type = NULL;
+  char *kwlist[] = {"X", "dims", "type", NULL};
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|i", kwlist, &X, &type)) 
+  int *dimarr;
+  fftw_r2r_kind *kindarr;
+  int free_dims = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|OO:dctn", kwlist, 
+	  &X, &dims, &type)) 
     return NULL;
 
   if (!(Matrix_Check(X) && MAT_ID(X) == DOUBLE))     
-    PY_ERR(PyExc_ValueError, "X must be a dense matrix with type 'd'");
+    PY_ERR_TYPE("X must be a dense matrix with type 'd'");
+
+  if (!dims) {
+    dims = PyTuple_New(2);
+    if (!dims) return PyErr_NoMemory();
+
+    PyTuple_SET_ITEM(dims, 0, PyInt_FromLong(MAT_NCOLS(X)));
+    PyTuple_SET_ITEM(dims, 1, PyInt_FromLong(MAT_NROWS(X)));
+    free_dims = 1;
+  }
    
-  int m = X->nrows, n = X->ncols;
-  if (m == 0 || n == 0) return Py_BuildValue("");
+  if (!PyTuple_Check(dims)) 
+    PY_ERR_TYPE("invalid dimension tuple");
 
-  fftw_r2r_kind kind;
-  switch(type) {
-  case 1: 
-    kind = FFTW_REDFT00; 
-    if (m <= 1) PY_ERR(PyExc_ValueError, "m must be greater than 1 for DCT-I");
-    break;
-  case 2: kind = FFTW_REDFT10; break;
-  case 3: kind = FFTW_REDFT01; break;
-  case 4: kind = FFTW_REDFT11; break;
-  default: PY_ERR(PyExc_ValueError, "type must be between 1 and 4");
+  if (type && !PyTuple_Check(type)) {
+    if (free_dims) { Py_DECREF(dims); }
+    PY_ERR_TYPE("invalid type tuple");
+  }
+
+  int len = PySequence_Size(dims);
+  if (type && PySequence_Size(type) != len) {
+    if (free_dims) { Py_DECREF(dims); }
+    PY_ERR_TYPE("dimensions and type tuples must have same length");
   }
 
-  fftw_plan p = fftw_plan_r2r_2d(n, m,
-      X->buffer, X->buffer, kind, kind, FFTW_ESTIMATE);
+  PyObject *seq = PySequence_Fast(dims, "list is not iterable");
 
+  if (!(dimarr = malloc(len*sizeof(int)))) {
+    if (free_dims) { Py_DECREF(dims); }
+    Py_DECREF(seq);
+    return PyErr_NoMemory();
+  }
+
+  if (!(kindarr = malloc(len*sizeof(fftw_r2r_kind)))) {
+    if (free_dims) { Py_DECREF(dims); }
+    Py_DECREF(seq);
+    free(dimarr);
+    return PyErr_NoMemory();
+  }
+
+  int i, proddim = 1;
+  for (i=0; i<len; i++) {
+    PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+	
+    if (!PyInt_Check(item)) {
+      if (free_dims) { Py_DECREF(dims); }
+      Py_DECREF(seq);
+      free(dimarr); free(kindarr);
+      PY_ERR_TYPE("non-integer in dimension tuple");
+    }
+    
+    dimarr[len-i-1] = PyInt_AS_LONG(item);
+    if (dimarr[len-i-1] < 0) {
+      if (free_dims) { Py_DECREF(dims); }
+      Py_DECREF(seq);
+      free(dimarr); free(kindarr);
+      PY_ERR(PyExc_ValueError, "negative dimension");
+    }        
+    proddim *= dimarr[len-i-1];
+  }
+
+  if (free_dims) { Py_DECREF(dims); }
+
+  if (proddim != MAT_LGT(X)) {
+    Py_DECREF(seq);
+    free(dimarr); free(kindarr);
+    PY_ERR_TYPE("length of X does not match dimensions");
+  }
+    
+  if (proddim == 0) {
+    Py_DECREF(seq);
+    free(dimarr); free(kindarr);
+    return Py_BuildValue("");
+  }
+
+  Py_DECREF(seq);
+  
+  if (type == NULL) {
+    for (i=0; i<len; i++)
+      kindarr[i] = FFTW_REDFT10;
+  } else {
+    
+    seq = PySequence_Fast(type, "list is not iterable");
+
+    for (i=0; i<len; i++) {
+      PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+	
+      if (!PyInt_Check(item)) {
+	Py_DECREF(seq);
+	free(dimarr); free(kindarr);
+	PY_ERR_TYPE("non-integer in type tuple");
+      }
+      
+      switch(PyInt_AS_LONG(item)) {
+      case 1: 
+	kindarr[len-i-1] = FFTW_REDFT00; 
+	if (dimarr[len-i-1] <= 1) {
+	  Py_DECREF(seq);
+	  free(dimarr); free(kindarr);
+	  PY_ERR(PyExc_ValueError, 
+	      "dimension must be greater than 1 for DCT-I");
+	}
+	break;
+      case 2: kindarr[len-i-1] = FFTW_REDFT10; break;
+      case 3: kindarr[len-i-1] = FFTW_REDFT01; break;
+      case 4: kindarr[len-i-1] = FFTW_REDFT11; break;
+      default: 
+	Py_DECREF(seq);
+	free(dimarr); free(kindarr);
+	PY_ERR(PyExc_ValueError, "type must be between 1 and 4");
+      }      
+    }
+
+    Py_DECREF(seq);
+  }
+
+  fftw_plan p = fftw_plan_r2r(len, dimarr,
+      X->buffer, X->buffer, kindarr, FFTW_ESTIMATE);
+
+  Py_BEGIN_ALLOW_THREADS
   fftw_execute(p);   
+  Py_END_ALLOW_THREADS
   fftw_destroy_plan(p);  
+  free(dimarr); free(kindarr);
   return Py_BuildValue("");
 }
 
 static char doc_idct[] = 
-    "IDCT of a matrix.\n"
+    "Inverse DCT of a matrix.\n"
     "X := idct(X, type=2)\n\n" 
     "PURPOSE\n"
     "Computes the IDCT of a dense matrix X column by column.\n\n"
     "ARGUMENTS\n"
     "X         A dense matrix of typecode 'd'.\n\n"
-    "type      integer from 1 to 4; chooses the inverse transform for\n"
-    "          either DCT-I, DCT-II, DCT-III or DCT-IV.";
+    "type      integer from 1 to 4; chooses either DCT-I, DCT-II, \n"
+    "          DCT-III or DCT-IV.";
 
 static PyObject *idct(PyObject *self, PyObject *args, PyObject *kwrds)
 {
@@ -291,7 +540,9 @@ static PyObject *idct(PyObject *self, PyObject *args, PyObject *kwrds)
       X->buffer, &m, 1, m,
       &kind, FFTW_ESTIMATE);
 
+  Py_BEGIN_ALLOW_THREADS
   fftw_execute(p);   
+  Py_END_ALLOW_THREADS
   
   double a = 1.0/(type == 1 ? MAX(1,2*(m-1)) : 2*m);
   int mn = m*n, ix = 1;
@@ -301,53 +552,164 @@ static PyObject *idct(PyObject *self, PyObject *args, PyObject *kwrds)
   return Py_BuildValue("");
 }
 
-static char doc_idct2[] = 
-    "2D-IDCT of a matrix.\n"
-    "X := idct2(X, type=2)\n\n" 
+static char doc_idctn[] = 
+    "Inverse N-dimensional DCT of a matrix.\n"
+    "X := idctn(X, dims, type=2)\n\n" 
     "PURPOSE\n"
-    "Computes the 2D-IDCT of a dense matrix X.\n\n"
+    "Computes the IDCT of an N-dimensional array represented by a dense\n"
+    "matrix X. The shape of the matrix X does not matter, but the data\n"
+    "must be arranged in row-major-order.  The total dimension (defined\n"
+    "as mul(dims)) must equal the length of the array X.\n\n"
     "ARGUMENTS\n"
     "X         A dense matrix of typecode 'd'.\n\n"
-    "type      integer from 1 to 4; chooses the inverse transform for\n"
-    "          either DCT-I, DCT-II, DCT-III or DCT-IV.";
+    "dims      a tuple with the dimensions of the array.\n\n"
+    "type      integer from 1 to 4; chooses either DCT-I, DCT-II, \n"
+    "          DCT-III or DCT-IV.";
 
-static PyObject *idct2(PyObject *self, PyObject *args, PyObject *kwrds)
+static PyObject *idctn(PyObject *self, PyObject *args, PyObject *kwrds)
 {
   matrix *X;
-  int type = 2;
-  char *kwlist[] = {"X", "type", NULL};
+  PyObject *dims = NULL, *type = NULL;
+  char *kwlist[] = {"X", "dims", "type", NULL};
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|i", kwlist, &X, &type)) 
+  int *dimarr;
+  fftw_r2r_kind *kindarr;
+  int free_dims = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|OO:idctn", kwlist, 
+	  &X, &dims, &type)) 
     return NULL;
 
   if (!(Matrix_Check(X) && MAT_ID(X) == DOUBLE))     
-    PY_ERR(PyExc_ValueError, "X must be a dense matrix with type 'd'");
+    PY_ERR_TYPE("X must be a dense matrix with type 'd'");
+
+  if (!dims) {
+    dims = PyTuple_New(2);
+    if (!dims) return PyErr_NoMemory();
+
+    PyTuple_SET_ITEM(dims, 0, PyInt_FromLong(MAT_NCOLS(X)));
+    PyTuple_SET_ITEM(dims, 1, PyInt_FromLong(MAT_NROWS(X)));
+    free_dims = 1;
+  }
    
-  int m = X->nrows, n = X->ncols;
-  if (m == 0 || n == 0) return Py_BuildValue("");
+  if (!PyTuple_Check(dims)) 
+    PY_ERR_TYPE("invalid dimension tuple");
 
-  fftw_r2r_kind kind;
-  switch(type) {
-  case 1: 
-    kind = FFTW_REDFT00; 
-    if (MIN(m,n) <= 1)
-      PY_ERR(PyExc_ValueError, "m and n must both greater than 1 for DCT2-I");
-    break;
-  case 2: kind = FFTW_REDFT01; break;
-  case 3: kind = FFTW_REDFT10; break;
-  case 4: kind = FFTW_REDFT11; break;
-  default: PY_ERR(PyExc_ValueError, "type must be between 1 and 4");
+  if (type && !PyTuple_Check(type)) {
+    if (free_dims) { Py_DECREF(dims); }
+    PY_ERR_TYPE("invalid type tuple");
   }
 
-  fftw_plan p = fftw_plan_r2r_2d(n, m,
-      X->buffer, X->buffer, kind, kind, FFTW_ESTIMATE);
+  int len = PySequence_Size(dims);
+  if (type && PySequence_Size(type) != len) {
+    if (free_dims) { Py_DECREF(dims); }
+    PY_ERR_TYPE("dimensions and type tuples must have same length");
+  }
 
-  double a = 1.0/(type == 1 ? MAX(1,2*(m-1))*MAX(1,2*(n-1)) : 4*m*n);
-  int mn = m*n, ix = 1;
-  dscal_(&mn, &a, MAT_BUFD(X), &ix);
+  PyObject *seq = PySequence_Fast(dims, "list is not iterable");
+
+  if (!(dimarr = malloc(len*sizeof(int)))) {
+    if (free_dims) { Py_DECREF(dims); }
+    Py_DECREF(seq);
+    return PyErr_NoMemory();
+  }
+
+  if (!(kindarr = malloc(len*sizeof(fftw_r2r_kind)))) {
+    if (free_dims) { Py_DECREF(dims); }
+    Py_DECREF(seq);
+    free(dimarr);
+    return PyErr_NoMemory();
+  }
 
+  int i, proddim = 1;
+  for (i=0; i<len; i++) {
+    PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+	
+    if (!PyInt_Check(item)) {
+      if (free_dims) { Py_DECREF(dims); }
+      Py_DECREF(seq);
+      free(dimarr); free(kindarr);
+      PY_ERR_TYPE("non-integer in dimension tuple");
+    }
+    
+    dimarr[len-i-1] = PyInt_AS_LONG(item);
+    if (dimarr[len-i-1] < 0) {
+      if (free_dims) { Py_DECREF(dims); }
+      Py_DECREF(seq);
+      free(dimarr); free(kindarr);
+      PY_ERR(PyExc_ValueError, "negative dimension");
+    }        
+    proddim *= dimarr[len-i-1];
+  }
+
+  if (free_dims) { Py_DECREF(dims); }
+
+  Py_DECREF(seq);
+
+  if (proddim != MAT_LGT(X)) {
+    free(dimarr); free(kindarr);
+    PY_ERR_TYPE("length of X does not match dimensions");
+  }
+    
+  if (proddim == 0) {
+    free(dimarr); free(kindarr);
+    return Py_BuildValue("");
+  }
+    
+  if (type == NULL) {
+    for (i=0; i<len; i++)
+      kindarr[i] = FFTW_REDFT01;
+  } else {
+    
+    seq = PySequence_Fast(type, "list is not iterable");
+
+    for (i=0; i<len; i++) {
+      PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+      
+      if (!PyInt_Check(item)) {
+	Py_DECREF(seq);
+	free(dimarr); free(kindarr);
+	PY_ERR_TYPE("non-integer in type tuple");
+      }
+      
+      switch(PyInt_AS_LONG(item)) {
+      case 1: 
+	kindarr[len-i-1] = FFTW_REDFT00; 
+	if (dimarr[len-i-1] <= 1) {
+	  Py_DECREF(seq);
+	  free(dimarr); free(kindarr);
+	  PY_ERR(PyExc_ValueError, 
+	      "dimension must be greater than 1 for DCT-I");
+	}
+	break;
+      case 2: kindarr[len-i-1] = FFTW_REDFT01; break;
+      case 3: kindarr[len-i-1] = FFTW_REDFT10; break;
+      case 4: kindarr[len-i-1] = FFTW_REDFT11; break;
+      default: 
+	Py_DECREF(seq);
+	free(dimarr); free(kindarr);
+	PY_ERR(PyExc_ValueError, "type must be between 1 and 4");
+      }
+    }
+
+    Py_DECREF(seq);
+  }
+
+  double a = 1.0;
+  for (i=0; i<len; i++)
+    a /= (kindarr[i] == FFTW_REDFT00 ? MAX(1,2*(dimarr[i]-1)) : 2*dimarr[i]);
+
+  int ix = 1;
+  dscal_(&proddim, &a, MAT_BUFD(X), &ix);
+  
+  fftw_plan p = fftw_plan_r2r(len, dimarr,
+      X->buffer, X->buffer, kindarr, FFTW_ESTIMATE);
+
+  Py_BEGIN_ALLOW_THREADS
   fftw_execute(p);   
+  Py_END_ALLOW_THREADS
   fftw_destroy_plan(p);  
+  free(dimarr); free(kindarr);
   return Py_BuildValue("");
 }
 
@@ -389,50 +751,144 @@ static PyObject *dst(PyObject *self, PyObject *args, PyObject *kwrds)
       X->buffer, &m, 1, m,
       &kind, FFTW_ESTIMATE);
 
+  Py_BEGIN_ALLOW_THREADS
   fftw_execute(p);   
+  Py_END_ALLOW_THREADS
   fftw_destroy_plan(p);  
   return Py_BuildValue("");
 }
 
-static char doc_dst2[] = 
-    "2D-DST of a matrix.\n"
-    "X := dst2(X, type=1)\n\n" 
+static char doc_dstn[] = 
+    "N-dimensional DST of a matrix.\n"
+    "X := dstn(X, dims, type=1)\n\n" 
     "PURPOSE\n"
-    "Computes the 2D-DST of a dense matrix X.\n\n"
+    "Computes the DST of an N-dimensional array represented by a dense\n"
+    "matrix X. The shape of the matrix X does not matter, but the data\n"
+    "must be arranged in row-major-order.  The total dimension (defined\n"
+    "as mul(dims)) must equal the length of the array X.\n\n"
     "ARGUMENTS\n"
     "X         A dense matrix of typecode 'd'.\n\n"
+    "dims      a tuple with the dimensions of the array.\n\n"
     "type      integer from 1 to 4; chooses either DST-I, DST-II, \n"
     "          DST-III or DST-IV.";
 
-static PyObject *dst2(PyObject *self, PyObject *args, PyObject *kwrds)
+static PyObject *dstn(PyObject *self, PyObject *args, PyObject *kwrds)
 {
   matrix *X;
-  int type = 1;
-  char *kwlist[] = {"X", "type", NULL};
+  PyObject *dims = NULL, *type = NULL;
+  char *kwlist[] = {"X", "dims", "type", NULL};
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|i", kwlist, &X, &type)) 
+  int *dimarr;
+  fftw_r2r_kind *kindarr;
+  int free_dims = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|OO:dctn", kwlist, 
+	  &X, &dims, &type)) 
     return NULL;
 
   if (!(Matrix_Check(X) && MAT_ID(X) == DOUBLE))     
-    PY_ERR(PyExc_ValueError, "X must be a dense matrix with type 'd'");
+    PY_ERR_TYPE("X must be a dense matrix with type 'd'");
+
+  if (!dims) {
+    dims = PyTuple_New(2);
+    if (!dims) return PyErr_NoMemory();
+
+    PyTuple_SET_ITEM(dims, 0, PyInt_FromLong(MAT_NCOLS(X)));
+    PyTuple_SET_ITEM(dims, 1, PyInt_FromLong(MAT_NROWS(X)));
+    free_dims = 1;
+  }
    
-  int m = X->nrows, n = X->ncols;
-  if (m == 0 || n == 0) return Py_BuildValue("");
+  if (!PyTuple_Check(dims)) 
+    PY_ERR_TYPE("invalid dimension tuple");
 
-  fftw_r2r_kind kind;
-  switch(type) {
-  case 1: kind = FFTW_RODFT00; break;
-  case 2: kind = FFTW_RODFT10; break;
-  case 3: kind = FFTW_RODFT01; break;
-  case 4: kind = FFTW_RODFT11; break;
-  default: PY_ERR(PyExc_ValueError, "type must be between 1 and 4");
+  if (type && !PyTuple_Check(type))
+    PY_ERR_TYPE("invalid type tuple");
+
+  int len = PySequence_Size(dims);
+  if (type && PySequence_Size(type) != len) {
+    if (free_dims) { Py_DECREF(dims); }
+    PY_ERR_TYPE("dimensions and type tuples must have same length");
   }
 
-  fftw_plan p = fftw_plan_r2r_2d(n, m,
-      X->buffer, X->buffer, kind, kind, FFTW_ESTIMATE);
+  PyObject *seq = PySequence_Fast(dims, "list is not iterable");
 
+  if (!(dimarr = malloc(len*sizeof(int)))) {
+    if (free_dims) { Py_DECREF(dims); }
+    return PyErr_NoMemory();
+  }
+
+  if (!(kindarr = malloc(len*sizeof(fftw_r2r_kind)))) {
+    if (free_dims) { Py_DECREF(dims); }
+    free(dimarr);
+    return PyErr_NoMemory();
+  }
+
+  int i, proddim = 1;
+  for (i=0; i<len; i++) {
+    PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+	
+    if (!PyInt_Check(item)) {
+      if (free_dims) { Py_DECREF(dims); }
+      free(dimarr); free(kindarr);
+      PY_ERR_TYPE("non-integer in dimension tuple");
+    }
+    
+    dimarr[len-i-1] = PyInt_AS_LONG(item);
+    if (dimarr[len-i-1] < 0) {
+      if (free_dims) { Py_DECREF(dims); }
+      free(dimarr); free(kindarr);
+      PY_ERR(PyExc_ValueError, "negative dimension");
+    }        
+    proddim *= dimarr[len-i-1];
+  }
+
+  if (free_dims) { Py_DECREF(dims); }
+
+  if (proddim != MAT_LGT(X)) {
+    free(dimarr); free(kindarr);
+    PY_ERR_TYPE("length of X does not match dimensions");
+  }
+    
+  if (proddim == 0) {
+    free(dimarr); free(kindarr);
+    return Py_BuildValue("");
+  }
+
+  if (type == NULL) {
+    for (i=0; i<len; i++)
+      kindarr[i] = FFTW_RODFT00;
+  } else {
+    
+    seq = PySequence_Fast(type, "list is not iterable");
+
+    for (i=0; i<len; i++) {
+      PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+	
+      if (!PyInt_Check(item)) {
+	free(dimarr); free(kindarr);
+	PY_ERR_TYPE("non-integer in type tuple");
+      }
+      
+      switch(PyInt_AS_LONG(item)) {
+      case 1: kindarr[len-i-1] = FFTW_RODFT00; break;
+      case 2: kindarr[len-i-1] = FFTW_RODFT10; break;
+      case 3: kindarr[len-i-1] = FFTW_RODFT01; break;
+      case 4: kindarr[len-i-1] = FFTW_RODFT11; break;
+      default: 
+	free(dimarr); free(kindarr);
+	PY_ERR(PyExc_ValueError, "type must be between 1 and 4");
+      }
+    }
+  }
+
+  fftw_plan p = fftw_plan_r2r(len, dimarr,
+      X->buffer, X->buffer, kindarr, FFTW_ESTIMATE);
+
+  Py_BEGIN_ALLOW_THREADS
   fftw_execute(p);   
+  Py_END_ALLOW_THREADS
   fftw_destroy_plan(p);  
+  free(dimarr); free(kindarr);
   return Py_BuildValue("");
 }
 
@@ -474,7 +930,9 @@ static PyObject *idst(PyObject *self, PyObject *args, PyObject *kwrds)
       X->buffer, &m, 1, m,
       &kind, FFTW_ESTIMATE);
 
+  Py_BEGIN_ALLOW_THREADS
   fftw_execute(p);   
+  Py_END_ALLOW_THREADS
   
   double a = 1.0/(type == 1 ? MAX(1,2*(m+1)) : 2*m);
   int mn = m*n, ix = 1;
@@ -484,65 +942,163 @@ static PyObject *idst(PyObject *self, PyObject *args, PyObject *kwrds)
   return Py_BuildValue("");
 }
 
-static char doc_idst2[] = 
-    "2D-IDST of a matrix.\n"
-    "X := idst2(X, type=1)\n\n" 
+static char doc_idstn[] = 
+    "Inverse N-dimensional DST of a matrix.\n"
+    "X := idstn(X, dims, type=1)\n\n" 
     "PURPOSE\n"
-    "Computes the 2D-IDST of a dense matrix X.\n\n"
+    "Computes the IDST of an N-dimensional array represented by a dense\n"
+    "matrix X. The shape of the matrix X does not matter, but the data\n"
+    "must be arranged in row-major-order.  The total dimension (defined\n"
+    "as mul(dims)) must equal the length of the array X.\n\n"
     "ARGUMENTS\n"
     "X         A dense matrix of typecode 'd'.\n\n"
-    "type      integer from 1 to 4; chooses the inverse transform for\n"
-    "          either DST-I, DST-II, DST-III or DST-IV.";
+    "dims      a tuple with the dimensions of the array.\n\n"
+    "type      integer from 1 to 4; chooses either DST-I, DST-II, \n"
+    "          DST-III or DST-IV.";
 
-static PyObject *idst2(PyObject *self, PyObject *args, PyObject *kwrds)
+static PyObject *idstn(PyObject *self, PyObject *args, PyObject *kwrds)
 {
   matrix *X;
-  int type = 1;
-  char *kwlist[] = {"X", "type", NULL};
+  PyObject *dims = NULL, *type = NULL;
+  char *kwlist[] = {"X", "dims", "type", NULL};
 
-  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|i", kwlist, &X, &type)) 
+  int *dimarr;
+  fftw_r2r_kind *kindarr;
+  int free_dims = 0;
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|OO:dctn", kwlist, 
+	  &X, &dims, &type)) 
     return NULL;
 
   if (!(Matrix_Check(X) && MAT_ID(X) == DOUBLE))     
-    PY_ERR(PyExc_ValueError, "X must be a dense matrix with type 'd'");
+    PY_ERR_TYPE("X must be a dense matrix with type 'd'");
+
+  if (!dims) {
+    dims = PyTuple_New(2);
+    if (!dims) return PyErr_NoMemory();
+
+    PyTuple_SET_ITEM(dims, 0, PyInt_FromLong(MAT_NCOLS(X)));
+    PyTuple_SET_ITEM(dims, 1, PyInt_FromLong(MAT_NROWS(X)));
+    free_dims = 1;
+  }
    
-  int m = X->nrows, n = X->ncols;
-  if (m == 0 || n == 0) return Py_BuildValue("");
+  if (!PyTuple_Check(dims)) 
+    PY_ERR_TYPE("invalid dimension tuple");
 
-  fftw_r2r_kind kind;
-  switch(type) {
-  case 1: kind = FFTW_RODFT00; break;
-  case 2: kind = FFTW_RODFT01; break;
-  case 3: kind = FFTW_RODFT10; break;
-  case 4: kind = FFTW_RODFT11; break;
-  default: PY_ERR(PyExc_ValueError, "type must be between 1 and 4");
+  if (type && !PyTuple_Check(type)) {
+    if (free_dims) { Py_DECREF(dims); }
+    PY_ERR_TYPE("invalid type tuple");
   }
-  fftw_plan p = fftw_plan_r2r_2d(n, m,
-      X->buffer, X->buffer, kind, kind, FFTW_ESTIMATE);
 
-  fftw_execute(p);   
+  int len = PySequence_Size(dims);
+  if (type && PySequence_Size(type) != len) {
+    if (free_dims) { Py_DECREF(dims); }
+    PY_ERR_TYPE("dimensions and type tuples must have same length");
+  }
+
+  PyObject *seq = PySequence_Fast(dims, "list is not iterable");
+
+  if (!(dimarr = malloc(len*sizeof(int)))) {
+    if (free_dims) { Py_DECREF(dims); }
+    return PyErr_NoMemory();
+  }
+
+  if (!(kindarr = malloc(len*sizeof(fftw_r2r_kind)))) {
+    if (free_dims) { Py_DECREF(dims); }
+    free(dimarr);
+    return PyErr_NoMemory();
+  }
+
+  int i, proddim = 1;
+  for (i=0; i<len; i++) {
+    PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+	
+    if (!PyInt_Check(item)) {
+      if (free_dims) { Py_DECREF(dims); }
+      free(dimarr); free(kindarr);
+      PY_ERR_TYPE("non-integer in dimension tuple");
+    }
+    
+    dimarr[len-i-1] = PyInt_AS_LONG(item);
+    if (dimarr[len-i-1] < 0) {
+      if (free_dims) { Py_DECREF(dims); }
+      free(dimarr); free(kindarr);
+      PY_ERR(PyExc_ValueError, "negative dimension");
+    }        
+    proddim *= dimarr[len-i-1];
+  }
+
+  if (free_dims) { Py_DECREF(dims); }
+
+  if (proddim != MAT_LGT(X)) {
+    free(dimarr); free(kindarr);
+    PY_ERR_TYPE("length of X does not match dimensions");
+  }
+    
+  if (proddim == 0) {
+    free(dimarr); free(kindarr);
+    return Py_BuildValue("");
+  }
+    
+  if (type == NULL) {
+    for (i=0; i<len; i++)
+      kindarr[i] = FFTW_RODFT00;
+  } else {
+    
+    seq = PySequence_Fast(type, "list is not iterable");
+
+    for (i=0; i<len; i++) {
+      PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+	
+      if (!PyInt_Check(item)) {
+	free(dimarr); free(kindarr);
+	PY_ERR_TYPE("non-integer in type tuple");
+      }
+    
+      switch(PyInt_AS_LONG(item)) {
+      case 1: kindarr[len-i-1] = FFTW_RODFT00; break;
+      case 2: kindarr[len-i-1] = FFTW_RODFT10; break;
+      case 3: kindarr[len-i-1] = FFTW_RODFT01; break;
+      case 4: kindarr[len-i-1] = FFTW_RODFT11; break;
+      default: 
+	free(dimarr); free(kindarr);
+	PY_ERR(PyExc_ValueError, "type must be between 1 and 4");
+      }
+    }
+  }
+
+  double a = 1.0;
+  for (i=0; i<len; i++)
+    a /= (kindarr[i] == FFTW_RODFT00 ? MAX(1,2*(dimarr[i]+1)) : 2*dimarr[i]);
+
+  int ix = 1;
+  dscal_(&proddim, &a, MAT_BUFD(X), &ix);
   
-  double a = 1.0/(type == 1 ? MAX(1,2*(m+1))*MAX(1,2*(n+1)) : 4*m*n);
-  int mn = m*n, ix = 1;
-  dscal_(&mn, &a, MAT_BUFD(X), &ix);
+  fftw_plan p = fftw_plan_r2r(len, dimarr,
+      X->buffer, X->buffer, kindarr, FFTW_ESTIMATE);
 
+  Py_BEGIN_ALLOW_THREADS
+  fftw_execute(p);   
+  Py_END_ALLOW_THREADS
   fftw_destroy_plan(p);  
+  free(dimarr); free(kindarr);
   return Py_BuildValue("");
 }
 
+
 static PyMethodDef fftw_functions[] = {
     {"dft", (PyCFunction) dft, METH_VARARGS|METH_KEYWORDS, doc_dft},
-    {"dft2", (PyCFunction) dft2, METH_VARARGS|METH_KEYWORDS, doc_dft2},
+    {"dftn", (PyCFunction) dftn, METH_VARARGS|METH_KEYWORDS, doc_dftn},
     {"idft", (PyCFunction) idft, METH_VARARGS|METH_KEYWORDS, doc_idft},
-    {"idft2", (PyCFunction) idft2, METH_VARARGS|METH_KEYWORDS, doc_idft2},
+    {"idftn", (PyCFunction) idftn, METH_VARARGS|METH_KEYWORDS, doc_idftn},
     {"dct", (PyCFunction) dct, METH_VARARGS|METH_KEYWORDS, doc_dct},
-    {"dct2", (PyCFunction) dct2, METH_VARARGS|METH_KEYWORDS, doc_dct2},
+    {"dctn", (PyCFunction) dctn, METH_VARARGS|METH_KEYWORDS, doc_dctn},
     {"idct", (PyCFunction) idct, METH_VARARGS|METH_KEYWORDS, doc_idct},
-    {"idct2", (PyCFunction) idct2, METH_VARARGS|METH_KEYWORDS, doc_idct2},
+    {"idctn", (PyCFunction) idctn, METH_VARARGS|METH_KEYWORDS, doc_idctn},
     {"dst", (PyCFunction) dst, METH_VARARGS|METH_KEYWORDS, doc_dst},
-    {"dst2", (PyCFunction) dst2, METH_VARARGS|METH_KEYWORDS, doc_dst2},
+    {"dstn", (PyCFunction) dstn, METH_VARARGS|METH_KEYWORDS, doc_dstn},
     {"idst", (PyCFunction) idst, METH_VARARGS|METH_KEYWORDS, doc_idst},
-    {"idst2", (PyCFunction) idst2, METH_VARARGS|METH_KEYWORDS, doc_idst2},
+    {"idstn", (PyCFunction) idstn, METH_VARARGS|METH_KEYWORDS, doc_idstn},
     {NULL}  /* Sentinel */
 };
 
diff --git a/src/C/glpk.c b/src/C/glpk.c
index cd4bc9a..cae0623 100644
--- a/src/C/glpk.c
+++ b/src/C/glpk.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2007 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -590,6 +590,7 @@ static PyObject *integer(PyObject *self, PyObject *args,
       }
 
       Py_DECREF(iter);
+
     }
     
 
diff --git a/src/C/gsl.c b/src/C/gsl.c
index 19e2c56..d15a0e2 100644
--- a/src/C/gsl.c
+++ b/src/C/gsl.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/C/lapack.c b/src/C/lapack.c
index e5bbe70..bac7f24 100644
--- a/src/C/lapack.c
+++ b/src/C/lapack.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -28,8 +28,8 @@
 PyDoc_STRVAR(lapack__doc__, "Interface to the LAPACK library.\n\n"
     "Double-precision real and complex LAPACK routines for solving\n"
     "sets of linear equations, linear least-squares and least-norm\n"
-    "problems, symmetric and Hermitian eigenvalue problems, and \n"
-    "singular value decomposition.\n\n"
+    "problems, symmetric and Hermitian eigenvalue problems, singular \n"
+    "value decomposition, and Schur factorization.\n\n"
     "For more details, see the LAPACK Users' Guide at \n"
     "www.netlib.org/lapack/lug/lapack_lug.html.\n\n"
     "Double and complex matrices and vectors are stored in CVXOPT \n"
@@ -234,6 +234,23 @@ extern void zgesdd_(char *jobz, int *m, int *n, complex *A, int *ldA,
     double *S, complex *U, int *ldU, complex *Vt, int *ldVt, complex *work,
     int *lwork, double *rwork, int *iwork, int *info);
 
+extern void dgees_(char *jobvs, char *sort, void *select, int *n, 
+    double *A, int *ldA, int *sdim, double *wr, double *wi, double *vs, 
+    int *ldvs, double *work, int *lwork, int *bwork, int *info);
+extern void zgees_(char *jobvs, char *sort, void *select, int *n, 
+    complex *A, int *ldA, int *sdim, complex *w, complex *vs, int *ldvs,
+    complex *work, int *lwork, complex *rwork, int *bwork, int *info);
+extern void dgges_(char *jobvsl, char *jobvsr, char *sort, void *delctg, 
+    int *n, double *A, int *ldA, double *B, int *ldB, int *sdim, 
+    double *alphar, double *alphai, double *beta, double *vsl, int *ldvsl,
+    double *vsr, int *ldvsr, double *work, int *lwork, int *bwork, 
+    int *info);
+extern void zgges_(char *jobvsl, char *jobvsr, char *sort, void *delctg, 
+    int *n, complex *A, int *ldA, complex *B, int *ldB, int *sdim, 
+    complex *alpha, complex *beta, complex *vsl, int *ldvsl, complex *vsr,
+    int *ldvsr, complex *work, int *lwork, double *rwork, int *bwork, 
+    int *info);
+
 
 static char doc_getrf[] = 
     "LU factorization of a general real or complex m by n matrix.\n\n"
@@ -285,11 +302,15 @@ static PyObject* getrf(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)) {
         case DOUBLE: 
+            Py_BEGIN_ALLOW_THREADS	    
             dgetrf_(&m, &n, MAT_BUFD(A)+oA, &ldA, ipiv_ptr, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zgetrf_(&m, &n, MAT_BUFZ(A)+oA, &ldA, ipiv_ptr, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -386,13 +407,17 @@ static PyObject* getrs(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             if (trans == 'C') trans = 'T';
+            Py_BEGIN_ALLOW_THREADS	    
             dgetrs_(&trans, &n, &nrhs, MAT_BUFD(A)+oA, &ldA, ipiv_ptr, 
                 MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zgetrs_(&trans, &n, &nrhs, MAT_BUFZ(A)+oA, &ldA, ipiv_ptr, 
                 MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
     
 	default:
@@ -464,7 +489,9 @@ static PyObject* getri(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dgetri_(&n, NULL, &ldA, NULL, &wl.d, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = (void *) calloc(lwork, sizeof(double)))) {
 #if (SIZEOF_INT < SIZEOF_LONG)
@@ -472,14 +499,18 @@ static PyObject* getri(PyObject *self, PyObject *args, PyObject *kwrds)
 #endif
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             dgetri_(&n, MAT_BUFD(A)+oA, &ldA, ipiv_ptr, (double *) work,
                 &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
             break;
 
         case COMPLEX:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             zgetri_(&n, NULL, &ldA, NULL, &wl.z, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) creal(wl.z);
             if (!(work = (void *) calloc(lwork, sizeof(complex)))){
 #if (SIZEOF_INT < SIZEOF_LONG)
@@ -487,8 +518,10 @@ static PyObject* getri(PyObject *self, PyObject *args, PyObject *kwrds)
 #endif
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             zgetri_(&n, MAT_BUFZ(A)+oA, &ldA, ipiv_ptr, 
                 (complex *) work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
             break;
 
@@ -584,8 +617,10 @@ static PyObject* gesv(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             if (ipiv) 
+                Py_BEGIN_ALLOW_THREADS	    
                 dgesv_(&n, &nrhs, MAT_BUFD(A)+oA, &ldA, ipivc,
                     MAT_BUFD(B)+oB, &ldB, &info);
+                Py_END_ALLOW_THREADS	    
             else {
                 if (!(Ac = (void *) calloc(n*n, sizeof(double)))){
                     free(ipivc);  
@@ -593,16 +628,20 @@ static PyObject* gesv(PyObject *self, PyObject *args, PyObject *kwrds)
                 }
                 for (k=0; k<n; k++) memcpy((double *) Ac + k*n, 
                     MAT_BUFD(A)+oA+k*ldA, n*sizeof(double));
+                Py_BEGIN_ALLOW_THREADS	    
                 dgesv_(&n, &nrhs, (double *) Ac, &n, ipivc, 
                     MAT_BUFD(B)+oB, &ldB, &info);
+                Py_END_ALLOW_THREADS	    
                 free(Ac);
             }
             break;
 
         case COMPLEX:
             if (ipiv) 
+                Py_BEGIN_ALLOW_THREADS	    
                 zgesv_(&n, &nrhs, MAT_BUFZ(A)+oA, &ldA, ipivc,
                     MAT_BUFZ(B)+oB, &ldB, &info);
+                Py_END_ALLOW_THREADS	    
             else {
                 if (!(Ac = (void *) calloc(n*n, sizeof(complex)))){
                     free(ipivc);  
@@ -610,8 +649,10 @@ static PyObject* gesv(PyObject *self, PyObject *args, PyObject *kwrds)
                 }
                 for (k=0; k<n; k++) memcpy((complex *) Ac + k*n, 
                     MAT_BUFZ(A)+oA+k*ldA, n*sizeof(complex));
+                Py_BEGIN_ALLOW_THREADS	    
                 zgesv_(&n, &nrhs, (complex *) Ac, &n, ipivc, 
                     MAT_BUFZ(B)+oB, &ldB, &info);
+                Py_END_ALLOW_THREADS	    
                 free(Ac);
             }
             break;
@@ -695,13 +736,17 @@ static PyObject* gbtrf(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)) {
         case DOUBLE: 
+            Py_BEGIN_ALLOW_THREADS	    
             dgbtrf_(&m, &n, &kl, &ku, MAT_BUFD(A)+oA, &ldA, ipiv_ptr, 
                 &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zgbtrf_(&m, &n, &kl, &ku, MAT_BUFZ(A)+oA, &ldA, ipiv_ptr, 
                 &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -799,13 +844,17 @@ static PyObject* gbtrs(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             if (trans == 'C') trans = 'T';
+            Py_BEGIN_ALLOW_THREADS	    
             dgbtrs_(&trans, &n, &kl, &ku, &nrhs, MAT_BUFD(A)+oA, &ldA, 
                 ipiv_ptr, MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zgbtrs_(&trans, &n, &kl, &ku, &nrhs, MAT_BUFZ(A)+oA, &ldA, 
                 ipiv_ptr, MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
     
 	default:
@@ -909,8 +958,10 @@ static PyObject* gbsv(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)) {
         case DOUBLE:
             if (ipiv) 
+                Py_BEGIN_ALLOW_THREADS	    
                 dgbsv_(&n, &kl, &ku, &nrhs, MAT_BUFD(A)+oA, &ldA, ipivc,
                     MAT_BUFD(B)+oB, &ldB, &info);
+                Py_END_ALLOW_THREADS	    
             else {
                 if (!(Ac = (void *) calloc((2*kl+ku+1)*n, 
                     sizeof(double)))){
@@ -922,16 +973,20 @@ static PyObject* gbsv(PyObject *self, PyObject *args, PyObject *kwrds)
                         MAT_BUFD(A) + oA + k*ldA, 
                         (kl+ku+1)*sizeof(double)); 
                 ldA = 2*kl+ku+1;
+                Py_BEGIN_ALLOW_THREADS	    
                 dgbsv_(&n, &kl, &ku, &nrhs, (double *) Ac, &ldA, ipivc,
                     MAT_BUFD(B)+oB, &ldB, &info);
+                Py_END_ALLOW_THREADS	    
                 free(Ac);
             }
             break;
     
         case COMPLEX:
             if (ipiv) 
+                Py_BEGIN_ALLOW_THREADS	    
                 zgbsv_(&n, &kl, &ku, &nrhs, MAT_BUFZ(A)+oA, &ldA, ipivc,
                     MAT_BUFZ(B)+oB, &ldB, &info);
+                Py_END_ALLOW_THREADS	    
             else {
                 if (!(Ac = (void *) calloc((2*kl+ku+1)*n, 
                     sizeof(complex)))){
@@ -943,8 +998,10 @@ static PyObject* gbsv(PyObject *self, PyObject *args, PyObject *kwrds)
                         MAT_BUFZ(A) + oA + k*ldA, 
                         (kl+ku+1)*sizeof(complex)); 
                 ldA = 2*kl+ku+1;
+                Py_BEGIN_ALLOW_THREADS	    
                 zgbsv_(&n, &kl, &ku, &nrhs, (complex *) Ac, &ldA, ipivc,
                     MAT_BUFZ(B)+oB, &ldB, &info);
+                Py_END_ALLOW_THREADS	    
                 free(Ac);
             }
             break;
@@ -1035,13 +1092,17 @@ static PyObject* gttrf(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(dl)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dgttrf_(&n, MAT_BUFD(dl)+odl, MAT_BUFD(d)+od, MAT_BUFD(du)+odu,
                 MAT_BUFD(du2), ipiv_ptr, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zgttrf_(&n, MAT_BUFZ(dl)+odl, MAT_BUFZ(d)+od, MAT_BUFZ(du)+odu,
                 MAT_BUFZ(du2), ipiv_ptr, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1144,15 +1205,19 @@ static PyObject* gttrs(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(dl)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dgttrs_(&trans, &n, &nrhs, MAT_BUFD(dl)+odl, MAT_BUFD(d)+od,
                 MAT_BUFD(du)+odu, MAT_BUFD(du2), ipiv_ptr, 
                 MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zgttrs_(&trans, &n, &nrhs, MAT_BUFZ(dl)+odl, MAT_BUFZ(d)+od,
                 MAT_BUFZ(du)+odu, MAT_BUFZ(du2), ipiv_ptr, 
                 MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1234,13 +1299,17 @@ static PyObject* gtsv(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(dl)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dgtsv_(&n, &nrhs, MAT_BUFD(dl)+odl, MAT_BUFD(d)+od,
                 MAT_BUFD(du)+odu, MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zgtsv_(&n, &nrhs, MAT_BUFZ(dl)+odl, MAT_BUFZ(d)+od,
                 MAT_BUFZ(du)+odu, MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1300,11 +1369,15 @@ static PyObject* potrf(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dpotrf_(&uplo, &n, MAT_BUFD(A)+oA, &ldA, &info);
+            Py_END_ALLOW_THREADS	    
 	    break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zpotrf_(&uplo, &n, MAT_BUFZ(A)+oA, &ldA, &info);
+            Py_END_ALLOW_THREADS	    
 	    break;
 
 	default:
@@ -1372,13 +1445,17 @@ static PyObject* potrs(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dpotrs_(&uplo, &n, &nrhs, MAT_BUFD(A)+oA, &ldA, MAT_BUFD(B)+oB,
                 &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zpotrs_(&uplo, &n, &nrhs, MAT_BUFZ(A)+oA, &ldA, MAT_BUFZ(B)+oB,
                 &ldB, &info);
+            Py_END_ALLOW_THREADS	    
 	    break;
 
         default:
@@ -1428,11 +1505,15 @@ static PyObject* potri(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dpotri_(&uplo, &n, MAT_BUFD(A)+oA, &ldA, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zpotri_(&uplo, &n, MAT_BUFZ(A)+oA, &ldA, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1500,13 +1581,17 @@ static PyObject* posv(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dposv_(&uplo, &n, &nrhs, MAT_BUFD(A)+oA, &ldA, MAT_BUFD(B)+oB,
                 &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zposv_(&uplo, &n, &nrhs, MAT_BUFZ(A)+oA, &ldA, MAT_BUFZ(B)+oB,
                 &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1564,11 +1649,15 @@ static PyObject* pbtrf(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dpbtrf_(&uplo, &n, &kd, MAT_BUFD(A)+oA, &ldA, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zpbtrf_(&uplo, &n, &kd, MAT_BUFZ(A)+oA, &ldA, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1641,13 +1730,17 @@ static PyObject* pbtrs(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dpbtrs_(&uplo, &n, &kd, &nrhs, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zpbtrs_(&uplo, &n, &kd, &nrhs, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1721,13 +1814,17 @@ static PyObject* pbsv(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dpbsv_(&uplo, &n, &kd, &nrhs, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zpbsv_(&uplo, &n, &kd, &nrhs, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1778,11 +1875,15 @@ static PyObject* pttrf(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(e)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dpttrf_(&n, MAT_BUFD(d)+od, MAT_BUFD(e)+oe, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zpttrf_(&n, MAT_BUFD(d)+od, MAT_BUFZ(e)+oe, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1856,13 +1957,17 @@ static PyObject* pttrs(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(e)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dpttrs_(&n, &nrhs, MAT_BUFD(d)+od, MAT_BUFD(e)+oe, 
                 MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zpttrs_(&uplo, &n, &nrhs, MAT_BUFD(d)+od, MAT_BUFZ(e)+oe, 
                 MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -1930,13 +2035,17 @@ static PyObject* ptsv(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(e)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dptsv_(&n, &nrhs, MAT_BUFD(d)+od, MAT_BUFD(e)+oe, 
                 MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zptsv_(&n, &nrhs, MAT_BUFD(d)+od, MAT_BUFZ(e)+oe, 
                 MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -2004,7 +2113,9 @@ static PyObject* sytrf(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dsytrf_(&uplo, &n, NULL, &ldA, NULL, &wl.d, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = (void *) calloc(lwork, sizeof(double)))){
 #if (SIZEOF_INT < SIZEOF_LONG)			
@@ -2012,14 +2123,18 @@ static PyObject* sytrf(PyObject *self, PyObject *args, PyObject *kwrds)
 #endif						
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             dsytrf_(&uplo, &n, MAT_BUFD(A)+oA, &ldA, ipiv_ptr, 
                 (double *) work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   
             break;
 
         case COMPLEX:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             zsytrf_(&uplo, &n, NULL, &ldA, NULL, &wl.z, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) creal(wl.z);
             if (!(work = (void *) calloc(lwork, sizeof(complex)))){
 #if (SIZEOF_INT < SIZEOF_LONG)			
@@ -2027,8 +2142,10 @@ static PyObject* sytrf(PyObject *self, PyObject *args, PyObject *kwrds)
 #endif						
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             zsytrf_(&uplo, &n, MAT_BUFZ(A)+oA, &ldA, ipiv_ptr, 
                 (complex *) work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   
             break;
 
@@ -2105,7 +2222,9 @@ static PyObject* hetrf(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dsytrf_(&uplo, &n, NULL, &ldA, NULL, &wl.d, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = (void *) calloc(lwork, sizeof(double)))){
 #if (SIZEOF_INT < SIZEOF_LONG)			
@@ -2113,14 +2232,18 @@ static PyObject* hetrf(PyObject *self, PyObject *args, PyObject *kwrds)
 #endif						
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             dsytrf_(&uplo, &n, MAT_BUFD(A)+oA, &ldA, ipiv_ptr, 
                 (double *) work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   
             break;
 
         case COMPLEX:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             zhetrf_(&uplo, &n, NULL, &ldA, NULL, &wl.z, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) creal(wl.z);
             if (!(work = (void *) calloc(lwork, sizeof(complex)))){
 #if (SIZEOF_INT < SIZEOF_LONG)			
@@ -2128,8 +2251,10 @@ static PyObject* hetrf(PyObject *self, PyObject *args, PyObject *kwrds)
 #endif						
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             zhetrf_(&uplo, &n, MAT_BUFZ(A)+oA, &ldA, ipiv_ptr, 
                 (complex *) work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   
             break;
 
@@ -2222,13 +2347,17 @@ static PyObject* sytrs(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dsytrs_(&uplo, &n, &nrhs, MAT_BUFD(A)+oA, &ldA, ipiv_ptr, 
                 MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
 	case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zsytrs_(&uplo, &n, &nrhs, MAT_BUFZ(A)+oA, &ldA, ipiv_ptr, 
                 MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
 	default:
@@ -2320,13 +2449,17 @@ static PyObject* hetrs(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dsytrs_(&uplo, &n, &nrhs, MAT_BUFD(A)+oA, &ldA, 
                 ipiv_ptr, MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
 	case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             zhetrs_(&uplo, &n, &nrhs, MAT_BUFZ(A)+oA, &ldA, 
                 ipiv_ptr, MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
 	default:
@@ -2408,8 +2541,10 @@ static PyObject* sytri(PyObject *self, PyObject *args, PyObject *kwrds)
 #endif
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             dsytri_(&uplo, &n, MAT_BUFD(A)+oA, &ldA, ipiv_ptr, 
                 (double *) work, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
             break;
 
@@ -2420,8 +2555,10 @@ static PyObject* sytri(PyObject *self, PyObject *args, PyObject *kwrds)
 #endif
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             zsytri_(&uplo, &n, MAT_BUFZ(A)+oA, &ldA, ipiv_ptr, 
                 (complex *) work, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
             break;
 
@@ -2504,8 +2641,10 @@ static PyObject* hetri(PyObject *self, PyObject *args, PyObject *kwrds)
 #endif
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             dsytri_(&uplo, &n, MAT_BUFD(A)+oA, &ldA, ipiv_ptr, 
                 (double *) work, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
             break;
 
@@ -2516,8 +2655,10 @@ static PyObject* hetri(PyObject *self, PyObject *args, PyObject *kwrds)
 #endif
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             zhetri_(&uplo, &n, MAT_BUFZ(A)+oA, &ldA, ipiv_ptr, 
                 (complex *) work, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
             break;
 
@@ -2606,7 +2747,9 @@ static PyObject* sysv(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dsytrf_(&uplo, &n, NULL, &ldA, NULL, &wl.d, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = (void *) calloc(lwork, sizeof(double))))
                 return PyErr_NoMemory();
@@ -2620,9 +2763,11 @@ static PyObject* sysv(PyObject *self, PyObject *args, PyObject *kwrds)
 #else 
                 ipivc = MAT_BUFI(ipiv);		
 #endif						
+                Py_BEGIN_ALLOW_THREADS	    
                 dsysv_(&uplo, &n, &nrhs, MAT_BUFD(A)+oA, &ldA, ipivc, 
                     MAT_BUFD(B)+oB, &ldB, (double *) work, &lwork, 
                     &info);
+                Py_END_ALLOW_THREADS	    
 #if (SIZEOF_INT < SIZEOF_LONG) 
 		for (k=0; k<n; k++) MAT_BUFI(ipiv)[k] = ipivc[k];
                 free(ipivc); 
@@ -2638,8 +2783,10 @@ static PyObject* sysv(PyObject *self, PyObject *args, PyObject *kwrds)
                 for (k=0; k<n; k++) 
                     memcpy((double *) Ac + k*n, MAT_BUFD(A) + oA + k*ldA, 
                         n*sizeof(double));
+                Py_BEGIN_ALLOW_THREADS	    
                 dsysv_(&uplo, &n, &nrhs, (double *) Ac, &n, ipivc, 
                     MAT_BUFD(B)+oB, &ldB, work, &lwork, &info);
+                Py_END_ALLOW_THREADS	    
                 free(ipivc); free(Ac);
             }
             free(work);   
@@ -2647,7 +2794,9 @@ static PyObject* sysv(PyObject *self, PyObject *args, PyObject *kwrds)
 
         case COMPLEX:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             zsytrf_(&uplo, &n, NULL, &ldA, NULL, &wl.z, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) creal(wl.z);
             if (!(work = (void *) calloc(lwork, sizeof(complex))))
                 return PyErr_NoMemory();
@@ -2661,8 +2810,10 @@ static PyObject* sysv(PyObject *self, PyObject *args, PyObject *kwrds)
 #else 
                 ipivc = MAT_BUFI(ipiv);		
 #endif
+                Py_BEGIN_ALLOW_THREADS	    
                 zsysv_(&uplo, &n, &nrhs, MAT_BUFZ(A)+oA, &ldA, ipivc, 
                     MAT_BUFZ(B)+oB, &ldB, (complex *) work, &lwork, &info);
+                Py_END_ALLOW_THREADS	    
 #if (SIZEOF_INT < SIZEOF_LONG) 
                 for (k=0; k<n; k++) MAT_BUFI(ipiv)[k] = ipivc[k];
                 free(ipivc); 
@@ -2678,8 +2829,10 @@ static PyObject* sysv(PyObject *self, PyObject *args, PyObject *kwrds)
                 for (k=0; k<n; k++) 
                     memcpy((complex *) Ac + k*n, MAT_BUFZ(A) + oA + k*ldA, 
                         n*sizeof(complex));
+                Py_BEGIN_ALLOW_THREADS	    
                 zsysv_(&uplo, &n, &nrhs, (complex *) Ac, &n, ipivc, 
                     MAT_BUFZ(B)+oB, &ldB, work, &lwork, &info);
+                Py_END_ALLOW_THREADS	    
                 free(ipivc);  free(Ac);    
             }
             free(work);   
@@ -2765,7 +2918,9 @@ static PyObject* hesv(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dsytrf_(&uplo, &n, NULL, &ldA, NULL, &wl.d, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = (void *) calloc(lwork, sizeof(double))))
                 return PyErr_NoMemory();
@@ -2779,9 +2934,11 @@ static PyObject* hesv(PyObject *self, PyObject *args, PyObject *kwrds)
 #else 
                 ipivc = MAT_BUFI(ipiv);		
 #endif
+                Py_BEGIN_ALLOW_THREADS	    
                 dsysv_(&uplo, &n, &nrhs, MAT_BUFD(A)+oA, &ldA, ipivc, 
                     MAT_BUFD(B)+oB, &ldB, (double *) work, &lwork, 
                     &info);
+                Py_END_ALLOW_THREADS	    
 #if (SIZEOF_INT < SIZEOF_LONG) 
                 for (i=0; i<n; i++) MAT_BUFI(ipiv)[i] = ipivc[i];
                 free(ipivc); 
@@ -2797,8 +2954,10 @@ static PyObject* hesv(PyObject *self, PyObject *args, PyObject *kwrds)
                 for (k=0; k<n; k++) 
                     memcpy((double *) Ac + k*n, MAT_BUFD(A) + oA + k*ldA, 
                         n*sizeof(double));
+                Py_BEGIN_ALLOW_THREADS	    
                 dsysv_(&uplo, &n, &nrhs, (double *) Ac, &n, ipivc, 
                     MAT_BUFD(B)+oB, &ldB, work, &lwork, &info);
+                Py_END_ALLOW_THREADS	    
                 free(ipivc);  free(Ac); 
             }
             free(work);   
@@ -2820,8 +2979,10 @@ static PyObject* hesv(PyObject *self, PyObject *args, PyObject *kwrds)
 #else 
                 ipivc = MAT_BUFI(ipiv);		
 #endif
+                Py_BEGIN_ALLOW_THREADS	    
                 zhesv_(&uplo, &n, &nrhs, MAT_BUFZ(A)+oA, &ldA, ipivc, 
                     MAT_BUFZ(B)+oB, &ldB, (complex *) work, &lwork, &info);
+                Py_END_ALLOW_THREADS	    
 #if (SIZEOF_INT < SIZEOF_LONG) 
                 for (k=0; k<n; k++) MAT_BUFI(ipiv)[k] = ipivc[k];
                 free(ipivc); 
@@ -2837,8 +2998,10 @@ static PyObject* hesv(PyObject *self, PyObject *args, PyObject *kwrds)
                 for (k=0; k<n; k++) 
                     memcpy((complex *) Ac + k*n, MAT_BUFZ(A) + oA + k*ldA, 
                         n*sizeof(complex));
+                Py_BEGIN_ALLOW_THREADS	    
                 zhesv_(&uplo, &n, &nrhs, (complex *) Ac, &n, ipivc, 
                     MAT_BUFZ(B)+oB, &ldB, work, &lwork, &info);
+                Py_END_ALLOW_THREADS	    
                 free(ipivc);  free(Ac); 
             }
             free(work);   
@@ -2921,13 +3084,17 @@ static PyObject* trtrs(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             if (trans == 'C') trans = 'T';
+            Py_BEGIN_ALLOW_THREADS	    
             dtrtrs_(&uplo, &trans, &diag, &n, &nrhs, MAT_BUFD(A)+oA, 
                 &ldA, MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             ztrtrs_(&uplo, &trans, &diag, &n, &nrhs, MAT_BUFZ(A)+oA, 
                 &ldA, MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -2984,11 +3151,15 @@ static PyObject* trtri(PyObject *self, PyObject *args, PyObject *kwrds)
 
     switch (MAT_ID(A)){
         case DOUBLE:
+            Py_BEGIN_ALLOW_THREADS	    
             dtrtri_(&uplo, &diag, &n, MAT_BUFD(A)+oA, &ldA, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             ztrtri_(&uplo, &diag, &n, MAT_BUFZ(A)+oA, &ldA, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -3068,13 +3239,17 @@ static PyObject* tbtrs(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             if (trans == 'C') trans = 'T';
+            Py_BEGIN_ALLOW_THREADS	    
             dtbtrs_(&uplo, &trans, &diag, &n, &kd, &nrhs, MAT_BUFD(A)+oA, 
                 &ldA, MAT_BUFD(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         case COMPLEX:
+            Py_BEGIN_ALLOW_THREADS	    
             ztbtrs_(&uplo, &trans, &diag, &n, &kd, &nrhs, MAT_BUFZ(A)+oA, 
                 &ldA, MAT_BUFZ(B)+oB, &ldB, &info);
+            Py_END_ALLOW_THREADS	    
             break;
 
         default:
@@ -3158,26 +3333,34 @@ static PyObject* gels(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             if (trans == 'C') trans = 'T';
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dgels_(&trans, &m, &n, &nrhs, NULL, &ldA, NULL, &ldB, &wl.d,
                 &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = (void *) calloc(lwork, sizeof(double))))
                 return PyErr_NoMemory();
+            Py_BEGIN_ALLOW_THREADS	    
             dgels_(&trans, &m, &n, &nrhs, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(B)+oB, &ldB, (double *) work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
 	    break;
 
         case COMPLEX:
             if (trans == 'T') err_char("trans", "'N', 'C'");
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             zgels_(&trans, &m, &n, &nrhs, NULL, &ldA, NULL, &ldB, &wl.z,
                 &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) creal(wl.z);
             if (!(work = (void *) calloc(lwork, sizeof(complex))))
                 return PyErr_NoMemory();
+            Py_BEGIN_ALLOW_THREADS	    
             zgels_(&trans, &m, &n, &nrhs, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(B)+oB, &ldB, (complex *) work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
 	    break;
 
@@ -3241,23 +3424,31 @@ static PyObject* geqrf(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dgeqrf_(&m, &n, NULL, &ldA, NULL, &wl.d, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = (void *) calloc(lwork, sizeof(double))))
                 return PyErr_NoMemory();
+            Py_BEGIN_ALLOW_THREADS	    
             dgeqrf_(&m, &n, MAT_BUFD(A)+oA, &ldA, MAT_BUFD(tau), 
                 (double *) work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
 	    break;
 
         case COMPLEX:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             zgeqrf_(&m, &n, NULL, &ldA, NULL, &wl.z, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) creal(wl.z);
             if (!(work = (void *) calloc(lwork, sizeof(complex))))
                 return PyErr_NoMemory();
+            Py_BEGIN_ALLOW_THREADS	    
             zgeqrf_(&m, &n, MAT_BUFZ(A)+oA, &ldA, MAT_BUFZ(tau), 
                 (complex *) work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
 	    break;
 
@@ -3342,14 +3533,18 @@ static PyObject* ormqr(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dormqr_(&side, &trans, &m, &n, &k, NULL, &ldA, NULL, NULL,
                 &ldC, &wl.d, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = (void *) calloc(lwork, sizeof(double))))
                 return PyErr_NoMemory();
+            Py_BEGIN_ALLOW_THREADS	    
             dormqr_(&side, &trans, &m, &n, &k, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(tau), MAT_BUFD(C)+oC, &ldC, (double *) work, 
                 &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
 	    break;
 
@@ -3439,28 +3634,36 @@ static PyObject* unmqr(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             if (trans == 'C') trans = 'T';
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dormqr_(&side, &trans, &m, &n, &k, NULL, &ldA, NULL, NULL,
                 &ldC, &wl.d, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = (void *) calloc(lwork, sizeof(double))))
                 return PyErr_NoMemory();
+            Py_BEGIN_ALLOW_THREADS	    
             dormqr_(&side, &trans, &m, &n, &k, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(tau), MAT_BUFD(C)+oC, &ldC, (double *) work, 
                 &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
 	    break;
 
         case COMPLEX:
             if (trans == 'T') err_char("trans", "'N', 'C'");
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             zunmqr_(&side, &trans, &m, &n, &k, NULL, &ldA, NULL, NULL,
                 &ldC, &wl.z, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) creal(wl.z);
             if (!(work = (void *) calloc(lwork, sizeof(complex))))
                 return PyErr_NoMemory();
+            Py_BEGIN_ALLOW_THREADS	    
             zunmqr_(&side, &trans, &m, &n, &k, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(tau), MAT_BUFZ(C)+oC, &ldC, (complex *) work, 
                 &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);
 	    break;
 
@@ -3529,13 +3732,17 @@ static PyObject* syev(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
 	case DOUBLE:
 	    lwork=-1;
+            Py_BEGIN_ALLOW_THREADS	    
 	    dsyev_(&jobz, &uplo, &n, NULL, &ldA, NULL, &wl.d, &lwork, 
                 &info);
+            Py_END_ALLOW_THREADS	    
 	    lwork = (int) wl.d;
 	    if (!(work = calloc(lwork, sizeof(double))))
 		return PyErr_NoMemory();
+            Py_BEGIN_ALLOW_THREADS	    
 	    dsyev_(&jobz, &uplo, &n, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(W)+oW, work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
 	    free(work);   
 	    break;
 
@@ -3606,20 +3813,26 @@ static PyObject* heev(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
 	case DOUBLE:
 	    lwork=-1;
+            Py_BEGIN_ALLOW_THREADS	    
 	    dsyev_(&jobz, &uplo, &n, NULL, &ldA, NULL, &wl.d, &lwork, 
                 &info);
+            Py_END_ALLOW_THREADS	    
 	    lwork = (int) wl.d;
 	    if (!(work = (void *) calloc(lwork, sizeof(double))))
 		return PyErr_NoMemory();
+            Py_BEGIN_ALLOW_THREADS	    
 	    dsyev_(&jobz, &uplo, &n, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(W)+oW, (double *) work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
 	    free(work);   
 	    break;
 
         case COMPLEX:
 	    lwork=-1;
+            Py_BEGIN_ALLOW_THREADS	    
 	    zheev_(&jobz, &uplo, &n, NULL, &ldA, NULL, &wl.z, &lwork,
                 NULL, &info);
+            Py_END_ALLOW_THREADS	    
 	    lwork = (int) creal(wl.z);
 	    work = (void *) calloc(lwork, sizeof(complex));
 	    rwork = (double *) calloc(3*n-2, sizeof(double));
@@ -3627,9 +3840,11 @@ static PyObject* heev(PyObject *self, PyObject *args, PyObject *kwrds)
 		free(work);  free(rwork);
 		return PyErr_NoMemory();
 	    }
+            Py_BEGIN_ALLOW_THREADS	    
 	    zheev_(&jobz, &uplo, &n, MAT_BUFZ(A)+oA, &ldA, 
 		MAT_BUFD(W)+oW,  (complex *) work, &lwork, rwork, 
 		&info);
+            Py_END_ALLOW_THREADS	    
 	    free(work);  free(rwork);
 	    break;
 
@@ -3749,9 +3964,11 @@ static PyObject* syevx(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
 	    lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
 	    dsyevx_(&jobz, &range, &uplo, &n, NULL, &ldA, &vl, &vu, &il,
                 &iu, &abstol, &m, NULL, NULL, &ldZ, &wl, &lwork, NULL,
 	       	NULL, &info);
+            Py_END_ALLOW_THREADS	    
 	    lwork = (int) wl;
 	    work = (double *) calloc(lwork, sizeof(double));
 	    iwork = (int *) calloc(5*n, sizeof(int));
@@ -3760,10 +3977,12 @@ static PyObject* syevx(PyObject *self, PyObject *args, PyObject *kwrds)
 		free(work); free(iwork); free(ifail);
 	        return PyErr_NoMemory();
 	    }
+            Py_BEGIN_ALLOW_THREADS	    
 	    dsyevx_(&jobz, &range, &uplo, &n, MAT_BUFD(A)+oA, &ldA, &vl,
                 &vu, &il, &iu, &abstol, &m, MAT_BUFD(W)+oW, 
 		(jobz == 'V') ? MAT_BUFD(Z)+oZ : NULL,  &ldZ, work, 
 		&lwork, iwork, ifail, &info);
+            Py_END_ALLOW_THREADS	    
 	    free(work);   free(iwork);   free(ifail);   
             break;
 
@@ -3885,9 +4104,11 @@ static PyObject* heevx(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
 	    lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
 	    dsyevx_(&jobz, &range, &uplo, &n, NULL, &ldA, &vl, &vu, &il,
                 &iu, &abstol, &m, NULL, NULL, &ldZ, &wl.d, &lwork, NULL,
 	       	NULL, &info);
+            Py_END_ALLOW_THREADS	    
 	    lwork = (int) wl.d;
 	    work = (void *) calloc(lwork, sizeof(double));
 	    iwork = (int *) calloc(5*n, sizeof(int));
@@ -3896,18 +4117,22 @@ static PyObject* heevx(PyObject *self, PyObject *args, PyObject *kwrds)
 		free(work); free(iwork); free(ifail);
 	        return PyErr_NoMemory();
 	    }
+            Py_BEGIN_ALLOW_THREADS	    
 	    dsyevx_(&jobz, &range, &uplo, &n, MAT_BUFD(A)+oA, &ldA, &vl,
                 &vu, &il, &iu, &abstol, &m, MAT_BUFD(W)+oW, 
 		(jobz == 'V') ? MAT_BUFD(Z)+oZ : NULL,  &ldZ, 
 		(double *) work, &lwork, iwork, ifail, &info);
+            Py_END_ALLOW_THREADS	    
 	    free(work);  free(iwork);  free(ifail);   
             break;
 
 	case COMPLEX:
 	    lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
 	    zheevx_(&jobz, &range, &uplo, &n, NULL, &ldA, &vl, &vu, &il,
                 &iu, &abstol, &m, NULL, NULL, &ldZ, &wl.z, &lwork, NULL,
 	       	NULL, NULL, &info);
+            Py_END_ALLOW_THREADS	    
 	    lwork = (int) creal(wl.z);
 	    work = (void *) calloc(lwork, sizeof(complex));
 	    rwork = (double *) calloc(7*n, sizeof(double));
@@ -3917,10 +4142,12 @@ static PyObject* heevx(PyObject *self, PyObject *args, PyObject *kwrds)
 		free(work); free(rwork); free(iwork); free(ifail);
 	        return PyErr_NoMemory();
 	    }
+            Py_BEGIN_ALLOW_THREADS	    
 	    zheevx_(&jobz, &range, &uplo, &n, MAT_BUFZ(A)+oA, &ldA, &vl,
                 &vu, &il, &iu, &abstol, &m, MAT_BUFD(W)+oW, 
 		(jobz == 'V') ? MAT_BUFZ(Z)+oZ : NULL,  &ldZ, 
 		(complex *) work, &lwork, rwork, iwork, ifail, &info);
+            Py_END_ALLOW_THREADS	    
 	    free(work);  free(rwork);  free(iwork);  free(ifail);   
             break;
 
@@ -3992,8 +4219,10 @@ static PyObject* syevd(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             lwork = -1;
             liwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dsyevd_(&jobz, &uplo, &n, NULL, &ldA, NULL, &wl, &lwork, 
                 &iwl, &liwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl;
             liwork = iwl;
             work = (double *) calloc(lwork, sizeof(double));
@@ -4002,8 +4231,10 @@ static PyObject* syevd(PyObject *self, PyObject *args, PyObject *kwrds)
                 free(work);  free(iwork);
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
 	    dsyevd_(&jobz, &uplo, &n, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(W)+oW, work, &lwork, iwork, &liwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);  free(iwork);
             break;
 
@@ -4078,8 +4309,10 @@ static PyObject* heevd(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             lwork = -1;
             liwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dsyevd_(&jobz, &uplo, &n, NULL, &ldA, NULL, &wl.d, &lwork, 
                 &iwl, &liwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             liwork = iwl;
             work = (void *) calloc(lwork, sizeof(double));
@@ -4088,9 +4321,11 @@ static PyObject* heevd(PyObject *self, PyObject *args, PyObject *kwrds)
                 free(work);  free(iwork);
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             dsyevd_(&jobz, &uplo, &n, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(W)+oW, (double *) work, &lwork, iwork, &liwork,
                 &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   free(iwork);
             break;
 
@@ -4098,8 +4333,10 @@ static PyObject* heevd(PyObject *self, PyObject *args, PyObject *kwrds)
             lwork = -1;
             liwork = -1;
             lrwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             zheevd_(&jobz, &uplo, &n, NULL, &ldA, NULL, &wl.z, &lwork, 
                 &rwl, &lrwork, &iwl, &liwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             lrwork = (int) rwl;
             liwork = iwl;
@@ -4110,9 +4347,11 @@ static PyObject* heevd(PyObject *self, PyObject *args, PyObject *kwrds)
                 free(work);  free(rwork);  free(iwork);
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             zheevd_(&jobz, &uplo, &n, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFD(W)+oW, (complex *) work, &lwork, rwork,
                 &lrwork, iwork, &liwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);  free(rwork);  free(iwork);
             break;
 
@@ -4237,9 +4476,11 @@ static PyObject* syevr(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             lwork = -1;
             liwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dsyevr_(&jobz, &range, &uplo, &n, NULL, &ldA, &vl, &vu, &il,
                 &iu, &abstol, &m, NULL, NULL, &ldZ, NULL, &wl, &lwork, 
                 &iwl, &liwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl;
             liwork = iwl;
             work = (void *) calloc(lwork, sizeof(double));
@@ -4251,11 +4492,13 @@ static PyObject* syevr(PyObject *self, PyObject *args, PyObject *kwrds)
                 free(work);  free(iwork);  free(isuppz);
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             dsyevr_(&jobz, &range, &uplo, &n, MAT_BUFD(A)+oA, &ldA, &vl,
                 &vu, &il, &iu, &abstol, &m, MAT_BUFD(W)+oW,
                 (jobz == 'V') ? MAT_BUFD(Z)+oZ : NULL,  &ldZ, 
                 (jobz == 'V') ? isuppz : NULL, work, &lwork, iwork, 
                 &liwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   free(iwork);   free(isuppz);   
             break;
 
@@ -4381,9 +4624,11 @@ static PyObject* heevr(PyObject *self, PyObject *args, PyObject *kwrds)
         case DOUBLE:
             lwork = -1;
             liwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dsyevr_(&jobz, &range, &uplo, &n, NULL, &ldA, &vl, &vu, &il,
                 &iu, &abstol, &m, NULL, NULL, &ldZ, NULL, &wl.d, &lwork,
                 &iwl, &liwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             liwork = iwl;
             work = (void *) calloc(lwork, sizeof(double));
@@ -4395,11 +4640,13 @@ static PyObject* heevr(PyObject *self, PyObject *args, PyObject *kwrds)
                 free(work);  free(iwork);  free(isuppz);
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             dsyevr_(&jobz, &range, &uplo, &n, MAT_BUFD(A)+oA, &ldA, &vl,
                 &vu, &il, &iu, &abstol, &m, MAT_BUFD(W)+oW,
                 (jobz == 'V') ? MAT_BUFD(Z)+oZ : NULL,  &ldZ, 
                 (jobz == 'V') ? isuppz : NULL, (double *) work, &lwork,
 	       	iwork, &liwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   free(iwork);   free(isuppz);   
             break;
 
@@ -4407,9 +4654,11 @@ static PyObject* heevr(PyObject *self, PyObject *args, PyObject *kwrds)
             lwork = -1;
             liwork = -1;
 	    lrwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             zheevr_(&jobz, &range, &uplo, &n, NULL, &ldA, &vl, &vu, &il,
                 &iu, &abstol, &m, NULL, NULL, &ldZ, NULL, &wl.z, &lwork,
                 &rwl, &lrwork, &iwl, &liwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) creal(wl.z);
 	    lrwork = (int) rwl;
             liwork = iwl;
@@ -4423,11 +4672,13 @@ static PyObject* heevr(PyObject *self, PyObject *args, PyObject *kwrds)
                 free(work);  free(rwork);  free(iwork);  free(isuppz);
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             zheevr_(&jobz, &range, &uplo, &n, MAT_BUFZ(A)+oA, &ldA, &vl,
                 &vu, &il, &iu, &abstol, &m, MAT_BUFD(W)+oW,
                 (jobz == 'V') ? MAT_BUFZ(Z)+oZ : NULL,  &ldZ, 
                 (jobz == 'V') ? isuppz : NULL, (complex *) work, &lwork,
 	       	rwork, &lrwork, iwork, &liwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   free(rwork); free(iwork);   free(isuppz);   
             break;
 
@@ -4526,14 +4777,18 @@ static PyObject* sygv(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
 	case DOUBLE:
             lwork=-1;
+            Py_BEGIN_ALLOW_THREADS	    
             dsygv_(&itype, &jobz, &uplo, &n, NULL, &ldA, NULL, &ldB,
                 NULL, &wl.d, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = calloc(lwork, sizeof(double))))
                 return PyErr_NoMemory();
+            Py_BEGIN_ALLOW_THREADS	    
             dsygv_(&itype, &jobz, &uplo, &n, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(B)+oB, &ldB, MAT_BUFD(W)+oW, work, &lwork, 
                 &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   
             break;
 
@@ -4634,14 +4889,18 @@ static PyObject* hegv(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             lwork=-1;
+            Py_BEGIN_ALLOW_THREADS	    
             dsygv_(&itype, &jobz, &uplo, &n, NULL, &ldA, NULL, &ldB,
                 NULL, &wl.d, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = calloc(lwork, sizeof(double))))
                 return PyErr_NoMemory();
+            Py_BEGIN_ALLOW_THREADS	    
             dsygv_(&itype, &jobz, &uplo, &n, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(B)+oB, &ldB, MAT_BUFD(W)+oW, work, &lwork, 
                 &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   
             break;
 
@@ -4649,8 +4908,10 @@ static PyObject* hegv(PyObject *self, PyObject *args, PyObject *kwrds)
 #if 0       
             /* zhegv does not handle lwork=-1 correctly */
             lwork=-1;
+            Py_BEGIN_ALLOW_THREADS	    
             zhegv_(&itype, &jobz, &uplo, &n, NULL, &n, NULL, &n, NULL, 
                 &wl.z, &lwork, NULL, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) creal(wl.z);
 #endif
             /* replaces call to zhegv with lwork=-1 */
@@ -4663,9 +4924,11 @@ static PyObject* hegv(PyObject *self, PyObject *args, PyObject *kwrds)
                 free(work);  free(rwork);
                 return PyErr_NoMemory();
             }	
+            Py_BEGIN_ALLOW_THREADS	    
             zhegv_(&itype, &jobz, &uplo, &n, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFZ(B)+oB, &ldB, MAT_BUFD(W)+oW, (complex *) work, 
                 &lwork, rwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);  free(rwork);
             break;
 
@@ -4818,25 +5081,31 @@ static PyObject* gesvd(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             lwork = -1;
+             Py_BEGIN_ALLOW_THREADS	    
             dgesvd_(&jobu, &jobvt, &m, &n, NULL, &ldA, NULL, NULL, 
                 &ldU, NULL, &ldVt, &wl.d, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             if (!(work = (void *) calloc(lwork, sizeof(double)))){
                 free(work);  
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             dgesvd_(&jobu, &jobvt, &m, &n, MAT_BUFD(A)+oA, &ldA, 
                 MAT_BUFD(S)+oS,  (jobu == 'A' || jobu == 'S') ? 
 		MAT_BUFD(U)+oU : NULL, &ldU, (jobvt == 'A' || 
                 jobvt == 'S') ?  MAT_BUFD(Vt)+oVt : NULL, &ldVt, 
 		(double *) work, &lwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   
             break;
 
 	case COMPLEX:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             zgesvd_(&jobu, &jobvt, &m, &n, NULL, &ldA, NULL, NULL, 
                 &ldU, NULL, &ldVt, &wl.z, &lwork, NULL, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) creal(wl.z);
             work = (void *) calloc(lwork, sizeof(complex));
             rwork = (double *) calloc(5*MIN(m,n), sizeof(double));
@@ -4844,11 +5113,13 @@ static PyObject* gesvd(PyObject *self, PyObject *args, PyObject *kwrds)
                 free(work);  free(rwork);
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             zgesvd_(&jobu, &jobvt, &m, &n, MAT_BUFZ(A)+oA, &ldA, 
                 MAT_BUFD(S)+oS, (jobu == 'A' || jobu == 'S') ? 
                 MAT_BUFZ(U)+oU : NULL,  &ldU, (jobvt == 'A' || 
                 jobvt == 'S') ? MAT_BUFZ(Vt)+oVt : NULL, &ldVt, 
                 (complex *) work, &lwork, rwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   free(rwork); 
             break;
 
@@ -4992,8 +5263,10 @@ static PyObject* gesdd(PyObject *self, PyObject *args, PyObject *kwrds)
     switch (MAT_ID(A)){
         case DOUBLE:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             dgesdd_(&jobz, &m, &n, NULL, &ldA, NULL, NULL, &ldU, NULL, 
                 &ldVt, &wl.d, &lwork, NULL, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) wl.d;
             work = (void *) calloc(lwork, sizeof(double));
             iwork = (int *) calloc(8*MIN(m,n), sizeof(int));
@@ -5001,19 +5274,23 @@ static PyObject* gesdd(PyObject *self, PyObject *args, PyObject *kwrds)
                 free(work);  free(iwork);
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             dgesdd_(&jobz, &m, &n, MAT_BUFD(A)+oA, &ldA, MAT_BUFD(S)+oS,
                 (jobz == 'A' || jobz == 'S' || (jobz == 'O' && m<n)) ?
                 MAT_BUFD(U)+oU : NULL, &ldU, (jobz == 'A' || 
                 jobz == 'S' || (jobz == 'O'  && m>=n)) ? 
                 MAT_BUFD(Vt)+oVt : NULL, &ldVt, (double *) work, &lwork,
                 iwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);   free(iwork);
             break;
 
 	case COMPLEX:
             lwork = -1;
+            Py_BEGIN_ALLOW_THREADS	    
             zgesdd_(&jobz, &m, &n, NULL, &ldA, NULL, NULL, &ldU, NULL, 
                 &ldVt, &wl.z, &lwork, NULL, NULL, &info);
+            Py_END_ALLOW_THREADS	    
             lwork = (int) creal(wl.z);
             work = (void *) calloc(lwork, sizeof(complex));
             iwork = (int *) calloc(8*MIN(m,n), sizeof(int));
@@ -5023,12 +5300,14 @@ static PyObject* gesdd(PyObject *self, PyObject *args, PyObject *kwrds)
                 free(work);  free(iwork); free(rwork);
                 return PyErr_NoMemory();
             }
+            Py_BEGIN_ALLOW_THREADS	    
             zgesdd_(&jobz, &m, &n, MAT_BUFZ(A)+oA, &ldA, MAT_BUFD(S)+oS,
                 (jobz == 'A' || jobz == 'S' || (jobz == 'O' && m<n)) ? 
 		MAT_BUFZ(U)+oU : NULL,  &ldU, (jobz == 'A' || 
                 jobz == 'S' || (jobz == 'O' && m>=n)) ? 
                 MAT_BUFZ(Vt)+oVt : NULL, &ldVt, (complex *) work, 
                 &lwork, rwork, iwork, &info);
+            Py_END_ALLOW_THREADS	    
             free(work);  free(iwork); free(rwork); 
             break;
 
@@ -5041,6 +5320,458 @@ static PyObject* gesdd(PyObject *self, PyObject *args, PyObject *kwrds)
 }
 
 
+static char doc_gees[] = 
+    "Schur factorization of a real of complex matrix.\n\n"
+    "sdim = gees(A, w=None, V=None, select=None, n=A.size[0],\n" 
+    "            ldA=max(1,A.size[0]), ldV=max(1,Vs.size[0]), offsetA=0,\n"
+    "            offsetw=0, offsetV=0)\n\n"
+    "PURPOSE\n"
+    "Computes the real Schur form A = V * S * V^T or the complex Schur\n"
+    "form A = V * S * V^H, the eigenvalues, and, optionally, the matrix\n"
+    "of Schur vectors of an n by n matrix A.  The real Schur form is \n"
+    "computed if A is real, and the conmplex Schur form is computed if \n"
+    "A is complex.  On exit, A is replaced with S.  If the argument w is\n"
+    "provided, the eigenvalues are returned in w.  If V is provided, the\n"
+    "Schur vectors are computed and returned in V.  The argument select\n"
+    "can be used to obtain an ordered Schur factorization.  It must be a\n"
+    "Python function that can be called as f(s) with s complex, and \n"
+    "returns 0 or 1.  The eigenvalues s for which f(s) is 1 will be \n"
+    "placed first in the Schur factorization.   For the real case, \n"
+    "eigenvalues s for which f(s) or f(conj(s)) is 1, are placed first.\n"
+    "If select is provided, gees() returns the number of eigenvalues \n"
+    "that satisfy the selection criterion.   Otherwise, it returns 0.\n\n"
+    "ARGUMENTS\n"
+    "A         'd' or 'z' matrix\n\n"  
+    "w         'z' matrix of length at least n\n\n"  
+    "V         'd' or 'z' matrix.  Must have the same type as A.\n\n"
+    "select    Python function that takes a complex number as argument\n"
+    "          and returns True or False.\n\n"
+    "n         integer.  If negative, the default value is used.\n\n"
+    "ldA       nonnegative integer.  ldA >= max(1,n).\n"
+    "          If zero, the default value is used.\n\n"
+    "ldV       nonnegative integer.  ldV >= 1 and ldV >= n if V is\n"
+    "          present.  If zero, the default value is used (with \n"
+    "          V.size[0] replaced by 0 if V is None).\n\n"
+    "offsetA   nonnegative integer\n\n"
+    "offsetW   nonnegative integer\n\n"
+    "offsetV   nonnegative integer\n\n"
+    "sdim      number of eigenvalues that satisfy the selection\n"
+    "          criterion specified by select.";
+
+static PyObject *py_select_r;
+static PyObject *py_select_c;
+
+extern int fselect_c(complex *w)
+{
+    PyObject *wpy, *result;
+    int a = 0;
+
+    wpy = PyComplex_FromDoubles(creal(*w), cimag(*w));
+    if (!(result = PyObject_CallFunctionObjArgs(py_select_c, wpy, NULL))) {
+        Py_XDECREF(wpy);
+        return -1;
+    }
+    if PyInt_Check(result)
+        a = (int) PyInt_AsLong(result);  
+    else 
+        PyErr_SetString(PyExc_TypeError, "select() must return an integer "
+            "argument");
+    Py_XDECREF(wpy);  Py_XDECREF(result);
+    return a;
+}
+
+extern int fselect_r(double *wr, double *wi)
+{
+    PyObject *wpy, *result;
+    int a = 0;
+
+    wpy = PyComplex_FromDoubles(*wr, *wi);
+    if (!(result = PyObject_CallFunctionObjArgs(py_select_r, wpy, NULL))) {
+        Py_XDECREF(wpy);
+        return -1;
+    }
+    if PyInt_Check(result)
+        a = (int) PyInt_AsLong(result);  
+    else 
+        PyErr_SetString(PyExc_TypeError, "select() must return an integer "
+           "argument");
+    Py_XDECREF(wpy);  Py_XDECREF(result);
+    return a;
+}
+
+static PyObject* gees(PyObject *self, PyObject *args, PyObject *kwrds)
+{
+    PyObject *F=NULL;
+    matrix *A, *W=NULL, *Vs=NULL; 
+    int n=-1, ldA=0, ldVs=0, oA=0, oVs=0, oW=0, info, lwork, sdim, k,
+        *bwork=NULL; 
+    double *wr=NULL, *wi=NULL, *rwork=NULL;
+    complex *w=NULL;
+    void *work=NULL;
+    number wl;
+    char *kwlist[] = {"A", "w", "V", "select", "n", "ldA", "ldV", 
+        "offsetA", "offsetw", "offsetV", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|OOOiiiiii",
+        kwlist, &A, &W, &Vs, &F, &n, &ldA, &ldVs, &oA, &oW, &oVs)) 
+        return NULL;
+
+    if (!Matrix_Check(A)) err_mtrx("A"); 
+    if (n < 0){ 
+        n = A->nrows;
+        if (n != A->ncols){
+            PyErr_SetString(PyExc_TypeError, "A must be square");
+            return NULL;
+        }
+    }
+    if (ldA == 0) ldA = MAX(1,A->nrows);
+    if (ldA < MAX(1,n)) err_ld("ldA");
+    if (oA < 0) err_nn_int("offsetA");
+    if (oA + (n-1)*ldA + n > len(A)) err_buf_len("A");
+
+    if (W){
+        if (!Matrix_Check(W) || MAT_ID(W) != COMPLEX) 
+            PY_ERR_TYPE("W must be a matrix with typecode 'z'") 
+        if (oW < 0) err_nn_int("offsetW");
+        if (oW + n > len(W)) err_buf_len("W");
+    }
+
+    if (Vs){
+        if (!Matrix_Check(Vs)) err_mtrx("Vs");
+        if (MAT_ID(Vs) != MAT_ID(A)) err_conflicting_ids; 
+        if (ldVs == 0) ldVs = MAX(1, Vs->nrows);
+        if (ldVs < MAX(1,n)) err_ld("ldVs");
+        if (oVs < 0) err_nn_int("offsetVs");
+        if (oVs + (n-1)*ldVs + n > len(Vs)) err_buf_len("Vs"); 
+    } else {
+        if (ldVs == 0) ldVs = 1;
+        if (ldVs < 1) err_ld("ldVs");
+    }
+
+    if (F && !PyFunction_Check(F)) 
+        PY_ERR_TYPE("select must be a Python function") 
+
+
+    switch (MAT_ID(A)){
+        case DOUBLE:
+            lwork = -1;
+            dgees_(Vs ? "V" : "N", F ? "S" : "N", NULL, &n, NULL, &ldA, 
+                &sdim, NULL, NULL, NULL, &ldVs, &wl.d, &lwork, NULL, 
+                &info);
+            lwork = (int) wl.d;
+            work = (void *) calloc(lwork, sizeof(double));
+            wr = (double *) calloc(n, sizeof(double));
+            wi = (double *) calloc(n, sizeof(double));
+            if (F) bwork = (int *) calloc(n, sizeof(int));
+            if (!work || !wr || !wi || (F && !bwork)){
+                free(work);  free(wr);  free(wi);  free(bwork);
+                return PyErr_NoMemory();
+            }
+            py_select_r = F;
+            dgees_(Vs ? "V": "N", F ? "S" : "N", F ? &fselect_r : NULL, 
+                &n, MAT_BUFD(A) + oA, &ldA, &sdim, wr, wi, 
+                Vs ? MAT_BUFD(Vs) + oVs : NULL, &ldVs, (double *) work, 
+                &lwork, bwork, &info);
+            if (W) for (k=0; k<n; k++)
+                MAT_BUFZ(W)[oW + k] = wr[k] + I * wi[k];
+            free(work);  free(wr);  free(wi);  free(bwork);
+            break;
+
+	case COMPLEX:
+            lwork = -1;
+            zgees_(Vs ? "V" : "N", F ? "S": "N", NULL, &n, NULL, &ldA, 
+                &sdim, NULL, NULL, &ldVs, &wl.z, &lwork, NULL, NULL, 
+                &info);
+            lwork = (int) creal(wl.z);
+            work = (void *) calloc(lwork, sizeof(complex));
+            rwork = (double *) calloc(n, sizeof(complex));
+            if (F) bwork = (int *) calloc(n, sizeof(int));
+            if (!W) w = (complex *) calloc(n, sizeof(complex));
+	    if (!work || !rwork || (F && !bwork) || (!W && !w) ){ 
+                free(work);  free(rwork); free(bwork);  free(w); 
+                return PyErr_NoMemory();
+            }
+            py_select_c = F;
+            zgees_(Vs ? "V": "N", F ? "S" : "N", F ? &fselect_c : NULL, 
+                &n, MAT_BUFZ(A) + oA, &ldA, &sdim, 
+                W ? MAT_BUFZ(W) + oW : w, Vs ? MAT_BUFZ(Vs) + oVs : NULL, 
+                &ldVs, (complex *) work, &lwork, (complex *) rwork,  bwork,
+                 &info);
+            free(work);  free(rwork); free(bwork);  free(w);
+            break;
+
+        default:
+            err_invalid_id;
+    }
+
+    if (PyErr_Occurred()) return NULL;
+
+    if (info) err_lapack
+    else return Py_BuildValue("i", F ? sdim : 0);
+}
+
+
+static char doc_gges[] = 
+    "Generalized Schur factorization of real or complex matrices.\n\n"
+    "sdim = gges(A, B, a=None, b=None, Vl=None, Vr=None, select=None,\n"
+    "            n=A.size[0], ldA=max(1,A.size[0]),\n"
+    "            ldB=max(1,B.size[0]), ldVl=max(1,Vl.size[0]),\n"
+    "            ldVr=max(1,Vr.size[0]), offsetA=0, offestB=0, \n"
+    "            offseta=0, offsetb=0, offsetVl=0, offsetVr=0)\n\n"
+    "PURPOSE\n"
+    "Computes the real generalized Schur form A = Vl * S * Vr^T, \n"
+    "B = Vl * T * Vr^T, or the complex generalized Schur form \n"
+    "A = Vl * S * Vr^H, B = Vl * T * Vr^H of the square matrices A, B,\n"
+    "the generalized eigenvalues, and, optionally, the matrices of left \n"
+    "and right Schur vectors.  The real form is computed if A and B are \n"
+    "real, and the complex Schur form is computed if A and B are\n"
+    "complex.  On exit, A is replaced with S and B is replaced with T.\n"
+    "If the arguments a and b are provided, then on return a[i] / b[i] \n"
+    "is the ith generalized eigenvalue.  If Vl is provided, then the \n"
+    "left Schur vectors are computed and returned in Vl.  If Vr is \n"
+    "provided then the right Schur vectors are computed and returned \n"
+    "in Vr.  The argument select can be used to obtain an ordered Schur\n"
+    "factorization.  It must be a Python function that can be called as\n"
+    "f(u,v) with u complex, and v real, and returns 0 or 1.  The \n" 
+    "eigenvalues u/v for which f(u, v) is 1 will be placed first on the\n"
+    "diagonal of S and T.  For the real case, eigenvalues u/v for which\n"
+    "f(u, v) or f(conj(u), v) is 1, are placed first.  If select is \n"
+    "provided, gges() returns the number of eigenvalues that satisfy the\n"
+    "selection criterion.  Otherwise, it returns 0.\n\n"  
+    "ARGUMENTS\n"
+    "A         'd' or 'z' matrix\n\n"  
+    "B         'd' or 'z' matrix.  Must have the same type as A.\n\n"  
+    "a         'z' matrix of length at least n\n\n"  
+    "b         'd' matrix of length at least n\n\n"  
+    "Vl        'd' or 'z' matrix.  Must have the same type as A.\n\n"
+    "Vr        'd' or 'z' matrix.  Must have the same type as A.\n\n"
+    "select    Python function that takes a complex and a real number \n"
+    "          as argument and returns True or False.\n\n"
+    "n         integer.  If negative, the default value is used.\n\n"
+    "ldA       nonnegative integer.  ldA >= max(1,n).\n"
+    "          If zero, the default value is used.\n\n"
+    "ldB       nonnegative integer.  ldB >= max(1,n).\n"
+    "          If zero, the default value is used.\n\n"
+    "ldVl      nonnegative integer.  ldVl >= 1 and ldVl >= n if Vl \n"
+    "          is present.  If zero, the default value is used (with \n"
+    "          Vl.size[0] replaced by 0 if Vl is None).\n\n"
+    "ldVr      nonnegative integer.  ldVr >= 1 and ldVr >= n if Vr \n"
+    "          is present.  If zero, the default value is used (with \n"
+    "          Vr.size[0] replaced by 0 if Vr is None).\n\n"
+    "offsetA   nonnegative integer\n\n"
+    "offsetB   nonnegative integer\n\n"
+    "offseta   nonnegative integer\n\n"
+    "offsetb   nonnegative integer\n\n"
+    "offsetVl  nonnegative integer\n\n"
+    "offsetVr  nonnegative integer\n\n"
+    "sdim      number of eigenvalues that satisfy the selection\n"
+    "          criterion specified by select.";
+
+static PyObject *py_select_gr;
+static PyObject *py_select_gc;
+
+extern int fselect_gc(complex *w, double *v)
+{
+   PyObject *wpy, *vpy, *result;
+   int a = 0;
+
+   wpy = PyComplex_FromDoubles(creal(*w), cimag(*w));
+   vpy = PyFloat_FromDouble(*v);
+   if (!(result = PyObject_CallFunctionObjArgs(py_select_gc, wpy, vpy,
+       NULL))) {
+       Py_XDECREF(wpy); Py_XDECREF(vpy); 
+       return -1;
+   }
+   if PyInt_Check(result)
+       a = (int) PyInt_AsLong(result);  
+   else 
+       PyErr_SetString(PyExc_TypeError, "select() must return an integer "
+           "argument");
+   Py_XDECREF(wpy);  Py_XDECREF(vpy);  Py_XDECREF(result);
+   return a;
+}
+
+extern int fselect_gr(double *wr, double *wi, double *v)
+{
+   PyObject *wpy, *vpy, *result;
+   int a = 0;
+
+   wpy = PyComplex_FromDoubles(*wr, *wi);
+   vpy = PyFloat_FromDouble(*v);
+   if (!(result = PyObject_CallFunctionObjArgs(py_select_gr, wpy, vpy, 
+       NULL))) {
+       Py_XDECREF(wpy); Py_XDECREF(vpy);
+       return -1;
+   }
+   if PyInt_Check(result)
+       a = (int) PyInt_AsLong(result);  
+   else 
+       PyErr_SetString(PyExc_TypeError, "select() must return an integer "
+           "argument");
+   Py_XDECREF(wpy);  Py_XDECREF(vpy);  Py_XDECREF(result);
+   return a;
+}
+
+static PyObject* gges(PyObject *self, PyObject *args, PyObject *kwrds)
+{
+    PyObject *F=NULL;
+    matrix *A, *B, *a=NULL, *b=NULL, *Vsl=NULL, *Vsr=NULL; 
+    int n=-1, ldA=0, ldB=0, ldVsl=0, ldVsr=0, oA=0, oB=0, oa=0, ob=0, 
+        oVsl=0, oVsr=0, info, lwork, sdim, k, *bwork=NULL; 
+    double *ar=NULL, *ai=NULL, *rwork=NULL;
+    complex *ac=NULL; 
+    void *work=NULL, *bc=NULL;
+    number wl;
+
+    char *kwlist[] = {"A", "B", "a", "b", "Vl", "Vr", "select", "n", 
+        "ldA", "ldB", "ldVl", "ldVr", "offsetA", "offsetB", "offseta", 
+        "offsetb", "offsetVl", "offsetVr", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OO|OOOOOiiiiiiiiiii",
+        kwlist, &A, &B, &a, &b, &Vsl, &Vsr, &F, &n, &ldA, &ldB, &ldVsl, 
+        &ldVsr, &oA, &oB, &oa, &ob, &oVsl, &oVsr)) return NULL;
+
+    if (!Matrix_Check(A)) err_mtrx("A"); 
+    if (!Matrix_Check(B)) err_mtrx("B");
+    if (MAT_ID(B) != MAT_ID(A)) err_conflicting_ids; 
+    if (n < 0){ 
+        n = A->nrows;
+        if (n != A->ncols){
+            PyErr_SetString(PyExc_TypeError, "A must be square");
+            return NULL;
+        }
+        if (n != B->nrows || n != B->ncols){
+            PyErr_SetString(PyExc_TypeError, "B must be square and of the "
+                "same order as A");
+            return NULL;
+        }
+    }
+    if (ldA == 0) ldA = MAX(1,A->nrows);
+    if (ldA < MAX(1,n)) err_ld("ldA");
+    if (oA < 0) err_nn_int("offsetA");
+    if (oA + (n-1)*ldA + n > len(A)) err_buf_len("A");
+    if (ldB == 0) ldB = MAX(1, B->nrows);
+    if (ldB < MAX(1,n)) err_ld("ldB");
+    if (oB < 0) err_nn_int("offsetB");
+    if (oB + (n-1)*ldB + n > len(B)) err_buf_len("B");
+
+    if (a){
+        if (!Matrix_Check(a) || MAT_ID(a) != COMPLEX) 
+            PY_ERR_TYPE("a must be a matrix with typecode 'z'") 
+        if (oa < 0) err_nn_int("offseta");
+        if (oa + n > len(a)) err_buf_len("a");
+        if (!b){
+            PyErr_SetString(PyExc_ValueError, "'b' must be provided if "
+                "'a' is provided");
+            return NULL;
+        }
+    }
+    if (b){
+        if (!Matrix_Check(b) || MAT_ID(b) != DOUBLE) 
+            PY_ERR_TYPE("b must be a matrix with typecode 'd'") 
+        if (ob < 0) err_nn_int("offsetb");
+        if (ob + n > len(b)) err_buf_len("b");
+        if (!a){
+            PyErr_SetString(PyExc_ValueError, "'a' must be provided if "
+                "'b' is provided");
+            return NULL;
+        }
+    }
+
+    if (Vsl){
+        if (!Matrix_Check(Vsl)) err_mtrx("Vsl");
+        if (MAT_ID(Vsl) != MAT_ID(A)) err_conflicting_ids; 
+        if (ldVsl == 0) ldVsl = MAX(1, Vsl->nrows);
+        if (ldVsl < MAX(1,n)) err_ld("ldVsl");
+        if (oVsl < 0) err_nn_int("offsetVsl");
+        if (oVsl + (n-1)*ldVsl + n > len(Vsl)) err_buf_len("Vsl"); 
+    } else {
+        if (ldVsl == 0) ldVsl = 1;
+        if (ldVsl < 1) err_ld("ldVsl");
+    }
+
+    if (Vsr){
+        if (!Matrix_Check(Vsr)) err_mtrx("Vsr");
+        if (MAT_ID(Vsr) != MAT_ID(A)) err_conflicting_ids; 
+        if (ldVsr == 0) ldVsr = MAX(1, Vsr->nrows);
+        if (ldVsr < MAX(1,n)) err_ld("ldVsr");
+        if (oVsr < 0) err_nn_int("offsetVsr");
+        if (oVsr + (n-1)*ldVsr + n > len(Vsr)) err_buf_len("Vsr"); 
+    } else {
+        if (ldVsr == 0) ldVsr = 1;
+        if (ldVsr < 1) err_ld("ldVsr");
+    }
+
+    if (F && !PyFunction_Check(F)) 
+        PY_ERR_TYPE("select must be a Python function") 
+
+    switch (MAT_ID(A)){
+        case DOUBLE:
+            lwork = -1;
+            dgges_(Vsl ? "V" : "N", Vsr ? "V" : "N", F ? "S" : "N", NULL,
+                &n, NULL, &ldA, NULL, &ldB, &sdim, NULL, NULL, NULL, NULL,
+                &ldVsl, NULL, &ldVsr, &wl.d, &lwork, NULL, &info); 
+            lwork = (int) wl.d;
+            work = (void *) calloc(lwork, sizeof(double));
+            ar = (double *) calloc(n, sizeof(double));
+            ai = (double *) calloc(n, sizeof(double));
+            if (!b) bc = (double *) calloc(n, sizeof(double));
+            if (F) bwork = (int *) calloc(n, sizeof(int));
+            if (!work || !ar || !ai || (!b && !bc) || (F && !bwork)){
+                free(work);  free(ar);  free(ai);  free(b);  free(bwork);
+                return PyErr_NoMemory();
+            }
+            py_select_gr = F;
+            dgges_(Vsl ? "V" : "N", Vsr ? "V" : "N", F ? "S" : "N", 
+                F ? &fselect_gr : NULL, &n, MAT_BUFD(A) + oA, &ldA, 
+                MAT_BUFD(B) + oB, &ldB, &sdim, ar, ai, 
+                b ? MAT_BUFD(b) + ob : (double *) bc, 
+                Vsl ? MAT_BUFD(Vsl) + oVsl : NULL, &ldVsl, 
+                Vsr ? MAT_BUFD(Vsr) + oVsr : NULL, &ldVsr, 
+                (double *) work, &lwork, bwork, &info);
+            if (a) for (k=0; k<n; k++)
+                MAT_BUFZ(a)[oa + k] = ar[k] + I * ai[k];
+            free(work);  free(ar);  free(ai);  free(bc); free(bwork);
+            break;
+
+	case COMPLEX:
+            lwork = -1;
+            zgges_(Vsl ? "V" : "N", Vsr ? "V" : "N", F ? "S" : "N", NULL,
+                &n, NULL, &ldA, NULL, &ldB, &sdim, NULL, NULL, NULL, 
+                &ldVsl, NULL, &ldVsr, &wl.z, &lwork, NULL, NULL, &info);
+            lwork = (int) creal(wl.z);
+            work = (void *) calloc(lwork, sizeof(complex));
+            rwork = (double *) calloc(8*n, sizeof(double));
+            if (F) bwork = (int *) calloc(n, sizeof(int));
+            if (!a) ac = (complex *) calloc(n, sizeof(complex));
+            bc = (complex *) calloc(n, sizeof(complex));
+	    if (!work || !rwork || (F && !bwork) || (!a && !ac) || !bc){
+                free(work);  free(rwork); free(bwork); free(ac); free(bc);
+                return PyErr_NoMemory();
+            }
+            py_select_gc = F;
+            zgges_(Vsl ? "V": "N", Vsr ? "V" : "N", F ? "S" : "N", 
+                F ? &fselect_gc : NULL, &n, MAT_BUFZ(A) + oA, &ldA, 
+                MAT_BUFZ(B) + oB, &ldB, &sdim, a ? MAT_BUFZ(a) + oa : ac, 
+                (complex *) bc, Vsl ? MAT_BUFZ(Vsl) + oVsl : NULL, &ldVsl,
+                Vsr ? MAT_BUFZ(Vsr) + oVsr : NULL, &ldVsr, 
+                (complex *) work, &lwork, rwork,  bwork, &info);
+            if (b) for (k=0; k<n; k++)
+                MAT_BUFD(b)[ob + k] = (double) creal(((complex *) bc)[k]);
+
+            free(work);  free(rwork); free(bwork); free(ac);  free(bc);
+            break;
+
+        default:
+            err_invalid_id;
+    }
+
+    if (PyErr_Occurred()) return NULL;
+
+    if (info) err_lapack
+    else return Py_BuildValue("i", F ? sdim : 0);
+}
+
 static PyMethodDef lapack_functions[] = {
 {"getrf", (PyCFunction) getrf, METH_VARARGS|METH_KEYWORDS, doc_getrf},
 {"getrs", (PyCFunction) getrs, METH_VARARGS|METH_KEYWORDS, doc_getrs},
@@ -5089,6 +5820,8 @@ static PyMethodDef lapack_functions[] = {
 {"heevr", (PyCFunction) heevr, METH_VARARGS|METH_KEYWORDS, doc_heevr},
 {"gesvd", (PyCFunction) gesvd, METH_VARARGS|METH_KEYWORDS, doc_gesvd},
 {"gesdd", (PyCFunction) gesdd, METH_VARARGS|METH_KEYWORDS, doc_gesdd},
+{"gees", (PyCFunction) gees, METH_VARARGS|METH_KEYWORDS, doc_gees},
+{"gges", (PyCFunction) gges, METH_VARARGS|METH_KEYWORDS, doc_gges},
 {NULL}  /* Sentinel */
 };
 
diff --git a/src/C/misc.h b/src/C/misc.h
index debb469..ffefba2 100644
--- a/src/C/misc.h
+++ b/src/C/misc.h
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -49,6 +49,7 @@ typedef union {
 #define X_ID(O)    (Matrix_Check(O) ? MAT_ID(O)    : SP_ID(O))
 #define X_NROWS(O) (Matrix_Check(O) ? MAT_NROWS(O) : SP_NROWS(O))
 #define X_NCOLS(O) (Matrix_Check(O) ? MAT_NCOLS(O) : SP_NCOLS(O))
+#define X_Matrix_Check(O) (Matrix_Check(O) || SpMatrix_Check(O))
 
 #define TypeCheck_CObject(O,str,errstr) { \
     char *descr = PyCObject_GetDesc(O);   \
diff --git a/src/C/misc_solvers.c b/src/C/misc_solvers.c
index 14f7b44..588e97d 100644
--- a/src/C/misc_solvers.c
+++ b/src/C/misc_solvers.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/C/sparse.c b/src/C/sparse.c
index 89d2706..a1f8331 100644
--- a/src/C/sparse.c
+++ b/src/C/sparse.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -27,8 +27,6 @@
 
 #define CONJ(flag, val) (flag == 'C' ? conj(val) : val)
 
-extern PyObject *base_mod;
-
 extern const int  E_SIZE[];
 extern const char TC_CHAR[][2];
 extern number One[3], MinusOne[3], Zero[3];
@@ -802,7 +800,7 @@ static int sp_daxpy(number a, void *x, void *y, int sp_x, int sp_y,
 	  while (X->colptr[jX+1] == kX+1) jX++;
 	  kX++;
 	} 
-	else ((double *)Z->values)[nnzZ++] = ((double *)Y->values)[kY];		  
+	else ((double *)Z->values)[nnzZ++] = ((double *)Y->values)[kY];
       }
     }
     
@@ -831,7 +829,7 @@ static int sp_daxpy(number a, void *x, void *y, int sp_x, int sp_y,
 	((double *)Y->values)[kY] += a.d*X[jY*Y->nrows + Y->rowind[kY]];      
     }
   }
-  else { // if (!p_x && !sp_y) {
+  else { // if (!sp_x && !sp_y) {
 
     double *X = x;
     ccs *Y = y;
@@ -2334,9 +2332,10 @@ spmatrix * SpMatrix_NewFromIJV(matrix *Il, matrix *Jl, matrix *V,
 
 static void spmatrix_dealloc(spmatrix* self)
 {
-  free(SP_VAL(self));
-  free(SP_ROW(self));
-  free(SP_COL(self));
+  free(self->obj->values);
+  free(self->obj->colptr);
+  free(self->obj->rowind);
+  free(self->obj);
   self->ob_type->tp_free((PyObject*)self);
 }
 
@@ -2447,11 +2446,15 @@ spmatrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 static PyObject *
 spmatrix_str(matrix *self) {
 
+  PyObject *cvxopt = PyImport_ImportModule("cvxopt");
   PyObject *str, *ret;
-   
-  if (!(str = PyObject_GetAttrString(base_mod, "spmatrix_str")))
-    PY_ERR(PyExc_KeyError, "missing 'spmatrix_str' in 'base_options'");
+  
+  if (!(str = PyObject_GetAttrString(cvxopt, "spmatrix_str"))) {
+    Py_DECREF(cvxopt);
+    PY_ERR(PyExc_KeyError, "missing 'spmatrix_str' in 'cvxopt'");
+  }
 
+  Py_DECREF(cvxopt);
   if (!PyCallable_Check(str)) PY_ERR_TYPE("'spmatrix_str' is not callable");
 
   ret = PyObject_CallFunctionObjArgs(str, (PyObject *)self, NULL);
@@ -2463,17 +2466,27 @@ spmatrix_str(matrix *self) {
 static PyObject *
 spmatrix_repr(matrix *self) {
 
+  PyObject *cvxopt = PyImport_ImportModule("cvxopt");
   PyObject *repr, *ret;
-   
-  if (!(repr = PyObject_GetAttrString(base_mod, "spmatrix_repr")))
-    PY_ERR(PyExc_KeyError, "missing 'spmatrix_repr' in 'base_options'");
+  
+  if (!(repr = PyObject_GetAttrString(cvxopt, "spmatrix_repr"))) {
+    Py_DECREF(cvxopt);
+    PY_ERR(PyExc_KeyError, "missing 'spmatrix_repr' in 'cvxopt'");
+  }
 
+  Py_DECREF(cvxopt);
   if (!PyCallable_Check(repr)) PY_ERR_TYPE("'spmatrix_repr' is not callable");
 
   ret = PyObject_CallFunctionObjArgs(repr, (PyObject *)self, NULL);
   Py_DECREF(repr);
 
-  return ret;
+  return ret;  
+}
+
+static PyObject * 
+spmatrix_richcompare(PyObject *self, PyObject *other, int op) {
+
+  PY_ERR(PyExc_NotImplementedError, "matrix comparison not implemented");  
 }
 
 static PyObject * spmatrix_get_size(spmatrix *self, void *closure)
@@ -3106,7 +3119,7 @@ spmatrix_ass_subscr(spmatrix* self, PyObject* args, PyObject* value)
       free(SP_VAL(self)); SP_VAL(self) = val_merge;      
       free(ilist); 
 
-      realloc_ccs(self->obj, SP_NNZ(self)); //XXX
+      //realloc_ccs(self->obj, SP_NNZ(self)); 
     }
     /* ass. argument is a sparse matrix */
     else 
@@ -3201,7 +3214,7 @@ spmatrix_ass_subscr(spmatrix* self, PyObject* args, PyObject* value)
       free(SP_VAL(self)); SP_VAL(self) = val_merge;      
       free(ilist); 
 
-      realloc_ccs(self->obj, SP_NNZ(self));
+      //realloc_ccs(self->obj, SP_NNZ(self));
     }
     
     if (!Matrix_Check(args)) { Py_DECREF(Il); }
@@ -3211,6 +3224,7 @@ spmatrix_ass_subscr(spmatrix* self, PyObject* args, PyObject* value)
   }
 
   /* remainding cases are different two argument indexing */
+
   PyObject *argI = NULL, *argJ = NULL;
   if (!PyArg_ParseTuple(args, "OO", &argI, &argJ)) 
     PY_ERR_INT(PyExc_TypeError, "invalid index arguments");
@@ -3290,7 +3304,7 @@ spmatrix_ass_subscr(spmatrix* self, PyObject* args, PyObject* value)
       Js[i].value = i;
     }
     qsort(Js, lgtJ, sizeof(int_list), comp_int);
-
+    
     int_t rhs_cnti, rhs_cntj = 0, tot_cnt = 0;
     int_t rhs_i, rhs_j = Js[0].key; 
     for (j=0; j<SP_NCOLS(self); j++) {
@@ -3368,7 +3382,7 @@ spmatrix_ass_subscr(spmatrix* self, PyObject* args, PyObject* value)
     free(SP_VAL(self)); SP_VAL(self) = val_merge;      
     free(Is); free(Js); 
 
-    realloc_ccs(self->obj, SP_NNZ(self));    
+    //realloc_ccs(self->obj, SP_NNZ(self));    
 
   }
   /* ass. argument is a sparse matrix */
@@ -3399,6 +3413,7 @@ spmatrix_ass_subscr(spmatrix* self, PyObject* args, PyObject* value)
     int_t rhs_cnti, rhs_cntj = -1, tot_cnt = 0, rhs_offs_rptr = 0;
     int_t rhs_i, rhs_j = -1;     
     for (j=0; j<SP_NCOLS(self); j++) {
+
       if (rhs_j < j && rhs_cntj++ < lgtJ-1) {	
 	rhs_j = Js[rhs_cntj].key;
 	rhs_offs_rptr = SP_COL(value)[Js[rhs_cntj].value];
@@ -3412,7 +3427,6 @@ spmatrix_ass_subscr(spmatrix* self, PyObject* args, PyObject* value)
 	  Is[SP_ROW(value)[i]].value = i-rhs_offs_rptr;
 	
 	qsort(Is, lgtI, sizeof(int_list), comp_int);
-
       }
       
       rhs_cnti = 0; rhs_i = Is[0].key;
@@ -3478,12 +3492,13 @@ spmatrix_ass_subscr(spmatrix* self, PyObject* args, PyObject* value)
     free(SP_VAL(self)); SP_VAL(self) = val_merge;      
     free(Is); free(Js); 
 
-    realloc_ccs(self->obj, SP_NNZ(self));    
+    //realloc_ccs(self->obj, SP_NNZ(self));    
   }
+
   if (!Matrix_Check(argI)) { Py_DECREF(Il); }
   if (!Matrix_Check(argJ)) { Py_DECREF(Jl); }
   if (decref_val) { Py_DECREF(value); }
-  
+
   return 0;
   
 }
@@ -3579,6 +3594,7 @@ spmatrix_add_helper(PyObject *self, PyObject *other, int add)
     if (((ccs *)y)->id != id) free_ccs(y);
     spmatrix *ret = SpMatrix_New(SP_NROWS(other), SP_NCOLS(other), 0, id);
     if (!ret) return PyErr_NoMemory();
+    free_ccs(ret->obj);
     ret->obj = z;
     return (PyObject *)ret;
   } 
@@ -3719,8 +3735,11 @@ spmatrix_mul(PyObject *self, PyObject *other)
   }
   
   int id = MAX(get_id(self, PY_NUMBER(self)),get_id(other, PY_NUMBER(other)));
-  if (PY_NUMBER(self) || (Matrix_Check(self) && MAT_LGT(self) == 1) || 
-      PY_NUMBER(other) || (Matrix_Check(other) && MAT_LGT(other) == 1)) {
+  if (PY_NUMBER(self) || (Matrix_Check(self) && MAT_LGT(self) == 1 &&
+	  !(SpMatrix_Check(other) && SP_NCOLS(other) == 1)) || 
+      PY_NUMBER(other) || (Matrix_Check(other) && MAT_LGT(other) == 1 &&
+	  !(SpMatrix_Check(self) && SP_NROWS(self) == 1)) )
+  {
     
     spmatrix *ret = SpMatrix_NewFromSpMatrix((spmatrix *)
 	(SpMatrix_Check(self) ? self : other), id);
@@ -3846,6 +3865,17 @@ static PyObject * spmatrix_idiv(PyObject *self,PyObject *other) {
   return spmatrix_div_generic((spmatrix *)self, other, 1);
 }
 
+static int spmatrix_nonzero(matrix *self)
+{
+  int i, res = 0;
+  for (i=0; i<SP_NNZ(self); i++) {
+    if ((SP_ID(self) == DOUBLE) && (SP_VALD(self)[i] != 0.0)) res = 1;
+    else if ((SP_ID(self) == COMPLEX) && (SP_VALZ(self)[i] != 0.0)) res = 1;
+  }
+  
+  return res;
+}
+
 
 static PyNumberMethods spmatrix_as_number = {
         (binaryfunc)spmatrix_add, /*nb_add*/
@@ -3858,7 +3888,7 @@ static PyNumberMethods spmatrix_as_number = {
 	(unaryfunc)spmatrix_neg,/*nb_negative*/
 	(unaryfunc)spmatrix_pos,/*nb_positive*/
 	(unaryfunc)spmatrix_abs,/*nb_absolute*/
-	0,	                /*nb_nonzero*/
+	(inquiry)spmatrix_nonzero,/*nb_nonzero*/
 	0,	                /*nb_invert*/
 	0,	                /*nb_lshift*/
 	0,	                /*nb_rshift*/
@@ -4012,7 +4042,7 @@ PyTypeObject spmatrix_tp = {
 	0,					/* tp_doc */
 	0,					/* tp_traverse */
 	0,					/* tp_clear */
-	0,	      		                /* tp_richcompare */
+	(richcmpfunc)spmatrix_richcompare,      /* tp_richcompare */
 	0,					/* tp_weaklistoffset */
 	(getiterfunc)spmatrix_iter,		/* tp_iter */
 	0,	       	                        /* tp_iternext */
diff --git a/src/C/umfpack.c b/src/C/umfpack.c
index ccea2bc..1cd145d 100644
--- a/src/C/umfpack.c
+++ b/src/C/umfpack.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2004-2008 J. Dahl and L. Vandenberghe.
  *
- * This file is part of CVXOPT version 1.0.
+ * This file is part of CVXOPT version 1.1.
  *
  * CVXOPT is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/python/__init__.py b/src/python/__init__.py
index 48f8e8a..775501d 100644
--- a/src/python/__init__.py
+++ b/src/python/__init__.py
@@ -1,12 +1,33 @@
-__all__ = ['base', 'blas', 'lapack', 'amd', 'umfpack', 'cholmod', 'solvers',
-    'modeling', 'printing', 'info']
-
-
-import base, printing
-base.matrix_str    = printing.matrix_str_default
-base.matrix_repr   = printing.matrix_repr_default
-base.spmatrix_str  = printing.spmatrix_str_default
-base.spmatrix_repr = printing.spmatrix_repr_default
+"""
+Python package for convex optimization 
+
+CVXOPT is a free software package for convex optimization based on the 
+Python programming language. It can be used with the interactive Python 
+interpreter, on the command line by executing Python scripts, or 
+integrated in other software via Python extension modules. Its main 
+purpose is to make the development of software for convex optimization 
+applications straightforward by building on Python's extensive standard 
+library and on the strengths of Python as a high-level programming 
+language.
+""" 
+
+# Copyright 2004-2008 J. Dahl and L. Vandenberghe.
+# 
+# This file is part of CVXOPT version 1.1.
+#
+# CVXOPT 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.
+#
+# CVXOPT 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
+
+import base
 
 def normal(nrows, ncols=1, mean=0.0, std=1.0):
     '''
@@ -103,7 +124,126 @@ def getseed():
         raise NotImplementedError, "getseed() not installed (requires GSL) "
     
 
-base.normal = normal
-base.uniform = uniform
-base.setseed = setseed
-base.getseed = getseed
+
+import __builtin__
+omax = __builtin__.max
+omin = __builtin__.min
+
+def max(*args):
+    ''' 
+    Elementwise max for matrices.
+
+    PURPOSE
+    max(a1, ..., an) computes the elementwise max for matrices.  The arguments
+    must be matrices of compatible dimensions,  and scalars.  The elementwise
+    max of a matrix and a scalar is a new matrix where each element is 
+    defined as max of a matrix element and the scalar.
+
+    max(iterable)  where the iterator generates matrices and scalars computes
+    the elementwise max between the objects in the iterator,  using the
+    same conventions as max(a1, ..., an).
+    '''    
+    
+    if len(args) == 1 and type(args[0]).__name__ in \
+            ['list', 'tuple', 'xrange', 'generator']: 
+        return +reduce(base.emax, *args)
+    elif len(args) == 1 and type(args[0]) is base.matrix:
+        return omax(args[0])
+    elif len(args) == 1 and type(args[0]) is base.spmatrix:
+        if len(args[0]) == mul(args[0].size):
+            return omax(args[0])
+        else:
+            return omax(omax(args[0]), 0.0)
+    else:
+        return +reduce(base.emax, args)
+
+
+def min(*args):
+    ''' 
+    Elementwise min for matrices.
+
+    PURPOSE
+    min(a1, ..., an) computes the elementwise min for matrices.  The arguments
+    must be matrices of compatible dimensions,  and scalars.  The elementwise
+    min of a matrix and a scalar is a new matrix where each element is 
+    defined as min of a matrix element and the scalar.
+
+    min(iterable)  where the iterator generates matrices and scalars computes
+    the elementwise min between the objects in the iterator,  using the
+    same conventions as min(a1, ..., an).
+    '''
+
+    if len(args) == 1 and type(args[0]).__name__ in \
+            ['list', 'tuple', 'xrange', 'generator']: 
+        return +reduce(base.emin, *args)
+    elif len(args) == 1 and type(args[0]) is base.matrix:
+        return omin(args[0])
+    elif len(args) == 1 and type(args[0]) is base.spmatrix:
+        if len(args[0]) == mul(args[0].size):
+            return omin(args[0])
+        else:
+            return omin(omin(args[0]), 0.0)
+    else:
+        return +reduce(base.emin, args)
+
+def mul(*args):
+    ''' 
+    Elementwise multiplication for matrices.
+
+    PURPOSE
+    mul(a1, ..., an) computes the elementwise multiplication for matrices.
+    The arguments must be matrices of compatible dimensions,  and scalars.  
+    The elementwise multiplication of a matrix and a scalar is a new matrix 
+    where each element is 
+    defined as the multiplication of a matrix element and the scalar.
+
+    mul(iterable)  where the iterator generates matrices and scalars computes
+    the elementwise multiplication between the objects in the iterator,  
+    using the same conventions as mul(a1, ..., an).
+    '''
+
+    if len(args) == 1 and type(args[0]).__name__ in \
+            ['list', 'tuple', 'xrange', 'generator']: 
+        return +reduce(base.emul, *args)
+    else:
+        return +reduce(base.emul, args)
+
+def div(*args):
+    ''' 
+    Elementwise division for matrices.
+
+    PURPOSE
+    div(a1, ..., an) computes the elementwise division for matrices.
+    The arguments must be matrices of compatible dimensions,  and scalars.  
+    The elementwise division of a matrix and a scalar is a new matrix 
+    where each element is defined as the division between a matrix element 
+    and the scalar.  
+
+    div(iterable)  where the iterator generates matrices and scalars computes
+    the elementwise division between the objects in the iterator,  
+    using the same conventions as div(a1, ..., an).
+    '''
+
+    if len(args) == 1 and type(args[0]).__name__ in \
+            ['list', 'tuple', 'xrange', 'generator']: 
+        return +reduce(base.ediv, *args)
+    else:
+        return +reduce(base.ediv, args)
+
+base.normal, base.uniform = normal, uniform
+base.setseed, base.getseed = setseed, getseed
+base.mul, base.div = mul, div
+
+from cvxopt import printing
+matrix_str    = printing.matrix_str_default
+matrix_repr   = printing.matrix_repr_default
+spmatrix_str  = printing.spmatrix_str_default
+spmatrix_repr = printing.spmatrix_repr_default
+
+from cvxopt.base import matrix, spmatrix, sparse, spdiag, sqrt, sin, cos, \
+    exp, log 
+
+__all__ = [ 'blas', 'lapack', 'amd', 'umfpack', 'cholmod', 'solvers',
+    'modeling', 'printing', 'info', 'matrix', 'spmatrix', 'sparse', 
+    'spdiag', 'sqrt', 'sin', 'cos', 'exp', 'log', 'min', 'max', 'mul', 
+    'div', 'normal', 'uniform', 'setseed', 'getseed' ]
diff --git a/src/python/coneprog.py b/src/python/coneprog.py
index 575f2ef..90c7ab4 100644
--- a/src/python/coneprog.py
+++ b/src/python/coneprog.py
@@ -4,7 +4,7 @@ Solver for linear, second-order cone and semidefinite programming.
 
 # Copyright 2004-2008 J. Dahl and L. Vandenberghe.
 # 
-# This file is part of CVXOPT version 1.0.
+# This file is part of CVXOPT version 1.1.
 #
 # CVXOPT is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -41,7 +41,6 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
         subject to  G'*z + A'*y + c = 0
                     z >= 0.
 
-
     The inequalities are with respect to a cone C defined as the Cartesian
     product of N + M + 1 cones:
     
@@ -122,23 +121,163 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
 
     Output arguments.
 
-        Returns a dictionary with keys 'status', 'x', 's', 'z', 'y'.
+        Returns a dictionary with keys 'status', 'x', 's', 'z', 'y',
+        'primal objective', 'dual objective', 'gap', 'relative gap',  
+        'primal infeasibility', 'dual infeasibility', 'primal slack',
+        'dual slack', 'residual as primal infeasibility certificate', 
+        'residual as dual infeasibility certificate'.
+
+        The 'status' field has values 'optimal', 'primal infeasible',
+        'dual infeasible', or 'unknown'.  The values of the other fields
+        depend on the exit status.
+
+        Status 'optimal'. 
+        - 'x', 's', 'y', 'z' are an approximate solution of the primal and
+          dual optimality conditions   
+
+              G*x + s = h,  A*x = b  
+              G'*z + A'*y + c = 0 
+              s >= 0, z >= 0
+              s'*z = 0.
+
+        - 'primal objective': the primal objective c'*x.
+        - 'dual objective': the dual objective -h'*z - b'*y.
+        - 'gap': the duality gap s'*z.  
+        - 'relative gap': the relative gap, defined as s'*z / -c'*x if 
+          the primal objective is negative, s'*z / -(h'*z + b'*y) if the 
+          dual objective is positive, and None otherwise.
+        - 'primal infeasibility': the residual in the primal constraints,
+          defined as the maximum of the residual in the inequalities 
+
+              || G*x + s + h || / max(1, ||h||) 
+
+          and the residual in the equalities 
+
+              || A*x - b || / max(1, ||b||).
+
+        - 'dual infeasibility': the residual in the dual constraints,
+          defined as 
+
+              || G'*z + A'*y + c || / max(1, ||c||).
+
+        - 'primal slack': the smallest primal slack, sup {t | s >= t*e }, 
+           where 
+
+              e = ( e_0, e_1, ..., e_N, e_{N+1}, ..., e_{M+N} )
+
+          is the identity vector in C.  e_0 is an ml-vector of ones, 
+          e_k, k = 1,..., N, is the unit vector (1,0,...,0) of length
+          mq[k], and e_k = vec(I) where I is the identity matrix of order
+          ms[k].
+        - 'dual slack': the smallest dual slack, sup {t | z >= t*e }.
+        - 'residual as primal infeasibility certificate': None.
+        - 'residual as dual infeasibility certificate': None.
+        The primal infeasibility is guaranteed to be less than 
+        solvers.options['feastol'] (default 1e-7).  The dual infeasibility
+        is guaranteed to be less than solvers.options['feastol'] 
+        (default 1e-7).  The gap is less than solvers.options['abstol'] 
+        (default 1e-7) or the relative gap is less than 
+        solvers.options['reltol'] (default 1e-6).
+      
+        Status 'primal infeasible'. 
+        - 'x', 's': None.
+        - 'y', 'z' are an approximate certificate of infeasibility  
+
+              -h'*z - b'*y = 1,  G'*z + A'*y = 0,  z >= 0.
+
+        - 'primal objective': None.
+        - 'dual objective': 1.0.
+        - 'gap', 'relative gap': None.
+        - 'primal infeasibility' and 'dual infeasibility': None.
+        - 'primal slack': None.
+        - 'dual slack': the smallest dual slack, sup {t | z >= t*e }.
+        - 'residual as primal infeasibility certificate': the residual in 
+          the condition of the infeasibility certificate, defined as 
+
+              || G'*z + A'*y || / max(1, ||c||).
+
+        - 'residual as dual infeasibility certificate': None.
+        The residual as primal infeasiblity certificate is guaranteed 
+        to be less than solvers.options['feastol'] (default 1e-7).  
+
+        Status 'dual infeasible'. 
+        - 'x', 's' are an approximate proof of dual infeasibility 
+
+              c'*x = -1,  G*x + s = 0,  A*x = 0,  s >= 0.
+
+        - 'y', 'z': None.
+        - 'primal objective': -1.0.
+        - 'dual objective': None.
+        - 'gap', 'relative gap': None.
+        - 'primal infeasibility' and 'dual infeasibility': None.
+        - 'primal slack': the smallest primal slack, sup {t | s >= t*e}.
+        - 'dual slack': None.
+        - 'residual as primal infeasibility certificate': None. 
+        - 'residual as dual infeasibility certificate: the residual in 
+          the conditions of the infeasibility certificate, defined as 
+          the maximum of 
+
+              || G*x + s || / max(1, ||h||) and || A*x || / max(1, ||b||).
+
+        The residual as dual infeasiblity certificate is guaranteed 
+        to be less than solvers.options['feastol'] (default 1e-7).  
+
+        Status 'unknown'. 
+        - 'x', 'y', 's', 'z' are the last iterates before termination.   
+          These satisfy s > 0 and z > 0, but are not necessarily feasible.
+        - 'primal objective': the primal cost c'*x.
+        - 'dual objective': the dual cost -h'*z - b'*y.
+        - 'gap': the duality gap s'*z. 
+        - 'relative gap': the relative gap, defined as s'*z / -c'*x if the 
+          primal cost is negative, s'*z / -(h'*z + b'*y) if the dual cost 
+          is positive, and None otherwise.
+        - 'primal infeasibility ': the residual in the primal constraints,
+          defined as the maximum of the residual in the inequalities 
+
+              || G*x + s + h || / max(1, ||h||) 
+
+          and the residual in the equalities 
 
-        If status is 'optimal', x, s, y, z are approximate primal and 
-        dual optimal solutions.
+              || A*x - b || / max(1, ||b||).
 
-        If status is 'primal infeasible', x = s = None, and z, y are an
-        approximate proof of infeasibility: 
+        - 'dual infeasibility': the residual in the dual constraints,
+          defined as 
 
-            h'*z + b'*y = -1,  G'*z + A'*y = 0,  z >= 0.
+              || G'*z + A'*y + c || / max(1, ||c||).
 
-        If status is 'dual infeasible', z = y = None, and x, s are an
-        approximate proof of dual infeasibility: 
+        - 'primal slack': the smallest primal slack, sup {t | s >= t*e}.
+        - 'dual slack': the smallest dual slack, sup {t | z >= t*e}.
+        - 'residual as primal infeasibility certificate': None if 
+           h'*z + b'*y >= 0; the residual 
 
-            c'*x = -1,  G*x + s = 0,  A*x = 0,  s >= 0.
+              || G'*z + A'*y || / ( -(h'*z + b'*y) * max(1, ||c||) )  
 
-        If status is 'unknown', x, y, s, z are None.
+          otherwise.
+        - 'residual as dual infeasibility certificate': 
+          None if c'*x >= 0; the maximum of the residuals 
 
+              || G*x + s || / ( -c'*x * max(1, ||h||) )
+
+          and  
+
+              || A*x || / ( -c'*x * max(1, ||b||) )  
+
+          otherwise.
+        Termination with status 'unknown' indicates that the algorithm 
+        failed to find a solution that satisfies the specified tolerances.
+        In some cases, the returned solution may be fairly accurate.  If 
+        the primal and dual infeasibilities, the gap, and the relative gap 
+        are small, then x, y, s, z are close to optimal.  If the residual 
+        as primal infeasibility certificate is small, then 
+
+            y / (-h'*z - b'*y),   z / (-h'*z - b'*y) 
+
+        provide an approximate certificate of primal infeasibility.  If 
+        the residual as certificate of dual infeasibility is small, then 
+
+            x / (-c'*x),   s / (-c'*x) 
+
+        provide an approximate proof of dual infeasibility.
 
 
     Advanced usage.
@@ -241,16 +380,16 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
         - xdot(u, v) returns the inner product of two vectors u and v in X.
         - xscal(alpha, u) computes u := alpha*u, where alpha is a scalar 
           and u is a vector in X.
-        - xaxpy(u, v, alpha = 1.0, beta = 0.0) computes v := alpha*u + v 
-          for a scalar alpha and two vectors u and v in X.
+        - xaxpy(u, v, alpha = 1.0) computes v := alpha*u + v for a scalar 
+          alpha and two vectors u and v in X.
 
         If Y is the vector space of primal variables y:
         - ynewcopy(u) creates a new copy of the vector u in Y.
         - ydot(u, v) returns the inner product of two vectors u and v in Y.
         - yscal(alpha, u) computes u := alpha*u, where alpha is a scalar 
           and u is a vector in Y.
-        - yaxpy(u, v, alpha = 1.0, beta = 0.0) computes v := alpha*u + v 
-          for a scalar alpha and two vectors u and v in Y.
+        - yaxpy(u, v, alpha = 1.0) computes v := alpha*u + v for a scalar 
+          alpha and two vectors u and v in Y.
         If this option is used, the argument b must be in the same format
         as y, the argument A must be a Python function, and the argument 
         kktsolver is required.
@@ -258,17 +397,17 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
 
     Control parameters.
 
-       The following control parameters can be modified by adding an 
-       entry to the dictionary options.  
+        The following control parameters can be modified by adding an 
+        entry to the dictionary options.  
 
-       options['show_progress'].  True/False (default: True).
-       options['maxiters'].  Positive integer (default: 100).
-       options['refinement'].  Positive integer (default: 0 for problems
-           with no second-order cone and matrix inequality constraints; 
-           1 otherwise).
-       options['abstol'].  Scalar (default: 1e-7).
-       options['reltol'].  Scalar (default: 1e-6).
-       options['feastol'].  Scalar (default: 1e-7).
+        options['show_progress'] True/False (default: True)
+        options['maxiters'] positive integer (default: 100)
+        options['refinement'] positive integer (default: 0 for problems
+            with no second-order cone and matrix inequality constraints; 
+            1 otherwise)
+        options['abstol'] scalar (default: 1e-7 )
+        options['reltol'] scalar (default: 1e-6)
+        options['feastol'] scalar (default: 1e-7).
 
     """
     import math
@@ -300,16 +439,21 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
         if type(RELTOL) is not float and type(RELTOL) is not int:
             raise ValueError, "options['reltol'] must be a scalar"
 
+    if RELTOL <= 0.0 and ABSTOL <= 0.0 :
+        raise ValueError, "at least one of options['reltol'] and " \
+            "options['abstol'] must be positive"
+
     try: FEASTOL = options['feastol']
     except KeyError: FEASTOL = 1e-7
     else:
-        if type(FEASTOL) is not float and type(FEASTOL) is not int:
-            raise ValueError, "options['feastol'] must be a scalar"
+        if (type(FEASTOL) is not float and type(FEASTOL) is not int) or \
+            FEASTOL <= 0.0:
+            raise ValueError, "options['feastol'] must be a positive "\
+                "scalar"
 
     try: show_progress = options['show_progress']
     except KeyError: show_progress = True
 
-
     if kktsolver is None: 
         if dims and (dims['q'] or dims['s']):  
             kktsolver = 'qr'            
@@ -333,7 +477,7 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
         raise ValueError, "use of non-vector type for x requires "\
             "function valued G, A and user-provided kktsolver"
     customy = (ynewcopy != None or ydot != None or yaxpy != None or 
-        yscal != None)  
+        yscal != None)
     if customy and (not operatorA or not customkkt):
         raise ValueError, "use of non-vector type for y requires "\
             "function valued A and user-provided kktsolver"
@@ -502,6 +646,9 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
         yscal(0.0, y) 
         yaxpy(x, y)
 
+    resx0 = max(1.0, math.sqrt(xdot(c,c)))
+    resy0 = max(1.0, math.sqrt(ydot(b,b)))
+    resz0 = max(1.0, misc.snrm2(h, dims))
 
     # Select initial points.
 
@@ -605,19 +752,61 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
         gap = misc.sdot(s, z, dims) 
         pcost = xdot(c,x)
         dcost = -ydot(b,y) - misc.sdot(h, z, dims) 
+        if pcost < 0.0:
+            relgap = gap / -pcost
+        elif dcost > 0.0:
+            relgap = gap / dcost
+        else: 
+            relgap = None
 
-        if ts <= 0 and tz <= 0 and (gap <= ABSTOL or ( pcost < 0.0 and 
-            gap / -pcost <= RELTOL ) or (dcost > 0.0 and gap / dcost 
-            <= RELTOL)):
+        if ts <= 0 and tz <= 0 and (gap <= ABSTOL or ( relgap is not None
+            and relgap <= RELTOL )):
 
             # The initial points we constructed happen to be feasible and 
             # optimal.  
+
             ind = dims['l'] + sum(dims['q'])
             for m in dims['s']:
                 misc.symm(s, m, ind)
                 misc.symm(z, m, ind)
                 ind += m**2
-            return {'status': 'optimal', 'x': x, 'y': y, 's': s, 'z': z}
+
+            # rx = A'*y + G'*z + c
+            rx = xnewcopy(c)
+            Af(y, rx, beta = 1.0, trans = 'T') 
+            Gf(z, rx, beta = 1.0, trans = 'T') 
+            resx = math.sqrt( xdot(rx, rx) ) 
+
+            # ry = b - A*x 
+            ry = ynewcopy(b)
+            Af(x, ry, alpha = -1.0, beta = 1.0)
+            resy = math.sqrt( ydot(ry, ry) ) 
+
+            # rz = s + G*x - h 
+            rz = matrix(0.0, (cdim,1))
+            Gf(x, rz)
+            blas.axpy(s, rz)
+            blas.axpy(h, rz, alpha = -1.0)
+            resz = misc.snrm2(rz, dims) 
+
+            pres = max(resy/resy0, resz/resz0)
+            dres = resx/resx0
+            cx, by, hz = xdot(c,x), ydot(b,y), misc.sdot(h, z, dims) 
+
+            if show_progress:
+                print "Optimal solution found."
+            return { 'x': x, 'y': y, 's': s, 'z': z,
+                'status': 'optimal', 
+                'gap': gap, 
+                'relative gap': relgap, 
+                'primal objective': cx,
+                'dual objective': -(by + hz),
+                'primal infeasibility': pres,
+                'primal slack': -ts,
+                'dual slack': -tz,
+                'dual infeasibility': dres,
+                'residual as primal infeasibility certificate': None,
+                'residual as dual infeasibility certificate': None } 
 
         if ts >= -1e-8 * max(nrms, 1.0):  
             a = 1.0 + ts  
@@ -670,12 +859,9 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
     lmbda = matrix(0.0, (cdim_diag + 1, 1))
     lmbdasq = matrix(0.0, (cdim_diag + 1, 1)) 
 
-    resx0 = max(1.0, math.sqrt(xdot(c,c)))
-    resy0 = max(1.0, math.sqrt(ydot(b,b)))
-    resz0 = max(1.0, misc.snrm2(h, dims))
     gap = misc.sdot(s, z, dims) 
 
-    for iters in xrange(MAXITERS):
+    for iters in xrange(MAXITERS+1):
 
         # hrx = -A'*y - G'*z 
         Af(y, hrx, alpha = -1.0, trans = 'T') 
@@ -714,8 +900,8 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
         cx, by, hz = xdot(c,x), ydot(b,y), misc.sdot(h, z, dims) 
         rt = kappa + cx + by + hz 
 
-        # stopping criteria
-        pcost, dcost = cx/tau, -(by + hz) / tau        
+        # Statistics for stopping criteria.
+        pcost, dcost = cx / tau, -(by + hz) / tau        
         if pcost < 0.0:
             relgap = gap / -pcost
         elif dcost > 0.0:
@@ -724,16 +910,26 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
             relgap = None
         pres = max(resy/resy0, resz/resz0)
         dres = resx/resx0
+        if hz + by < 0.0:  
+           pinfres =  hresx / resx0 / (-hz - by) 
+        else:
+           pinfres =  None
+        if cx < 0.0: 
+           dinfres = max(hresy / resy0, hresz/resz0) / (-cx) 
+        else:
+           dinfres = None
 
         if show_progress:
-            if iters==0:
+            if iters == 0:
                 print "% 10s% 12s% 10s% 8s% 7s % 5s" %("pcost", "dcost",
                     "gap", "pres", "dres", "k/t")
             print "%2d: % 8.4e % 8.4e % 4.0e% 7.0e% 7.0e% 7.0e" \
                 %(iters, pcost, dcost, gap, pres, dres, kappa/tau)
 
-        if pres <= FEASTOL and dres <= FEASTOL and ( gap <= ABSTOL or 
-            (relgap is not None and relgap <= RELTOL) ):
+
+        if ( pres <= FEASTOL and dres <= FEASTOL and ( gap <= ABSTOL or 
+            (relgap is not None and relgap <= RELTOL) ) ) or \
+            iters == MAXITERS:
             xscal(1.0/tau, x)
             yscal(1.0/tau, y)
             blas.scal(1.0/tau, s)
@@ -743,27 +939,89 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
                 misc.symm(s, m, ind)
                 misc.symm(z, m, ind)
                 ind += m**2
-            return {'status': 'optimal', 'x': x, 'y': y, 's': s, 'z': z}
+            ts = misc.max_step(s, dims)
+            tz = misc.max_step(z, dims)
+            if iters == MAXITERS:
+                if show_progress:
+                    print "Terminated (maximum number of iterations "\
+                        "reached)."
+                return { 'x': x, 'y': y, 's': s, 'z': z,
+                    'status': 'unknown', 
+                    'gap': gap, 
+                    'relative gap': relgap, 
+                    'primal objective': pcost,
+                    'dual objective' : dcost,
+                    'primal infeasibility': pres,
+                    'dual infeasibility': dres,
+                    'primal slack': -ts,
+                    'dual slack': -tz,
+                    'residual as primal infeasibility certificate': 
+                        pinfres,
+                    'residual as dual infeasibility certificate': 
+                        dinfres}
 
-        elif hz + by < 0.0 and hresx/resx0 / (-hz - by) <= FEASTOL:
+            else:
+                if show_progress:
+                    print "Optimal solution found."
+                return { 'x': x, 'y': y, 's': s, 'z': z,
+                    'status': 'optimal', 
+                    'gap': gap, 
+                    'relative gap': relgap, 
+                    'primal objective': pcost,
+                    'dual objective' : dcost,
+                    'primal infeasibility': pres,
+                    'dual infeasibility': dres,
+                    'primal slack': -ts,
+                    'dual slack': -tz,
+                    'residual as primal infeasibility certificate': None,
+                    'residual as dual infeasibility certificate': None }
+
+        elif pinfres is not None and pinfres <= FEASTOL:
             yscal(1.0/(-hz - by), y)
             blas.scal(1.0/(-hz - by), z)
             ind = dims['l'] + sum(dims['q'])
             for m in dims['s']:
                 misc.symm(z, m, ind)
                 ind += m**2
-            return {'status': 'primal infeasible', 'x': None, 's': None, 
-                'y': y, 'z': z }
-
-        elif cx < 0.0 and max(hresy/resy0, hresz/resz0) / (-cx) <= FEASTOL:
+            tz = misc.max_step(z, dims)
+            if show_progress:
+                print "Certificate of primal infeasibility found."
+            return { 'x': None, 'y': y, 's': None, 'z': z,
+                'status': 'primal infeasible',
+                'gap': None, 
+                'relative gap': None, 
+                'primal objective': None,
+                'dual objective' : 1.0,
+                'primal infeasibility': None,
+                'dual infeasibility': None,
+                'primal slack': None,
+                'dual slack': -tz,
+                'residual as primal infeasibility certificate': pinfres,
+                'residual as dual infeasibility certificate': None }
+
+        elif dinfres is not None and dinfres <= FEASTOL:
             xscal(1.0/(-cx), x)
             blas.scal(1.0/(-cx), s)
             ind = dims['l'] + sum(dims['q'])
             for m in dims['s']:
                 misc.symm(s, m, ind)
                 ind += m**2
-            return {'status': 'dual infeasible', 'x': x, 's': s, 'y': None,
-                'z': None}
+            y, z = None, None
+            ts = misc.max_step(s, dims)
+            if show_progress:
+                print "Certificate of dual infeasibility found."
+            return {'x': x, 'y': None, 's': s, 'z': None,
+                'status': 'dual infeasible',
+                'gap': None, 
+                'relative gap': None, 
+                'primal objective': -1.0,
+                'dual objective' : None,
+                'primal infeasibility': None,
+                'dual infeasibility': None,
+                'primal slack': -ts,
+                'dual slack': None,
+                'residual as primal infeasibility certificate': None,
+                'residual as dual infeasibility certificate': dinfres }
 
 
         # Compute initial scaling W:
@@ -798,36 +1056,57 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
         #
         # On entry, x, y, z contain bx, by, bz.
         # On exit, they contain ux, uy, uz.
-
-        try: f3 = kktsolver(W)
-        except ArithmeticError:
-            if iters == 0 and primalstart and dualstart: 
-                raise ValueError, "Rank(A) < p or Rank([G; A]) < n"
-            else:
-                raise ArithmeticError, "singular KKT matrix"
-
-
-        # Solve
+        #
+        # Also solve
         #
         #     [ 0   A'  G'    ] [ x1        ]          [ c ]
         #     [-A   0   0     ]*[ y1        ] = -dgi * [ b ].
         #     [-G   0   W'*W  ] [ W^{-1}*z1 ]          [ h ]
          
-        if iters == 0:
-            x1, y1 = xnewcopy(c), ynewcopy(b)
-            z1 = matrix(0.0, (cdim,1))
-        xcopy(c, x1);  xscal(-1, x1)
-        ycopy(b, y1)
-        blas.copy(h, z1)
-        try: f3(x1, y1, z1)
+
+        try: 
+            f3 = kktsolver(W)
+            if iters == 0:
+                x1, y1 = xnewcopy(c), ynewcopy(b)
+                z1 = matrix(0.0, (cdim,1))
+            xcopy(c, x1);  xscal(-1, x1)
+            ycopy(b, y1)
+            blas.copy(h, z1)
+            f3(x1, y1, z1)
+            xscal(dgi, x1)
+            yscal(dgi, y1)
+            blas.scal(dgi, z1)
         except ArithmeticError:
             if iters == 0 and primalstart and dualstart: 
                 raise ValueError, "Rank(A) < p or Rank([G; A]) < n"
             else:
-                raise ArithmeticError, "singular KKT matrix"
-        xscal(dgi, x1)
-        yscal(dgi, y1)
-        blas.scal(dgi, z1)
+                xscal(1.0/tau, x)
+                yscal(1.0/tau, y)
+                blas.scal(1.0/tau, s)
+                blas.scal(1.0/tau, z)
+                ind = dims['l'] + sum(dims['q'])
+                for m in dims['s']:
+                    misc.symm(s, m, ind)
+                    misc.symm(z, m, ind)
+                    ind += m**2
+                ts = misc.max_step(s, dims)
+                tz = misc.max_step(z, dims)
+                if show_progress:
+                    print "Terminated (singular KKT matrix)."
+                return { 'x': x, 'y': y, 's': s, 'z': z,
+                    'status': 'unknown', 
+                    'gap': gap, 
+                    'relative gap': relgap, 
+                    'primal objective': pcost,
+                    'dual objective' : dcost,
+                    'primal infeasibility': pres,
+                    'dual infeasibility': dres,
+                    'primal slack': -ts,
+                    'dual slack': -tz,
+                    'residual as primal infeasibility certificate': 
+                        pinfres,
+                    'residual as dual infeasibility certificate': 
+                        dinfres }
 
 
         # f6_no_ir(x, y, z, tau, s, kappa) solves
@@ -1155,12 +1434,9 @@ def conelp(c, G, h, dims = None, A = None, b = None, primalstart = None,
         misc.scale(z, W, inverse = 'I')
 
         kappa, tau = lmbda[-1]/dgi, lmbda[-1]*dgi
-        gap = blas.dot(lmbda, lmbda, n = lmbda.size[0]-1) / tau**2
+        gap = ( blas.nrm2(lmbda, n = lmbda.size[0]-1) / tau )**2
 
 
-    return {'status': 'unknown', 'x': None, 'y': None, 's': None, 
-        'z': None}
-
 
 def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
     initvals = None, kktsolver = None, xnewcopy = None, xdot = None,
@@ -1262,12 +1538,81 @@ def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
 
     Output arguments.
 
-        Returns a dictionary with keys 'status', 'x', 's', 'y', 'z'.
+        Returns a dictionary with keys 'status', 'x', 's', 'z', 'y',
+        'primal objective', 'dual objective', 'gap', 'relative gap', 
+        'primal infeasibility', 'dual infeasibility', 'primal slack',
+        'dual slack'. 
+
+        The 'status' field has values 'optimal' or 'unknown'.  
+        If the status is 'optimal', 'x', 's', 'y', 'z' are an approximate 
+        solution of the primal and dual optimality conditions   
+
+              G*x + s = h,  A*x = b  
+              P*x + G'*z + A'*y + q = 0 
+              s >= 0, z >= 0
+              s'*z = 0.
+
+        If the status is 'unknown', 'x', 'y', 's', 'z' are the last 
+        iterates before termination.  These satisfy s > 0 and z > 0, 
+        but are not necessarily feasible.
+
+        The values of the other fields are defined as follows.
+
+        - 'primal objective': the primal objective (1/2)*x'*P*x + q'*x.
+
+        - 'dual objective': the dual objective 
+
+              L(x,y,z) = (1/2)*x'*P*x + q'*x + z'*(G*x - h) + y'*(A*x-b).
+
+        - 'gap': the duality gap s'*z.  
+
+        - 'relative gap': the relative gap, defined as 
+
+              gap / -primal objective 
+
+          if the primal objective is negative, 
+
+              gap / dual objective
+
+          if the dual objective is positive, and None otherwise.
+
+        - 'primal infeasibility': the residual in the primal constraints,
+          defined as the maximum of the residual in the inequalities 
+
+              || G*x + s + h || / max(1, ||h||) 
 
-        If status is 'optimal', x, s, y, z are approximate solutions of 
-        the primal and dual problems.
+          and the residual in the equalities 
 
-        If status is 'unknown' x, s, y, z are None.
+              || A*x - b || / max(1, ||b||).
+
+
+        - 'dual infeasibility': the residual in the dual constraints,
+          defined as 
+
+              || P*x + G'*z + A'*y + q || / max(1, ||q||).
+
+        - 'primal slack': the smallest primal slack, sup {t | s >= t*e }, 
+           where 
+
+              e = ( e_0, e_1, ..., e_N, e_{N+1}, ..., e_{M+N} )
+
+          is the identity vector in C.  e_0 is an ml-vector of ones, 
+          e_k, k = 1,..., N, is the unit vector (1,0,...,0) of length
+          mq[k], and e_k = vec(I) where I is the identity matrix of order
+          ms[k].
+        - 'dual slack': the smallest dual slack, sup {t | z >= t*e }.
+
+        If the exit status is 'optimal', then the primal and dual
+        infeasibilities are guaranteed to be less than 
+        solvers.options['feastol'] (default 1e-7).  The gap is less than 
+        solvers.options['abstol'] (default 1e-7) or the relative gap is 
+        less than solvers.options['reltol'] (default 1e-6).
+      
+        Termination with status 'unknown' indicates that the algorithm 
+        failed to find a solution that satisfies the specified tolerances.
+        In some cases, the returned solution may be fairly accurate.  If 
+        the primal and dual infeasibilities, the gap, and the relative gap
+        are small, then x, y, s, z are close to optimal.  
 
 
     Advanced usage.
@@ -1378,16 +1723,16 @@ def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
         - xdot(u, v) returns the inner product of two vectors u and v in X.
         - xscal(alpha, u) computes u := alpha*u, where alpha is a scalar
           and u is a vector in X.
-        - xaxpy(u, v, alpha = 1.0, beta = 0.0) computes v := alpha*u + v
-          for a scalar alpha and two vectors u and v in X.
+        - xaxpy(u, v, alpha = 1.0) computes v := alpha*u + v for a scalar 
+          alpha and two vectors u and v in X.
 
         If Y is the vector space of primal variables y:
         - ynewcopy(u) creates a new copy of the vector u in Y.
         - ydot(u, v) returns the inner product of two vectors u and v in Y.
         - yscal(alpha, u) computes u := alpha*u, where alpha is a scalar
           and u is a vector in Y.
-        - yaxpy(u, v, alpha = 1.0, beta = 0.0) computes v := alpha*u + v
-          for a scalar alpha and two vectors u and v in Y.
+        - yaxpy(u, v, alpha = 1.0) computes v := alpha*u + v for a scalar 
+          alpha and two vectors u and v in Y.
 
 
     Control parameters.
@@ -1438,11 +1783,17 @@ def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
         if type(RELTOL) is not float and type(RELTOL) is not int: 
             raise ValueError, "options['reltol'] must be a scalar"
 
+    if RELTOL <= 0.0 and ABSTOL <= 0.0 :
+        raise ValueError, "at least one of options['reltol'] and " \
+            "options['abstol'] must be positive"
+
     try: FEASTOL = options['feastol']
     except KeyError: FEASTOL = 1e-7
     else: 
-        if type(FEASTOL) is not float and type(FEASTOL) is not int: 
-            raise ValueError, "options['feastol'] must be a scalar"
+        if (type(FEASTOL) is not float and type(FEASTOL) is not int) or \
+            FEASTOL <= 0.0:
+            raise ValueError, "options['feastol'] must be a positive "\
+                "scalar"
 
     try: show_progress = options['show_progress']
     except KeyError: show_progress = True
@@ -1633,6 +1984,9 @@ def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
         yscal(0.0, y) 
         yaxpy(x, y)
 
+    resx0 = max(1.0, math.sqrt(xdot(q,q)))
+    resy0 = max(1.0, math.sqrt(ydot(b,b)))
+    resz0 = max(1.0, misc.snrm2(h, dims))
 
     if cdim == 0: 
 
@@ -1650,8 +2004,29 @@ def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
         xscal(-1.0, x)
         y = ynewcopy(b)
         f3(x, y, matrix(0.0, (0,1)))
-        return {'status': 'optimal', 'x': x,  'y': y, 'z': 
-            matrix(0.0, (0,1)), 's': matrix(0.0, (0,1))}
+
+        # dres = || P*x + q + A'*y || / resx0 
+        rx = xnewcopy(q)
+        fP(x, rx, beta = 1.0)
+        pcost = 0.5 * (xdot(x, rx) + xdot(x, q))
+        fA(y, rx, beta = 1.0, trans = 'T')
+        dres = math.sqrt(xdot(rx, rx)) / resx0
+
+        # pres = || A*x - b || / resy0
+        ry = ynewcopy(b)
+        fA(x, ry, alpha = 1.0, beta = -1.0)
+        pres = math.sqrt(ydot(ry, ry)) / resy0 
+
+        if pcost == 0.0: relgap = None
+        else: relgap = 0.0
+
+        return { 'status': 'optimal', 'x': x,  'y': y, 'z': 
+            matrix(0.0, (0,1)), 's': matrix(0.0, (0,1)), 
+            'gap': 0.0, 'relgap': 0.0, 
+            'primal objective': pcost,
+            'dual objective': pcost,
+            'primal slack': 0.0, 'dual slack': 0.0,
+            'primal infeasibility': pres, 'dual infeasibility': dres} 
 
 
     # Default initial points are x = 0, y = 0, s = e, z = e. 
@@ -1710,16 +2085,14 @@ def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
     sigs = matrix(0.0, (sum(dims['s']), 1))
     sigz = matrix(0.0, (sum(dims['s']), 1))
 
+
     if show_progress: 
         print "% 10s% 12s% 10s% 8s% 7s" %("pcost", "dcost", "gap", "pres",
             "dres")
 
-    resx0 = max(1.0, math.sqrt(xdot(q,q)))
-    resy0 = max(1.0, math.sqrt(ydot(b,b)))
-    resz0 = max(1.0, misc.snrm2(h, dims))
     gap = misc.sdot(s, z, dims) 
 
-    for iters in xrange(MAXITERS):  
+    for iters in xrange(MAXITERS + 1):
 
         # f0 = (1/2)*x'*P*x + q'*x + r and  rx = P*x + q + A'*y + G'*z.
         xcopy(q, rx)
@@ -1740,12 +2113,21 @@ def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
         fG(x, rz, beta = 1.0)
         resz = misc.snrm2(rz, dims)
 
+
+        # Statistics for stopping criteria.
+
         # pcost = (1/2)*x'*P*x + q'*x 
         # dcost = (1/2)*x'*P*x + q'*x + y'*(A*x-b) + z'*(G*x-h)
         #       = (1/2)*x'*P*x + q'*x + y'*(A*x-b) + z'*(G*x-h+s) - z'*s
         #       = (1/2)*x'*P*x + q'*x + y'*ry + z'*rz - gap
         pcost = f0
         dcost = f0 + ydot(y, ry) + misc.sdot(z, rz, dims) - gap
+        if pcost < 0.0:
+            relgap = gap / -pcost
+        elif dcost > 0.0:
+            relgap = gap / dcost 
+        else:
+            relgap = None
         pres = max(resy/resy0, resz/resz0)
         dres = resx/resx0 
 
@@ -1753,17 +2135,32 @@ def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
             print "%2d: % 8.4e % 8.4e % 4.0e% 7.0e% 7.0e" \
                 %(iters, pcost, dcost, gap, pres, dres) 
 
-        # Stopping criteria.    
-        if pres <= FEASTOL and dres <= FEASTOL and ( gap <= ABSTOL or 
-            (dcost > 0 and gap/dcost <= RELTOL) or (pcost < 0 and 
-            -gap/pcost <= RELTOL) ):
+        if ( pres <= FEASTOL and dres <= FEASTOL and ( gap <= ABSTOL or 
+            (relgap is not None and relgap <= RELTOL) )) or \
+            iters == MAXITERS:
             ind = dims['l'] + sum(dims['q'])
             for m in dims['s']:
                 misc.symm(s, m, ind)
                 misc.symm(z, m, ind)
                 ind += m**2
-            return {'status': 'optimal', 'x': x,  'y': y, 'z': z, 's': s}
-
+            ts = misc.max_step(s, dims)
+            tz = misc.max_step(z, dims)
+            if iters == MAXITERS:
+                if show_progress:
+                    print "Terminated (maximum number of iterations "\
+                        "reached)."
+                status = 'unknown'
+            else:
+                if show_progress:
+                    print "Optimal solution found."
+                status = 'optimal'
+            return { 'x': x,  'y': y,  's': s,  'z': z,  'status': status,
+                    'gap': gap,  'relative gap': relgap, 
+                    'primal objective': pcost,  'dual objective': dcost,
+                    'primal infeasibility': pres,
+                    'dual infeasibility': dres, 'primal slack': -ts,
+                    'dual slack': -tz }
+                    
 
         # Compute initial scaling W and scaled iterates:  
         #
@@ -1789,8 +2186,20 @@ def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
             if iters == 0:
                 raise ValueError, "Rank(A) < p or Rank([P; A; G]) < n"
             else:  
-                raise ArithmeticError, "singular KKT matrix"
-
+                ind = dims['l'] + sum(dims['q'])
+                for m in dims['s']:
+                    misc.symm(s, m, ind)
+                    misc.symm(z, m, ind)
+                    ind += m**2
+                ts = misc.max_step(s, dims)
+                tz = misc.max_step(z, dims)
+                print "Terminated (singular KKT matrix)."
+                return { 'x': x,  'y': y,  's': s,  'z': z,  
+                    'status': 'unknown', 'gap': gap,  
+                    'relative gap': relgap, 'primal objective': pcost,  
+                    'dual objective': dcost, 'primal infeasibility': pres,
+                    'dual infeasibility': dres, 'primal slack': -ts,
+                    'dual slack': -tz }   
 
         # f4_no_ir(x, y, z, s) solves
         # 
@@ -1921,7 +2330,21 @@ def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
                 if iters == 0:
                     raise ValueError, "Rank(A) < p or Rank([P; A; G]) < n"
                 else:
-                    raise ArithmeticError, "singular KKT matrix"
+                    ind = dims['l'] + sum(dims['q'])
+                    for m in dims['s']:
+                        misc.symm(s, m, ind)
+                        misc.symm(z, m, ind)
+                        ind += m**2
+                    ts = misc.max_step(s, dims)
+                    tz = misc.max_step(z, dims)
+                    print "Terminated (singular KKT matrix)."
+                    return { 'x': x,  'y': y,  's': s,  'z': z,  
+                        'status': 'unknown', 'gap': gap,  
+                        'relative gap': relgap, 'primal objective': pcost, 
+                        'dual objective': dcost,
+                        'primal infeasibility': pres,
+                        'dual infeasibility': dres, 'primal slack': -ts,
+                        'dual slack': -tz }
 
             # Save ds o dz for Mehrotra correction
             if correction and i == 0:
@@ -2046,9 +2469,6 @@ def coneqp(P, q, G = None, h = None, dims = None, A = None, b = None,
 
         gap = blas.dot(lmbda, lmbda) 
 
-    return {'status': 'unknown', 'x': None,  'y': None, 'z': None, 
-        's': None}
-
 
 def lp(c, G, h, A = None, b = None, solver = None, primalstart = None,
     dualstart = None):
@@ -2056,13 +2476,17 @@ def lp(c, G, h, A = None, b = None, solver = None, primalstart = None,
 
     Solves a pair of primal and dual LPs
 
-        minimize    c'*x             maximize    -h'*z - b'*y 
-        subject to  G*x + s = h      subject to  G'*z + A'*y + c = 0
-                    A*x = b                      z >= 0.
+        minimize    c'*x
+        subject to  G*x + s = h
+                    A*x = b
                     s >= 0
 
+        maximize    -h'*z - b'*y
+        subject to  G'*z + A'*y + c = 0
+                    z >= 0.
 
-    Input arguments 
+
+    Input arguments. 
 
         G is m x n, h is m x 1, A is p x n, b is p x 1.  G and A must be 
         dense or sparse 'd' matrices.   h and b are dense 'd' matrices 
@@ -2075,12 +2499,12 @@ def lp(c, G, h, A = None, b = None, solver = None, primalstart = None,
         from MOSEK.
 
         The arguments primalstart and dualstart are ignored when solver
-        is 'glpk' or 'mosek' and are optional when solver is None.  
-        The argument primalstart is a dictionary with keys 'x' and 's'
+        is 'glpk' or 'mosek', and are optional when solver is None.  
+        The argument primalstart is a dictionary with keys 'x' and 's',
         and specifies a primal starting point.  primalstart['x'] must 
         be a dense 'd' matrix of length n;  primalstart['s'] must be a 
         positive dense 'd' matrix of length m.
-        The argument dualstart is a dictionary with keys 'z' and 'y' 
+        The argument dualstart is a dictionary with keys 'z' and 'y', 
         and specifies a dual starting point.   dualstart['y'] must 
         be a dense 'd' matrix of length p;  dualstart['z'] must be a 
         positive dense 'd' matrix of length m.
@@ -2089,49 +2513,194 @@ def lp(c, G, h, A = None, b = None, solver = None, primalstart = None,
         Rank([G; A]) = n
 
 
-    Returns a dictionary with keys 'status', 'x', 's', 'y', 'z'.
-
-        If solver is None or 'mosek':
-        
-            If status is 'optimal', x, s, y, z are the primal and dual 
-            optimal solutions.
-
-            If status is 'primal infeasible', x = s = None, and z, y 
-            are a proof of infeasibility: 
-
-                h'*z + b'*y = -1,  G'*z + A'*y = 0,  z >= 0.
+    Output arguments.
 
-            If status is 'dual infeasible', z = y = None, and x, s are 
-            a proof of dual infeasibility: 
+        Returns a dictionary with keys 'status', 'x', 's', 'z', 'y',
+        'primal objective', 'dual objective', 'gap', 'relative gap',  
+        'primal infeasibility', 'dual infeasibility', 'primal slack', 
+        'dual slack', 'residual as primal infeasibility certificate', 
+        'residual as dual infeasibility certificate'.
+
+        The 'status' field has values 'optimal', 'primal infeasible',
+        'dual infeasible', or 'unknown'.  The values of the other fields
+        depend on the exit status and the solver used.
+
+        Status 'optimal'. 
+        - 'x', 's', 'y', 'z' are an approximate solution of the primal and
+          dual optimality conditions   
+
+              G*x + s = h,  A*x = b  
+              G'*z + A'*y + c = 0 
+              s >= 0, z >= 0
+              s'*z = 0.
+
+        - 'primal objective': the primal objective c'*x.
+        - 'dual objective': the dual objective -h'*z - b'*y.
+        - 'gap': the duality gap s'*z.  
+        - 'relative gap': the relative gap, defined as s'*z / -c'*x if 
+          the primal objective is negative, s'*z / -(h'*z + b'*y) if the 
+          dual objective is positive, and None otherwise.
+        - 'primal infeasibility': the residual in the primal constraints,
+          defined as the maximum of the residual in the inequalities 
+
+              || G*x + s + h || / max(1, ||h||) 
+
+          and the residual in the equalities 
+
+              || A*x - b || / max(1, ||b||).
+
+        - 'dual infeasibility': the residual in the dual constraints,
+          defined as 
+
+              || G'*z + A'*y + c || / max(1, ||c||).
+
+        - 'primal slack': the smallest primal slack min_k s_k.
+        - 'dual slack': the smallest dual slack min_k z_k.
+        - 'residual as primal infeasibility certificate': None.
+        - 'residual as dual infeasibility certificate': None.
+        If the default solver is used, the primal infeasibility is 
+        guaranteed to be less than solvers.options['feastol'] 
+        (default 1e-7).  The dual infeasibility is guaranteed to be less 
+        than solvers.options['feastol'] (default 1e-7).  The gap is less 
+        than solvers.options['abstol'] (default 1e-7) or the relative gap 
+        is less than solvers.options['reltol'] (default 1e-6).
+        For the other solvers, the default GLPK or MOSEK exit criteria 
+        apply. 
+      
+        Status 'primal infeasible'.  If the GLPK solver is used, all the 
+        fields except the status field are None.  For the default and
+        the MOSEK solvers, the values are as follows. 
+        - 'x', 's': None.
+        - 'y', 'z' are an approximate certificate of infeasibility  
+
+              -h'*z - b'*y = 1,  G'*z + A'*y = 0,  z >= 0.
+
+        - 'primal objective': None.
+        - 'dual objective': 1.0.
+        - 'gap', 'relative gap': None.
+        - 'primal infeasibility' and 'dual infeasibility': None.
+        - 'primal slack': None.
+        - 'dual slack': the smallest dual slack min z_k.
+        - 'residual as primal infeasibility certificate': the residual in 
+          the condition of the infeasibility certificate, defined as 
+
+              || G'*z + A'*y || / max(1, ||c||).
+
+        - 'residual as dual infeasibility certificate': None.
+        If the default solver is used, the residual as primal infeasiblity
+        certificate is guaranteed to be less than 
+        solvers.options['feastol'] (default 1e-7).  For the other 
+        solvers, the default GLPK or MOSEK exit criteria apply.
+
+        Status 'dual infeasible'.  If the GLPK solver is used, all the 
+        fields except the status field are empty.  For the default and the
+        MOSEK solvers, the values are as follows. 
+        - 'x', 's' are an approximate proof of dual infeasibility 
+
+              c'*x = -1,  G*x + s = 0,  A*x = 0,  s >= 0.
+
+        - 'y', 'z': None.
+        - 'primal objective': -1.0.
+        - 'dual objective': None.
+        - 'gap', 'relative gap': None.
+        - 'primal infeasibility' and 'dual infeasibility': None.
+        - 'primal slack': the smallest primal slack min_k s_k .
+        - 'dual slack': None.
+        - 'residual as primal infeasibility certificate': None. 
+        - 'residual as dual infeasibility certificate: the residual in 
+          the conditions of the infeasibility certificate, defined as 
+          the maximum of 
+
+              || G*x + s || / max(1, ||h||) and || A*x || / max(1, ||b||).
+
+        If the default solver is used, the residual as dual infeasiblity 
+        certificate is guaranteed to be less than 
+        solvers.options['feastol'] (default 1e-7).  For the other 
+        solvers, the default GLPK or MOSEK exit criteria apply.
+
+        Status 'unknown'.  If the GLPK or MOSEK solver is used, all the 
+        fields except the status field are None.  If the default solver
+        is used, the values are as follows. 
+        - 'x', 'y', 's', 'z' are the last iterates before termination.   
+          These satisfy s > 0 and z > 0, but are not necessarily feasible.
+        - 'primal objective': the primal cost c'*x.
+        - 'dual objective': the dual cost -h'*z - b'*y.
+        - 'gap': the duality gap s'*z. 
+        - 'relative gap': the relative gap, defined as s'*z / -c'*x if the 
+          primal cost is negative, s'*z / -(h'*z + b'*y) if the dual cost 
+          is positive, and None otherwise.
+        - 'primal infeasibility ': the residual in the primal constraints,
+          defined as the maximum of the residual in the inequalities 
+
+              || G*x + s + h || / max(1, ||h||) 
+
+          and the residual in the equalities 
+
+              || A*x - b || / max(1, ||b||).
+
+        - 'dual infeasibility': the residual in the dual constraints,
+          defined as 
 
-                c'*x = -1,  G*x + s = 0,  A*x = 0,  s >= 0.
+              || G'*z + A'*y + c || / max(1, ||c||).
 
-            If status is 'unknown', x, y, s, z are None.
+        - 'primal slack': the smallest primal slack min_k s_k. 
+        - 'dual slack': the smallest dual slack min_k z_k. 
+        - 'residual as primal infeasibility certificate': 
+           None if h'*z + b'*y >= 0; the residual 
+
+              || G'*z + A'*y || / (-(h'*z + b'*y) * max(1, ||c||) ) 
+
+          otherwise.
+        - 'residual as dual infeasibility certificate': 
+          None if c'*x >= 0; the maximum of the residuals 
+
+              || G*x + s || / (-c'*x * max(1, ||h||))
+
+          and  
+
+              || A*x || / (-c'*x * max(1, ||b||)) 
+
+          otherwise.
+        Termination with status 'unknown' indicates that the algorithm 
+        failed to find a solution that satisfies the specified tolerances.
+        In some cases, the returned solution may be fairly accurate.  If 
+        the primal and dual infeasibilities, the gap, and the relative gap 
+        are small, then x, y, s, z are close to optimal.  If the residual 
+        as primal infeasibility certificate is small, then 
 
+            y / (-h'*z - b'*y),   z / (-h'*z - b'*y) 
 
-        If solver is 'glpk':
-        
-            If status is 'optimal', x, s, y, z are the primal and dual 
-            optimal solutions.
+        provide an approximate certificate of primal infeasibility.  If 
+        the residual as certificate of dual infeasibility is small, then 
 
-            If status is 'primal infeasible', 'dual infeasible',
-            or 'unknown', then x = s = z = y = None.
+            x / (-c'*x),   s / (-c'*x) 
 
+        provide an approximate proof of dual infeasibility.
 
-    The control parameters for the different solvers can be modified by 
-    adding an entry to the dictionary cvxopt.solvers.options.  The 
-    following parameters control the execution of the default solver.
 
-        options['show_progress'] True/False (default: True)
-        options['maxiters'] positive integer (default: 100)
-        options['abstol'] scalar (default: 1e-7)
-        options['reltol'] scalar (default: 1e-6)
-        options['feastol'] scalar (default: 1e-7).
+    Control parameters.
 
-    The control parameter names for GLPK and MOSEK can be found in the
-    GLPK and MOSEK documentation.  Options that are not recognized are 
-    replaced by their default values.
+        The control parameters for the different solvers can be modified 
+        by adding an entry to the dictionary cvxopt.solvers.options.  The 
+        following parameters control the execution of the default solver.
+
+            options['show_progress'] True/False (default: True)
+            options['maxiters'] positive integer (default: 100)
+            options['refinement']  positive integer (default: 0)
+            options['abstol'] scalar (default: 1e-7)
+            options['reltol'] scalar (default: 1e-6)
+            options['feastol'] scalar (default: 1e-7).
+
+        The control parameter names for GLPK are strings with the name of 
+        the GLPK parameter, listed in the GLPK documentation.  The MOSEK 
+        parameters can me modified by adding an entry options['MOSEK'],
+        containing a dictionary with MOSEK parameter/value pairs, as 
+        described in the MOSEK documentation.  
+
+        Options that are not recognized are replaced by their default 
+        values.
     """
+
     import math
     from cvxopt import base, blas, misc
     from cvxopt.base import matrix, spmatrix
@@ -2162,15 +2731,69 @@ def lp(c, G, h, A = None, b = None, solver = None, primalstart = None,
     if solver == 'glpk':
         try: from cvxopt import glpk
         except ImportError: raise ValueError, "invalid option "\
-            "(solver='glpk'): cvxopt.glpk is not installed" 
+            "(solver = 'glpk'): cvxopt.glpk is not installed" 
         glpk.options = options
-        status, x, z, y = glpk.lp(c,G,h,A,b)
+        status, x, z, y = glpk.lp(c, G, h, A, b)
+
         if status == 'optimal':
+            resx0 = max(1.0, blas.nrm2(c))
+            resy0 = max(1.0, blas.nrm2(b))
+            resz0 = max(1.0, blas.nrm2(h))
+
+            pcost = blas.dot(c,x)
+            dcost = -blas.dot(h,z) - blas.dot(b,y)
+
             s = matrix(h)
             base.gemv(G, x, s, alpha=-1.0, beta=1.0)
+
+            gap = blas.dot(s, z)
+            if pcost < 0.0:
+                relgap = gap / -pcost
+            elif dcost > 0.0:
+                relgap = gap / dcost
+            else: 
+                relgap = None
+   
+            # rx = c + G'*z + A'*y
+            rx = matrix(c)
+            base.gemv(G, z, rx, beta = 1.0, trans = 'T') 
+            base.gemv(A, y, rx, beta = 1.0, trans = 'T') 
+            resx = blas.nrm2(rx) / resx0
+
+            # ry = b - A*x
+            ry = matrix(b)
+            base.gemv(A, x, ry, alpha = -1.0, beta = 1.0)
+            resy = blas.nrm2(ry) / resy0
+
+            # rz = G*x + s - h
+            rz = matrix(0.0, (m,1))
+            base.gemv(G, x, rz)
+            blas.axpy(s, rz)
+            blas.axpy(h, rz, alpha = -1.0)
+            resz = blas.nrm2(rz) / resz0 
+
+            dims = {'l': m, 's': [], 'q': []}
+            pslack = -misc.max_step(s, dims)
+            dslack = -misc.max_step(z, dims)
+
+            pres, dres = max(resy, resz), resx
+            pinfres, dinfres = None, None
+
         else:
             s = None
-        return {'status': status, 'x': x, 's': s, 'y': y, 'z': z}
+            pcost, dcost = None, None
+            gap, relgap = None, None
+            pres, dres = None, None
+            pslack, dslack = None, None
+            pinfres, dinfres = None, None
+
+        return {'status': status, 'x': x, 's': s, 'y': y, 'z': z, 
+            'primal objective': pcost, 'dual objective': dcost, 
+            'gap': gap, 'relative gap': relgap,
+            'primal infeasibility': pres, 'dual infeasibility': dres, 
+            'primal slack': pslack, 'dual slack': dslack,
+            'residual as primal infeasibility certificate': pinfres, 
+            'residual as dual infeasibility certificate': dinfres} 
 
     if solver == 'mosek':
         try: 
@@ -2179,36 +2802,131 @@ def lp(c, G, h, A = None, b = None, solver = None, primalstart = None,
         except ImportError: 
             raise ValueError, "invalid option (solver = 'mosek'): "\
                 "cvxopt.mosek is not installed" 
+
         if 'MOSEK' in options:
             mosek.options = options['MOSEK']
         else:
             mosek.options = {}
+
         solsta, x, z, y  = mosek.lp(c, G, h, A, b)
+
+        resx0 = max(1.0, blas.nrm2(c))
+        resy0 = max(1.0, blas.nrm2(b))
+        resz0 = max(1.0, blas.nrm2(h))
+
         if solsta is msk.solsta.optimal:
-            s = matrix(h)
-            base.gemv(G, x, s, alpha=-1.0, beta=1.0)
             status = 'optimal'
+
+            pcost = blas.dot(c,x)
+            dcost = -blas.dot(h,z) - blas.dot(b,y)
+
+            # s = h - G*x
+            s = matrix(h)
+            base.gemv(G, x, s, alpha = -1.0, beta = 1.0)
+
+            gap = blas.dot(s, z)
+            if pcost < 0.0:
+                relgap = gap / -pcost
+            elif dcost > 0.0:
+                relgap = gap / dcost
+            else: 
+                relgap = None
+   
+            # rx = c + G'*z + A'*y
+            rx = matrix(c)
+            base.gemv(G, z, rx, beta = 1.0, trans = 'T') 
+            base.gemv(A, y, rx, beta = 1.0, trans = 'T') 
+            resx = blas.nrm2(rx) / resx0
+
+            # ry = b - A*x
+            ry = matrix(b)
+            base.gemv(A, x, ry, alpha = -1.0, beta = 1.0)
+            resy = blas.nrm2(ry) / resy0
+
+            # rz = G*x + s - h
+            rz = matrix(0.0, (m,1))
+            base.gemv(G, x, rz)
+            blas.axpy(s, rz)
+            blas.axpy(h, rz, alpha = -1.0)
+            resz = blas.nrm2(rz) / resz0 
+
+            dims = {'l': m, 's': [], 'q': []}
+            pslack = -misc.max_step(s, dims)
+            dslack = -misc.max_step(z, dims)
+
+            pres, dres = max(resy, resz), resx
+            pinfres, dinfres = None, None
+
         elif solsta is msk.solsta.prim_infeas_cer:
             status = 'primal infeasible'
-            ducost = -blas.dot(h,z) - blas.dot(b,y)
-            blas.scal(1.0/ducost, y)
-            blas.scal(1.0/ducost, z)
+
+            hz, by = blas.dot(h, z),  blas.dot(b, y)
+            blas.scal(1.0 / (-hz - by), y)
+            blas.scal(1.0 / (-hz - by), z)
+
+            # rx = -A'*y - G'*z 
+            rx = matrix(0.0, (n,1)) 
+            base.gemv(A, y, rx, alpha = -1.0, trans = 'T') 
+            base.gemv(G, z, rx, alpha = -1.0, beta = 1.0, trans = 'T') 
+            pinfres =  blas.nrm2(rx) / resx0 
+            dinfres = None
+
             x, s = None, None
+            pres, dres = None, None
+            pcost, dcost = None, 1.0
+            gap, relgap = None, None
+
+            dims = {'l': m, 's': [], 'q': []}
+            dslack = -misc.max_step(z, dims)
+            pslack = None
+
         elif solsta == msk.solsta.dual_infeas_cer:
             status = 'dual infeasible'
-            pcost = blas.dot(c,x)
-            blas.scal(-1.0/pcost, x)
+
+            cx = blas.dot(c,x)
+
+            blas.scal(-1.0/cx, x)
             s = matrix(0.0, (m,1))
-            base.gemv(G, x, s, alpha=-1.0)
+            base.gemv(G, x, s, alpha = -1.0)
+
+            # ry = A*x 
+            ry = matrix(0.0, (p,1))
+            base.gemv(A, x, ry)
+            resy = blas.nrm2(ry) / resy0
+
+            # rz = s + G*x  
+            rz = matrix(s)
+            base.gemv(G, x, rz, beta = 1.0)
+            resz = blas.nrm2(rz) / resz0
+           
+            pres, dres = None, None
+            dinfres, pinfres = max(resy, resz), None
             z, y = None, None
-        else: 
-            status = 'unknown'
-            x, s, y, z = None, None, None, None
-        return {'status': status, 'x': x, 's': s, 'y': y, 'z': z}
+            pcost, dcost = -1.0, None
+            gap, relgap = None, None
 
-    return conelp(c, G, h, {'l': m, 'q': [], 's': []}, A,  b, primalstart,
-        dualstart )
+            dims = {'l': m, 's': [], 'q': []}
+            pslack = -misc.max_step(s, dims)
+            dslack = None
+
+        else:
+            s = None
+            pcost, dcost = None, None
+            gap, relgap = None, None
+            pres, dres = None, None
+            pinfres, dinfres = None, None
+            pslack, dslack = None, None
+
+        return {'status': status, 'x': x, 's': s, 'y': y, 'z': z, 
+            'primal objective': pcost, 'dual objective': dcost, 
+            'gap': gap, 'relative gap': relgap,
+            'primal infeasibility': pres, 'dual infeasibility': dres, 
+            'residual as primal infeasibility certificate': pinfres, 
+            'residual as dual infeasibility certificate': dinfres,
+            'primal slack': pslack, 'dual slack': dslack} 
 
+    return conelp(c, G, h, {'l': m, 'q': [], 's': []}, A,  b, primalstart,
+        dualstart)
 
 
 def socp(c, Gl = None, hl = None, Gq = None, hq = None, A = None, b = None,
@@ -2235,14 +2953,14 @@ def socp(c, Gl = None, hl = None, Gq = None, hq = None, A = None, b = None,
         sq[k][0] >= || sq[k][1:] ||_2,  zq[k][0] >= || zq[k][1:] ||_2.
 
 
-    Input arguments 
+    Input arguments. 
 
         Gl is a dense or sparse 'd' matrix of size (ml, n).  hl is a 
         dense 'd' matrix of size (ml, 1). The default values of Gl and hl 
         are matrices with zero rows.
        
         The argument Gq is a list of N dense or sparse 'd' matrices of 
-        size (m[k] n), k = 0, ..., N-1, where m[k] >= 1.   hq is a list 
+        size (m[k] n), k = 0, ..., N-1, where m[k] >= 1.  hq is a list 
         of N dense 'd' matrices of size (m[k], 1), k = 0, ..., N-1.  
         The default values of Gq and hq are empty lists.
 
@@ -2251,7 +2969,7 @@ def socp(c, Gl = None, hl = None, Gq = None, hq = None, A = None, b = None,
         with zero rows.
 
         solver is None or 'mosek'.  The default solver (None) uses the 
-        cvxopt conelp() function.  The 'mosek' solver is the LP solver 
+        cvxopt conelp() function.  The 'mosek' solver is the SOCP solver 
         from MOSEK.
 
         The arguments primalstart and dualstart are ignored when solver
@@ -2272,36 +2990,212 @@ def socp(c, Gl = None, hl = None, Gq = None, hq = None, A = None, b = None,
         with respect to the second order cone of order m[k].
 
 
-    Returns a dictionary with keys 'status', 'x', 'sl', 'sq', 'y', 'zl',
-        'zq'.
+    Output arguments.
 
-        If status is 'optimal', x, sl, sq, y, zl, zq are approximate 
-        primal and dual optimal solutions.
+        Returns a dictionary with keys 'status', 'x', 'sl', 'sq', 'zl', 
+        'zq', 'y', 'primal objective', 'dual objective', 'gap', 
+        'relative gap',  'primal infeasibility', 'dual infeasibility', 
+        'primal slack', 'dual slack', 'residual as primal infeasibility 
+        certificate', 'residual as dual infeasibility certificate'.
 
-        If status is 'primal infeasible', x = sl = sq = None, and y, zl, 
-        zq are a proof of infeasibility: 
+        The 'status' field has values 'optimal', 'primal infeasible',
+        'dual infeasible', or 'unknown'.  The values of the other fields
+        depend on the exit status and the solver used.
 
-            hl'*zl + sum_k hq[k]' * zq[k] + b'*y = -1,  
-            Gl'*zl + sum_k Gq[k]' * zq[k] + A'*y = 0,  
-            zl >= 0,   zq[k] >= 0, k = 0, ..., N-1.
+        Status 'optimal'. 
+        - 'x', 'sl', 'sq', 'y', 'zl', 'zq' are an approximate solution of 
+          the primal and dual optimality conditions   
 
-        If status is 'dual infeasible', zl = zq = y = None, and x, sl, sq 
-        are a proof of dual infeasibility: 
+              G*x + s = h,  A*x = b  
+              G'*z + A'*y + c = 0 
+              s >= 0, z >= 0
+              s'*z = 0
 
-            c'*x = -1,  Gl*x + sl = 0,  Gq[k]*x + sq[k] = 0,  A*x = 0,  
-            sl >= 0,  sq[k] >= 0, k = 0, ..., N-1.
+          where
 
-        If status is 'unknown', x, y, sl, sq, zl, zq are None.
+              G = [ Gl; Gq[0]; ...; Gq[N-1] ]
+              h = [ hl; hq[0]; ...; hq[N-1] ]
+              s = [ sl; sq[0]; ...; sq[N-1] ]
+              z = [ zl; zq[0]; ...; zq[N-1] ].
 
+        - 'primal objective': the primal objective c'*x.
+        - 'dual objective': the dual objective -h'*z - b'*y.
+        - 'gap': the duality gap s'*z.  
+        - 'relative gap': the relative gap, defined as s'*z / -c'*x if 
+          the primal objective is negative, s'*z / -(h'*z + b'*y) if the 
+          dual objective is positive, and None otherwise.
+        - 'primal infeasibility': the residual in the primal constraints,
+          defined as the maximum of the residual in the inequalities 
+
+              || G*x + s + h || / max(1, ||h||) 
 
-    The following parameters control the execution of the default 
-    solver.
+          and the residual in the equalities 
 
-        options['show_progress'] True/False (default: True)
-        options['maxiters'] positive integer (default: 100)
-        options['abstol'] scalar (default: 1e-7)
-        options['reltol'] scalar (default: 1e-6)
-        options['feastol'] scalar (default: 1e-7).
+              || A*x - b || / max(1, ||b||).
+
+        - 'dual infeasibility': the residual in the dual constraints,
+          defined as 
+
+              || G'*z + A'*y + c || / max(1, ||c||).
+
+        - 'primal slack': the smallest primal slack, 
+
+              min( min_k sl_k, min_k (sq[k][0] - || sq[k][1:] ||) ).
+
+        - 'dual slack': the smallest dual slack, 
+
+              min( min_k zl_k, min_k (zq[k][0] - || zq[k][1:] ||) ).
+
+        - 'residual as primal infeasibility certificate': None.
+        - 'residual as dual infeasibility certificate': None.
+        If the default solver is used, the primal infeasibility is 
+        guaranteed to be less than solvers.options['feastol'] 
+        (default 1e-7).  The dual infeasibility is guaranteed to be less 
+        than solvers.options['feastol'] (default 1e-7).  The gap is less 
+        than solvers.options['abstol'] (default 1e-7) or the relative gap 
+        is less than solvers.options['reltol'] (default 1e-6).
+        If the MOSEK solver is used, the default MOSEK exit criteria 
+        apply.
+      
+        Status 'primal infeasible'.  
+        - 'x', 'sl', 'sq': None.
+        - 'y', 'zl', 'zq' are an approximate certificate of infeasibility  
+
+              -h'*z - b'*y = 1,  G'*z + A'*y = 0,  z >= 0.
+
+        - 'primal objective': None.
+        - 'dual objective': 1.0.
+        - 'gap', 'relative gap': None.
+        - 'primal infeasibility' and 'dual infeasibility': None.
+        - 'primal slack': None.
+        - 'dual slack': the smallest dual slack, 
+
+              min( min_k zl_k, min_k (zq[k][0] - || zq[k][1:] ||) ).
+
+        - 'residual as primal infeasibility certificate': the residual in 
+          the condition of the infeasibility certificate, defined as 
+
+              || G'*z + A'*y || / max(1, ||c||).
+
+        - 'residual as dual infeasibility certificate': None.
+        If the default solver is used, the residual as primal infeasiblity
+        certificate is guaranteed to be less than 
+        solvers.options['feastol'] (default 1e-7).  If the MOSEK solver is
+        used, the default MOSEK exit criteria apply.
+
+        Status 'dual infeasible'.  
+        - 'x', 'sl', 'sq': an approximate proof of dual infeasibility 
+
+              c'*x = -1,  G*x + s = 0,  A*x = 0,  s >= 0.
+
+        - 'y', 'zl', 'zq': None.
+        - 'primal objective': -1.0.
+        - 'dual objective': None.
+        - 'gap', 'relative gap': None.
+        - 'primal infeasibility' and 'dual infeasibility': None.
+        - 'primal slack': the smallest primal slack, 
+
+              min( min_k sl_k, min_k (sq[k][0] - || sq[k][1:] ||) ).
+
+        - 'dual slack': None.
+        - 'residual as primal infeasibility certificate': None. 
+        - 'residual as dual infeasibility certificate: the residual in 
+          the conditions of the infeasibility certificate, defined as 
+          the maximum of 
+
+              || G*x + s || / max(1, ||h||) and || A*x || / max(1, ||b||).
+
+        If the default solver is used, the residual as dual infeasiblity 
+        certificate is guaranteed to be less than 
+        solvers.options['feastol'] (default 1e-7).  If the MOSEK solver
+        is used, the default MOSEK exit criteria apply.
+
+        Status 'unknown'.  If the MOSEK solver is used, all the fields 
+        except the status field are empty.  If the default solver
+        is used, the values are as follows. 
+        - 'x', 'y', 'sl', 'sq', 'zl', 'zq': the last iterates before 
+          termination.   These satisfy s > 0 and z > 0, but are not 
+          necessarily feasible.
+        - 'primal objective': the primal cost c'*x.
+        - 'dual objective': the dual cost -h'*z - b'*y.
+        - 'gap': the duality gap s'*z. 
+        - 'relative gap': the relative gap, defined as s'*z / -c'*x if the 
+          primal cost is negative, s'*z / -(h'*z + b'*y) if the dual cost 
+          is positive, and None otherwise.
+        - 'primal infeasibility ': the residual in the primal constraints,
+          defined as the maximum of the residual in the inequalities 
+
+              || G*x + s + h || / max(1, ||h||) 
+
+          and the residual in the equalities 
+
+              || A*x - b || / max(1, ||b||).
+
+        - 'dual infeasibility': the residual in the dual constraints,
+          defined as 
+
+              || G'*z + A'*y + c || / max(1, ||c||).
+
+        - 'primal slack': the smallest primal slack, 
+
+              min( min_k sl_k, min_k (sq[k][0] - || sq[k][1:] ||) ).
+
+        - 'dual slack': the smallest dual slack, 
+
+              min( min_k zl_k, min_k (zq[k][0] - || zq[k][1:] ||) ).
+
+        - 'residual as primal infeasibility certificate': 
+           None if h'*z + b'*y >= 0; the residual 
+
+              || G'*z + A'*y || / (-(h'*z + b'*y) * max(1, ||c||) ) 
+
+          otherwise.
+        - 'residual as dual infeasibility certificate': 
+          None if c'*x >= 0; the maximum of the residuals 
+
+              || G*x + s || / (-c'*x * max(1, ||h||))
+
+          and  
+
+              || A*x || / (-c'*x * max(1, ||b||)) 
+
+          otherwise.
+        Termination with status 'unknown' indicates that the algorithm 
+        failed to find a solution that satisfies the specified tolerances.
+        In some cases, the returned solution may be fairly accurate.  If 
+        the primal and dual infeasibilities, the gap, and the relative gap 
+        are small, then x, y, s, z are close to optimal.  If the residual 
+        as primal infeasibility certificate is small, then 
+
+            y / (-h'*z - b'*y),   z / (-h'*z - b'*y) 
+
+        provide an approximate certificate of primal infeasibility.  If 
+        the residual as certificate of dual infeasibility is small, then 
+
+            x / (-c'*x),   s / (-c'*x) 
+
+        provide an approximate proof of dual infeasibility.
+
+
+    Control parameters.
+
+        The control parameters for the different solvers can be modified 
+        by adding an entry to the dictionary cvxopt.solvers.options.  The 
+        following parameters control the execution of the default solver.
+
+            options['show_progress'] True/False (default: True)
+            options['maxiters'] positive integer (default: 100)
+            options['refinement'] positive integer (default: 1)
+            options['abstol'] scalar (default: 1e-7)
+            options['reltol'] scalar (default: 1e-6)
+            options['feastol'] scalar (default: 1e-7).
+
+        The MOSEK parameters can me modified by adding an entry 
+        options['MOSEK'], containing a dictionary with MOSEK 
+        parameter/value pairs, as described in the MOSEK documentation.  
+
+        Options that are not recognized are replaced by their default 
+        values.
     """
 
     from cvxopt import base, blas
@@ -2355,7 +3249,11 @@ def socp(c, Gl = None, hl = None, Gq = None, hq = None, A = None, b = None,
     if type(b) is not matrix or b.typecode != 'd' or b.size != (p,1): 
         raise TypeError, "'b' must be a dense matrix of size (%d,1)" %p
 
+    dims = {'l': ml, 'q': mq, 's': []}
+    N = ml + sum(mq)
+
     if solver == 'mosek':
+        from cvxopt import misc
         try: 
             from cvxopt import mosek
             import pymosek as msk
@@ -2371,44 +3269,156 @@ def socp(c, Gl = None, hl = None, Gq = None, hq = None, A = None, b = None,
 
         solsta, x, zl, zq  = mosek.socp(c, Gl, hl, Gq, hq)
 
+        resx0 = max(1.0, blas.nrm2(c))
+        rh = matrix([ blas.nrm2(hl) ] + [ blas.nrm2(hqk) for hqk in hq ])
+        resz0 = max(1.0, blas.nrm2(rh))
+
         if solsta is msk.solsta.optimal:
+            status = 'optimal'
+            y = matrix(0.0, (0,1))
+            pcost = blas.dot(c,x)
+            dcost = -blas.dot(hl,zl) - \
+                sum([ blas.dot(hq[k],zq[k]) for k in xrange(len(mq))]) 
+
             sl = matrix(hl)
             base.gemv(Gl, x, sl, alpha = -1.0, beta = 1.0)
             sq = [ +hqk for hqk in hq ]
             for k in xrange(len(Gq)):
                 base.gemv(Gq[k], x, sq[k], alpha = -1.0, beta = 1.0)
-            y = matrix(0.0, (0,1))
-            status = 'optimal'
+
+            gap = blas.dot(sl, zl) + \
+                sum([blas.dot(zq[k],sq[k]) for k in xrange(len(mq))]) 
+            if pcost < 0.0:
+                relgap = gap / -pcost
+            elif dcost > 0.0:
+                relgap = gap / dcost
+            else: 
+                relgap = None
+
+            # rx = c + G'*z 
+            rx = matrix(c)
+            base.gemv(Gl, zl, rx, beta = 1.0, trans = 'T') 
+            for k in xrange(len(mq)):
+                base.gemv(Gq[k], zq[k], rx, beta = 1.0, trans = 'T') 
+            resx = blas.nrm2(rx) / resx0
+
+            # rz = G*x + s - h
+            rz = matrix(0.0, (ml + sum(mq),1))
+            base.gemv(Gl, x, rz)
+            blas.axpy(sl, rz)
+            blas.axpy(hl, rz, alpha = -1.0)
+            ind = ml
+            for k in xrange(len(mq)):
+                base.gemv(Gq[k], x, rz, offsety = ind)
+                blas.axpy(sq[k], rz, offsety = ind)
+                blas.axpy(hq[k], rz, alpha = -1.0, offsety = ind)
+                ind += mq[k]
+            resz = blas.nrm2(rz) / resz0 
+
+            s, z = matrix(0.0, (N,1)),  matrix(0.0, (N,1))
+            blas.copy(sl, s)
+            blas.copy(zl, z)
+            ind = ml
+            for k in xrange(len(mq)):
+                blas.copy(zq[k], z, offsety = ind)
+                blas.copy(sq[k], s, offsety = ind)
+                ind += mq[k]
+            pslack = -misc.max_step(s, dims)
+            dslack = -misc.max_step(z, dims)
+
+            pres, dres = resz, resx
+            pinfres, dinfres = None, None
 
         elif solsta is msk.solsta.prim_infeas_cer:
             status = 'primal infeasible'
-            ducost = -blas.dot(hl,zl) - sum([ blas.dot(hq[k], zq[k]) for
-                k in xrange(len(hq)) ])
             y = matrix(0.0, (0,1))
-            blas.scal(1.0/ducost, zl)
-            for zqk in zq: blas.scal(1.0/ducost, zqk)
+            hz = blas.dot(hl, zl) + sum([blas.dot(hq[k],zq[k]) for k 
+                in xrange(len(mq))]) 
+            blas.scal(1.0 / -hz, zl)
+            for k in xrange(len(mq)):
+                blas.scal(1.0 / -hz, zq[k])
+
             x, sl, sq = None, None, None
 
+            # rx = - G'*z 
+            rx = matrix(0.0, (n,1)) 
+            base.gemv(Gl, zl, rx, alpha = -1.0, beta = 1.0, trans = 'T') 
+            for k in xrange(len(mq)):
+                base.gemv(Gq[k], zq[k], rx, beta = 1.0, trans = 'T') 
+            pinfres =  blas.nrm2(rx) / resx0 
+            dinfres = None
+
+            z = matrix(0.0, (N,1))
+            blas.copy(zl, z)
+            ind = ml
+            for k in xrange(len(mq)):
+                blas.copy(zq[k], z, offsety = ind)
+                ind += mq[k]
+            dslack = -misc.max_step(z, dims)
+            pslack = None
+
+            x, s = None, None
+            pres, dres = None, None
+            pcost, dcost = None, 1.0
+            gap, relgap = None, None
+
         elif solsta == msk.solsta.dual_infeas_cer:
             status = 'dual infeasible'
-            pcost = blas.dot(c,x)
-            blas.scal(-1.0/pcost, x)
+            cx = blas.dot(c,x)
+
+            blas.scal(-1.0/cx, x)
             sl = matrix(0.0, (ml,1))
             base.gemv(Gl, x, sl, alpha = -1.0)
-            sq = [ matrix(0.0, (m,1)) for m in mq ]
+            sq = [ matrix(0.0, (mqk,1)) for mqk in mq ]
+            for k in xrange(len(mq)):
+                base.gemv(Gq[k], x, sq[k], alpha = -1.0, beta = 1.0)
+
+            # rz = s + G*x  
+            rz = matrix( [sl] + [sqk for sqk in sq])
+            base.gemv(Gl, x, rz, beta = 1.0)
+            ind = ml
             for k in xrange(len(mq)):
-                base.gemv(Gq[k], x, sq[k], alpha = -1.0)
-            zl, zq, y = None, None, None
+                base.gemv(Gq[k], x, rz, beta = 1.0, offsety = ind)
+                ind += mq[k]
+            resz = blas.nrm2(rz) / resz0
+
+            dims = {'l': ml, 's': [], 'q': mq}
+            s = matrix(0.0, (N,1))
+            blas.copy(sl, s)
+            ind = ml
+            for k in xrange(len(mq)):
+                blas.copy(sq[k], s, offsety = ind)
+                ind += mq[k]
+            pslack = -misc.max_step(s, dims)
+            dslack = None
+           
+            pres, dres = None, None
+            dinfres, pinfres = resz, None
+            z, y = None, None
+            pcost, dcost = -1.0, None
+            gap, relgap = None, None
 
         else: 
             status = 'unknown'
-            x, sl, sq, y, zl, zq = None, None, None, None, None, None
+            sl, sq = None, None
+            zl, zq = None, None
+            x, y = None, None
+            pcost, dcost = None, None
+            gap, relgap = None, None
+            pres, dres = None, None
+            pinfres, dinfres = None, None
+            pslack, dslack = None, None
+
+        print status
 
         return {'status': status, 'x': x, 'sl': sl, 'sq': sq, 'y': y, 
-            'zl': zl, 'zq': zq}
+            'zl': zl, 'zq': zq, 'primal objective': pcost, 
+            'dual objective': dcost, 'gap': gap, 'relative gap': relgap,
+            'primal infeasibility': pres, 'dual infeasibility': dres, 
+            'residual as primal infeasibility certificate': pinfres, 
+            'residual as dual infeasibility certificate': dinfres, 
+            'primal slack': pslack, 'dual slack': dslack} 
 
-    dims = {'l': ml, 'q': mq, 's': []}
-    N = ml + sum(mq)
     h = matrix(0.0, (N,1))
     if type(Gl) is matrix or [ Gk for Gk in Gq if type(Gk) is matrix ]:
         G = matrix(0.0, (N, n))
@@ -2450,30 +3460,31 @@ def socp(c, Gl = None, hl = None, Gq = None, hq = None, A = None, b = None,
 
     sol = conelp(c, G, h, dims, A = A, b = b, primalstart = ps, dualstart
         = ds)
-    val = {'status': sol['status'],  'x': sol['x'], 'y': sol['y']}
     if sol['s'] is None:  
-        val['sl'] = None
-        val['sq'] = None
+        sol['sl'] = None
+        sol['sq'] = None
     else: 
-        val['sl'] = sol['s'][:ml]  
-        val['sq'] = [ matrix(0.0, (m,1)) for m in mq ] 
+        sol['sl'] = sol['s'][:ml]  
+        sol['sq'] = [ matrix(0.0, (m,1)) for m in mq ] 
         ind = ml
         for k in xrange(len(mq)):
-            val['sq'][k][:] = sol['s'][ind : ind+mq[k]]
+            sol['sq'][k][:] = sol['s'][ind : ind+mq[k]]
             ind += mq[k]
+    del sol['s']
 
     if sol['z'] is None: 
-        val['zl'] = None
-        val['zq'] = None
+        sol['zl'] = None
+        sol['zq'] = None
     else: 
-        val['zl'] = sol['z'][:ml]
-        val['zq'] = [ matrix(0.0, (m,1)) for m in mq] 
+        sol['zl'] = sol['z'][:ml]
+        sol['zq'] = [ matrix(0.0, (m,1)) for m in mq] 
         ind = ml
         for k in xrange(len(mq)):
-            val['zq'][k][:] = sol['z'][ind : ind+mq[k]]
+            sol['zq'][k][:] = sol['z'][ind : ind+mq[k]]
             ind += mq[k]
+    del sol['z']
 
-    return val
+    return sol
 
     
 def sdp(c, Gl = None, hl = None, Gs = None, hs = None, A = None, b = None, 
@@ -2500,7 +3511,7 @@ def sdp(c, Gl = None, hl = None, Gs = None, hs = None, A = None, b = None,
     vector zs[k][:].
     
 
-    Input arguments 
+    Input arguments.
 
         Gl is a dense or sparse 'd' matrix of size (ml, n).  hl is a 
         dense 'd' matrix of size (ml, 1). The default values of Gl and hl 
@@ -2544,43 +3555,210 @@ def sdp(c, Gl = None, hl = None, Gs = None, hs = None, A = None, b = None,
         is 'dsdp'.
 
 
-    Returns a dictionary with keys 'status', 'x', 'sl', 'ss', 'y', 'zl',
-        'zs'.
+    Output arguments.
 
-        If status is 'optimal', x, sl, ss, y, zl, zs are approximate 
-        primal and dual optimal solutions.
+        Returns a dictionary with keys 'status', 'x', 'sl', 'ss', 'zl', 
+        'zs', 'y', 'primal objective', 'dual objective', 'gap', 
+        'relative gap',  'primal infeasibility', 'dual infeasibility', 
+        'primal slack', 'dual slack', 'residual as primal infeasibility 
+        certificate', 'residual as dual infeasibility certificate'.
 
-        If status is 'primal infeasible', x = sl = ss = None, and zl, 
-        zs, y are a proof of infeasibility: 
+        The 'status' field has values 'optimal', 'primal infeasible',
+        'dual infeasible', or 'unknown'.  The values of the other fields
+        depend on the exit status and the solver used.
 
-            hl'*zl + sum_k tr(hs[k]*zs[k]) + b'*y = -1,  
-            Gl'*zl + sum_k Gs[k]'*vec(zs[k]) + A'*y = 0,  
-            zl >= 0,  zs[k] >= 0, k = 0, ..., N-1.
+        Status 'optimal'. 
+        - 'x', 'sl', 'ss', 'y', 'zl', 'zs' are an approximate solution of 
+          the primal and dual optimality conditions   
 
-        If status is 'dual infeasible', zl = zs = y = None, and x, sl, 
-        ss are a proof of dual infeasibility: 
+              G*x + s = h,  A*x = b  
+              G'*z + A'*y + c = 0 
+              s >= 0, z >= 0
+              s'*z = 0
 
-            c'*x = -1,  
-            Gl*x + sl = 0,  mat(Gs[k]*x] + ss[k] = 0,  k = 0, ..., N-1
-            A*x = 0,  sl >= 0, ss[k] >= 0, k = 0, ..., N-1.
+          where
 
-        If status is 'unknown', x, y, sl, ss, zl, zs are None.
+              G = [ Gl; Gs[0][:]; ...; Gs[N-1][:] ]
+              h = [ hl; hs[0][:]; ...; hs[N-1][:] ]
+              s = [ sl; ss[0][:]; ...; ss[N-1][:] ]
+              z = [ zl; zs[0][:]; ...; zs[N-1][:] ].
 
+        - 'primal objective': the primal objective c'*x.
+        - 'dual objective': the dual objective -h'*z - b'*y.
+        - 'gap': the duality gap s'*z.  
+        - 'relative gap': the relative gap, defined as s'*z / -c'*x if 
+          the primal objective is negative, s'*z / -(h'*z + b'*y) if the 
+          dual objective is positive, and None otherwise.
+        - 'primal infeasibility': the residual in the primal constraints,
+          defined as the maximum of the residual in the inequalities 
 
-    The following parameters control the execution of the default 
-    solver.
+              || G*x + s + h || / max(1, ||h||) 
 
-        options['show_progress'] True/False (default: True)
-        options['maxiters'] positive integer (default: 100)
-        options['abstol'] scalar (default: 1e-7)
-        options['reltol'] scalar (default: 1e-6)
-        options['feastol'] scalar (default: 1e-7).
+          and the residual in the equalities 
+
+              || A*x - b || / max(1, ||b||).
+
+        - 'dual infeasibility': the residual in the dual constraints,
+          defined as 
+
+              || G'*z + A'*y + c || / max(1, ||c||).
+
+        - 'primal slack': the smallest primal slack, 
+    
+              min( min_k sl_k, min_k lambda_min(mat(ss[k])) ).
+
+        - 'dual slack': the smallest dual slack, 
+    
+              min( min_k zl_k, min_k lambda_min(mat(zs[k])) ).
+
+        - 'residual as primal infeasibility certificate': None.
+        - 'residual as dual infeasibility certificate': None.
+        If the default solver is used, the primal infeasibility is 
+        guaranteed to be less than solvers.options['feastol'] 
+        (default 1e-7).  The dual infeasibility is guaranteed to be less 
+        than solvers.options['feastol'] (default 1e-7).  The gap is less 
+        than solvers.options['abstol'] (default 1e-7) or the relative gap 
+        is less than solvers.options['reltol'] (default 1e-6).
+        If the DSDP solver is used, the default DSDP exit criteria 
+        apply.
+      
+        Status 'primal infeasible'.  
+        - 'x', 'sl', 'ss': None.
+        - 'y', 'zl', 'zs' are an approximate certificate of infeasibility  
+
+              -h'*z - b'*y = 1,  G'*z + A'*y = 0,  z >= 0.
+
+        - 'primal objective': None.
+        - 'dual objective': 1.0.
+        - 'gap', 'relative gap': None.
+        - 'primal infeasibility' and 'dual infeasibility': None.
+        - 'primal slack': None
+        - 'dual slack': the smallest dual slack, 
+    
+              min( min_k zl_k, min_k lambda_min(mat(zs[k])) ).
+
+        - 'residual as primal infeasibility certificate': the residual in 
+          the condition of the infeasibility certificate, defined as 
+
+              || G'*z + A'*y || / max(1, ||c||).
+
+        - 'residual as dual infeasibility certificate': None.
+        If the default solver is used, the residual as primal infeasiblity
+        certificate is guaranteed to be less than 
+        solvers.options['feastol'] (default 1e-7).  If the DSDP solver is
+        used, the default DSDP exit criteria apply.
+
+        Status 'dual infeasible'.  
+        - 'x', 'sl', 'ss': an approximate proof of dual infeasibility 
+
+              c'*x = -1,  G*x + s = 0,  A*x = 0,  s >= 0.
+
+        - 'y', 'zl', 'zs': None.
+        - 'primal objective': -1.0.
+        - 'dual objective': None.
+        - 'gap', 'relative gap': None.
+        - 'primal infeasibility' and 'dual infeasibility': None.
+        - 'primal slack': the smallest primal slack, 
+    
+              min( min_k sl_k, min_k lambda_min(mat(ss[k])) ).
+
+        - 'dual slack': None.
+        - 'residual as primal infeasibility certificate': None. 
+        - 'residual as dual infeasibility certificate: the residual in 
+          the conditions of the infeasibility certificate, defined as 
+          the maximum of 
+
+              || G*x + s || / max(1, ||h||) and || A*x || / max(1, ||b||).
+
+        If the default solver is used, the residual as dual infeasiblity 
+        certificate is guaranteed to be less than 
+        solvers.options['feastol'] (default 1e-7).  If the MOSEK solver
+        is used, the default MOSEK exit criteria apply.
+
+        Status 'unknown'.  If the DSDP solver is used, all the fields 
+        except the status field are empty.  If the default solver
+        is used, the values are as follows. 
+        - 'x', 'y', 'sl', 'ss', 'zl', 'zs': the last iterates before 
+          termination.   These satisfy s > 0 and z > 0, but are not 
+          necessarily feasible.
+        - 'primal objective': the primal cost c'*x.
+        - 'dual objective': the dual cost -h'*z - b'*y.
+        - 'gap': the duality gap s'*z. 
+        - 'relative gap': the relative gap, defined as s'*z / -c'*x if the 
+          primal cost is negative, s'*z / -(h'*z + b'*y) if the dual cost 
+          is positive, and None otherwise.
+        - 'primal infeasibility ': the residual in the primal constraints,
+          defined as the maximum of the residual in the inequalities 
+
+              || G*x + s + h || / max(1, ||h||) 
+
+          and the residual in the equalities 
+
+              || A*x - b || / max(1, ||b||).
+
+        - 'dual infeasibility': the residual in the dual constraints,
+          defined as 
+
+              || G'*z + A'*y + c || / max(1, ||c||).
+
+        - 'primal slack': the smallest primal slack, 
+    
+              min( min_k sl_k, min_k lambda_min(mat(ss[k])) ).
+
+        - 'dual slack': the smallest dual slack, 
+    
+              min( min_k zl_k, min_k lambda_min(mat(zs[k])) ).
+
+        - 'residual as primal infeasibility certificate': 
+           None if h'*z + b'*y >= 0; the residual 
+
+              || G'*z + A'*y || / (-(h'*z + b'*y) * max(1, ||c||) ) 
+
+          otherwise.
+        - 'residual as dual infeasibility certificate': 
+          None if c'*x >= 0; the maximum of the residuals 
+
+              || G*x + s || / (-c'*x * max(1, ||h||))
+
+          and  
+
+              || A*x || / (-c'*x * max(1, ||b||)) 
+
+          otherwise.
+        Termination with status 'unknown' indicates that the algorithm 
+        failed to find a solution that satisfies the specified tolerances.
+        In some cases, the returned solution may be fairly accurate.  If 
+        the primal and dual infeasibilities, the gap, and the relative gap 
+        are small, then x, y, s, z are close to optimal.  If the residual 
+        as primal infeasibility certificate is small, then 
+
+            y / (-h'*z - b'*y),   z / (-h'*z - b'*y) 
+
+        provide an approximate certificate of primal infeasibility.  If 
+        the residual as certificate of dual infeasibility is small, then 
+
+            x / (-c'*x),   s / (-c'*x) 
 
-    The execution of the 'dsdp' solver is controlled by: 
+        provide an approximate proof of dual infeasibility.
 
-        options['show_progress'] integer (default: 0)
-        options['maxiters'] positive integer 
-        options['rgaptol'] scalar (default: 1e-5).
+
+    Control parameters.
+
+        The following parameters control the execution of the default 
+        solver.
+
+            options['show_progress'] True/False (default: True)
+            options['maxiters'] positive integer (default: 100)
+            options['refinement'] positive integer (default: 1)
+            options['abstol'] scalar (default: 1e-7)
+            options['reltol'] scalar (default: 1e-6)
+            options['feastol'] scalar (default: 1e-7).
+
+        The execution of the 'dsdp' solver is controlled by: 
+
+            options['DSDP_Monitor'] integer (default: 0)
+            options['DSDP_MaxIts'] positive integer 
+            options['DSDP_GapTolerance'] scalar (default: 1e-5).
     """
 
     import math
@@ -2635,6 +3813,9 @@ def sdp(c, Gl = None, hl = None, Gs = None, hs = None, A = None, b = None,
     if type(b) is not matrix or b.typecode != 'd' or b.size != (p,1): 
         raise TypeError, "'b' must be a dense matrix of size (%d,1)" %p
 
+    dims = {'l': ml, 'q': [], 's': ms}
+    N = ml + sum([ m**2 for m in ms ])
+
     if solver == 'dsdp':
         try: from cvxopt import dsdp
         except ImportError: raise ValueError, "invalid option "\
@@ -2643,38 +3824,186 @@ def sdp(c, Gl = None, hl = None, Gs = None, hs = None, A = None, b = None,
         if p: raise ValueError, "sdp() with the solver = 'dsdp' option "\
             "does not handle problems with equality constraints"
         dsdpstatus, x, r, zl, zs = dsdp.sdp(c, Gl, hl, Gs, hs)
-        sl = hl - Gl*x
-        ss = [ hs[k] - matrix(Gs[k]*x, (ms[k], ms[k])) for k in 
-            xrange(len(ms)) ]
-        for k in xrange(len(ms)):  
-            misc.symm(ss[k], ms[k])
-            misc.symm(zs[k], ms[k])
-        if dsdpstatus == 'DSDP_PDFEASIBLE':
-            y = matrix(0.0, (0,1))
-            status = 'optimal'
-        elif dsdpstatus == 'DSDP_UNBOUNDED':
-            pcost = blas.dot(c,x)
-            x /= -pcost
-            sl /= -pcost
-            ss = [sk / -pcost for sk in ss]
-            zl, zs = None, None
+
+        resx0 = max(1.0, blas.nrm2(c))
+        rh = matrix([ blas.nrm2(hl) ] + [ math.sqrt(misc.sdot2(hsk, hsk)) 
+            for hsk in hs ])
+        resz0 = max(1.0, blas.nrm2(rh))
+
+        if dsdpstatus == 'DSDP_UNBOUNDED':
             status = 'dual infeasible'
+            cx = blas.dot(c,x)
+            blas.scal(-1.0/cx, x)
+            sl = -Gl*x
+            ss = [ -matrix(Gs[k]*x, (ms[k], ms[k])) for k in 
+                xrange(len(ms)) ]
+            for k in xrange(len(ms)):  
+                misc.symm(ss[k], ms[k])
+
+            # rz = s + G*x  
+            rz = matrix( [sl] + [ssk[:] for ssk in ss])
+            base.gemv(Gl, x, rz, beta = 1.0)
+            ind = ml
+            for k in xrange(len(ms)):
+                base.gemv(Gs[k], x, rz, beta = 1.0, offsety = ind)
+                ind += ms[k]**2
+            dims = {'l': ml, 's': ms, 'q': []}
+            resz = misc.nrm2(rz, dims) / resz0
+
+            s = matrix(0.0, (N,1))
+            blas.copy(sl, s)
+            ind = ml
+            for k in xrange(len(ms)):
+                blas.copy(ss[k], s, offsety = ind)
+                ind += ms[k]
+            pslack = -misc.max_step(s, dims)
+            sslack = None
+           
+            pres, dres = None, None
+            dinfres, pinfres = resz, None
+            zl, zs, y = None, None, None
+            pcost, dcost = -1.0, None
+            gap, relgap = None, None
+
         elif dsdpstatus == 'DSDP_INFEASIBLE':
-            dcost = -blas.dot(hl,zl) - misc.sdot2(hs, zs)
-            zl /= -dcost 
-            zs = [zs / -docst for zk in zs]
+            status = 'primal infeasible'
             y = matrix(0.0, (0,1))
+            hz = blas.dot(hl, zl) + misc.sdot2(hs, zs)
+            blas.scal(1.0 / -hz, zl)
+            for k in xrange(len(ms)):
+                blas.scal(1.0 / -hz, zs[k])
+                misc.symm(zs[k], ms[k])
+
+            # rx = -G'*z 
+            rx = matrix(0.0, (n,1)) 
+            base.gemv(Gl, zl, rx, alpha = -1.0, beta = 1.0, trans = 'T') 
+            ind = 0
+            for k in xrange(len(ms)):
+                blas.scal(0.5, zs[k], inc=ms[k]+1)
+                for j in xrange(ms[k]):
+                    blas.scal(0.0, zs[k], offset=j+ms[k]*(j+1), inc=ms[k])
+                base.gemv(Gs[k], zs[k], rx, alpha=2.0, beta=1.0, trans='T')
+                blas.scal(2.0, zs[k], inc=ms[k]+1)
+                ind += ms[k]
+            pinfres =  blas.nrm2(rx) / resx0 
+            dinfres = None
+
+            z = matrix(0.0, (N,1))
+            blas.copy(zl, z)
+            ind = ml
+            for k in xrange(len(ms)):
+                blas.copy(zs[k], z, offsety = ind)
+                ind += ms[k]
+            dslack = -misc.max_step(z, dims)
+            pslack = None
+
             x, sl, ss = None, None, None
-            status = 'primal infeasible'
-        else:
-            status = 'unknown'
-            x, sl, ss, y, zl, zs, = None, None, None, None, None, None
+            pres, dres = None, None
+            pcost, dcost = None, 1.0
+            gap, relgap = None, None
 
-        return {'status': status, 'x': x, 'y': y, 'sl': sl, 'ss': ss, 
-            'zl': zl, 'zs': zs}
+        else:
+            if dsdpstatus == 'DSDP_PDFEASIBLE':
+                status = 'optimal'
+            else: 
+                status = 'unknown'
+            y = matrix(0.0, (0,1))
+            sl = hl - Gl*x
+            ss = [ hs[k] - matrix(Gs[k]*x, (ms[k], ms[k])) for k in 
+                xrange(len(ms)) ]
+            for k in xrange(len(ms)): 
+                misc.symm(ss[k], ms[k])
+                misc.symm(zs[k], ms[k])
+            pcost = blas.dot(c,x)
+            dcost = -blas.dot(hl,zl) - misc.sdot2(hs, zs)
+            gap = blas.dot(sl, zl) + misc.sdot2(ss, zs) 
+            if pcost < 0.0:
+                relgap = gap / -pcost
+            elif dcost > 0.0:
+                relgap = gap / dcost
+            else: 
+                relgap = None
+
+            # rx = c + G'*z 
+            rx = matrix(c)
+            base.gemv(Gl, zl, rx, beta = 1.0, trans = 'T') 
+            ind = 0
+            for k in xrange(len(ms)):
+                blas.scal(0.5, zs[k], inc = ms[k]+1)
+                for j in xrange(ms[k]):
+                    blas.scal(0.0, zs[k], offset=j+ms[k]*(j+1), inc=ms[k])
+                base.gemv(Gs[k], zs[k], rx, alpha=2.0, beta=1.0, trans='T')
+                blas.scal(2.0, zs[k], inc=ms[k]+1)
+                ind += ms[k]
+            resx = blas.nrm2(rx) / resx0
+
+            # rz = G*x + s - h
+            rz = matrix(0.0, (ml + sum([msk**2 for msk in ms]), 1))
+            base.gemv(Gl, x, rz)
+            blas.axpy(sl, rz)
+            blas.axpy(hl, rz, alpha = -1.0)
+            ind = ml
+            for k in xrange(len(ms)):
+                base.gemv(Gs[k], x, rz, offsety = ind)
+                blas.axpy(ss[k], rz, offsety = ind, n = ms[k]**2)
+                blas.axpy(hs[k], rz, alpha = -1.0, offsety = ind, 
+                    n = ms[k]**2)
+                ind += ms[k]**2
+            resz = misc.snrm2(rz, dims) / resz0 
+            pres, dres = resz, resx
+
+            s, z = matrix(0.0, (N,1)), matrix(0.0, (N,1))
+            blas.copy(sl, s)
+            blas.copy(zl, z)
+            ind = ml
+            for k in xrange(len(ms)):
+                blas.copy(ss[k], s, offsety = ind)
+                blas.copy(zs[k], z, offsety = ind)
+                ind += ms[k]
+            pslack = -misc.max_step(s, dims)
+            dslack = -misc.max_step(z, dims)
+
+            if status is 'optimal' or dcost <= 0.0:
+                pinfres = None
+            else:
+                # rx = G'*z 
+                rx = matrix(0.0, (n,1))
+                base.gemv(Gl, zl, rx, beta = 1.0, trans = 'T') 
+                ind = 0
+                for k in xrange(len(ms)):
+                    blas.scal(0.5, zs[k], inc = ms[k]+1)
+                    for j in xrange(ms[k]):
+                        blas.scal(0.0, zs[k], offset=j+ms[k]*(j+1), 
+                            inc=ms[k])
+                    base.gemv(Gs[k], zs[k], rx, alpha=2.0, beta=1.0, 
+                        trans='T')
+                    blas.scal(2.0, zs[k], inc=ms[k]+1)
+                    ind += ms[k]
+                pinfres = blas.nrm2(rx) / resx0 / dcost
+
+            if status is 'optimal' or pcost >= 0.0:
+                dinfres = None
+            else:
+                # rz = G*x + s 
+                rz = matrix(0.0, (ml + sum([msk**2 for msk in ms]), 1))
+                base.gemv(Gl, x, rz)
+                blas.axpy(sl, rz)
+                ind = ml
+                for k in xrange(len(ms)):
+                    base.gemv(Gs[k], x, rz, offsety = ind)
+                    blas.axpy(ss[k], rz, offsety = ind, n = ms[k]**2)
+                    ind += ms[k]**2
+                dims = {'l': ml, 's': ms, 'q': []}
+                dinfres = misc.snrm2(rz, dims) / resz0  / -pcost
+
+        return {'status': status, 'x': x, 'sl': sl, 'ss': ss, 'y': y, 
+            'zl': zl, 'zs': zs, 'primal objective': pcost, 
+            'dual objective': dcost, 'gap': gap, 'relative gap': relgap,
+            'primal infeasibility': pres, 'dual infeasibility': dres, 
+            'residual as primal infeasibility certificate': pinfres, 
+            'residual as dual infeasibility certificate': dinfres,
+            'primal slack': pslack, 'dual slack': dslack} 
          
-    dims = {'l': ml, 'q': [], 's': ms}
-    N = ml + sum([ m**2 for m in ms ])
     h = matrix(0.0, (N,1))
     if type(Gl) is matrix or [ Gk for Gk in Gs if type(Gk) is matrix ]:
         G = matrix(0.0, (N, n))
@@ -2719,30 +4048,33 @@ def sdp(c, Gl = None, hl = None, Gs = None, hs = None, A = None, b = None,
 
     sol = conelp(c, G, h, dims, A = A, b = b, primalstart = ps, dualstart
         = ds)
-    val = {'status': sol['status'],  'x': sol['x'], 'y': sol['y']}
     if sol['s'] is None:
-        val['sl'] = None
-        val['ss'] = None
+        sol['sl'] = None
+        sol['ss'] = None
     else:
-        val['sl'] = sol['s'][:ml]
-        val['ss'] = [ matrix(0.0, (mk, mk)) for mk in ms ]
+        sol['sl'] = sol['s'][:ml]
+        sol['ss'] = [ matrix(0.0, (mk, mk)) for mk in ms ]
         ind = ml
         for k in xrange(len(ms)):
-             m = ms[k]
-             val['ss'][k][:] = sol['s'][ind:ind+m*m]
-             ind += m**2
+            m = ms[k]
+            sol['ss'][k][:] = sol['s'][ind:ind+m*m]
+            ind += m**2
+    del sol['s']
+
     if sol['z'] is None:
-        val['zl'] = None
-        val['zs'] = None
+        sol['zl'] = None
+        sol['zs'] = None
     else:
-        val['zl'] = sol['z'][:ml]
-        val['zs'] = [ matrix(0.0, (mk, mk)) for mk in ms ]
+        sol['zl'] = sol['z'][:ml]
+        sol['zs'] = [ matrix(0.0, (mk, mk)) for mk in ms ]
         ind = ml
         for k in xrange(len(ms)):
-             m = ms[k]
-             val['zs'][k][:] = sol['z'][ind:ind+m*m]
-             ind += m**2
-    return val
+            m = ms[k]
+            sol['zs'][k][:] = sol['z'][ind:ind+m*m]
+            ind += m**2
+    del sol['z']
+
+    return sol
 
 
 def qp(P, q, G = None, h = None, A = None, b = None, solver = None, 
@@ -2756,21 +4088,21 @@ def qp(P, q, G = None, h = None, A = None, b = None, solver = None,
                     A*x = b.
 
 
-    Input arguments 
+    Input arguments.
 
-        P is a nxn dense or sparse 'd' matrix with the lower triangular 
+        P is a n x n dense or sparse 'd' matrix with the lower triangular 
         part of P stored in the lower triangle.  Must be positive 
         semidefinite.
 
-        q is an nx1 dense 'd' matrix.
+        q is an n x 1 dense 'd' matrix.
 
-        G is an mxn dense or sparse 'd' matrix.
+        G is an m x n dense or sparse 'd' matrix.
 
-        h is an mx1 dense 'd' matrix.
+        h is an m x 1 dense 'd' matrix.
 
-        A is a pxn dense or sparse 'd' matrix.
+        A is a p x n dense or sparse 'd' matrix.
 
-        b is a px1 dense 'd' matrix or None.
+        b is a p x 1 dense 'd' matrix or None.
 
         solver is None or 'mosek'.
 
@@ -2778,32 +4110,160 @@ def qp(P, q, G = None, h = None, A = None, b = None, solver = None,
         zero rows.
 
 
-    Returns a dictionary with keys 'status', 'x', 's', 'y', 'z'.
+    Output arguments (default solver).
+
+        Returns a dictionary with keys 'status', 'x', 's', 'y', 'z',
+        'primal objective', 'dual objective', 'gap', 'relative gap',
+        'primal infeasibility, 'dual infeasibility', 'primal slack',
+        'dual slack'.
+    
+        The 'status' field has values 'optimal' or 'unknown'.  
+        If the status is 'optimal', 'x', 's', 'y', 'z' are an approximate
+        solution of the primal and dual optimal solutions
+
+            G*x + s = h,  A*x = b
+            P*x + G'*z + A'*y + q = 0
+            s >= 0, z >= 0 
+            s'*z = o.
+ 
+        If the status is 'unknown', 'x', 's', 'y', 'z' are the last
+        iterates before termination.  These satisfy s > 0 and z > 0, but
+        are not necessarily feasible.
+
+        The values of the other fields are defined as follows.
 
-        The default solver returns with status 'optimal' or 'unknown'.
-        The MOSEK solver can also return with status 'primal infeasible'
-        or 'dual infeasible'.
+        - 'primal objective': the primal objective (1/2)*x'*P*x + q'*x.
 
-        If status is 'optimal', x, s, y, z are the primal and dual 
-        optimal solutions.
+        - 'dual objective': the dual objective 
 
-        If status is 'primal infeasible', x = s = None and z, y are 
-        a proof of primal infeasibility:
+              L(x,y,z) = (1/2)*x'*P*x + q'*x + z'*(G*x - h) + y'*(A*x-b).
 
-            G'*z + A'*y = 0,  h'*z + b'*y = -1,  z >= 0.
+        - 'gap': the duality gap s'*z.  
 
-        If status is 'dual infeasible', z = y = None, and x, s are 
-        a proof of dual infeasibility:
+        - 'relative gap': the relative gap, defined as 
 
-            P*x = 0,  q'*x = -1,  G*x + s = 0,  A*x = 0,  s >=0
+              gap / -primal objective 
 
-        If status is 'unknown', x, y, s, z are None.  
+          if the primal objective is negative, 
+
+              gap / dual objective
+
+          if the dual objective is positive, and None otherwise.
+
+        - 'primal infeasibility': the residual in the primal constraints,
+          defined as the maximum of the residual in the inequalities 
+
+              || G*x + s + h || / max(1, ||h||) 
+
+          and the residual in the equalities 
+
+              || A*x - b || / max(1, ||b||).
+
+
+        - 'dual infeasibility': the residual in the dual constraints,
+          defined as 
+
+              || P*x + G'*z + A'*y + q || / max(1, ||q||).
+
+        - 'primal slack': the smallest primal slack, min_k s_k.
+        - 'dual slack': the smallest dual slack, min_k z_k.
+
+        If the exit status is 'optimal', then the primal and dual
+        infeasibilities are guaranteed to be less than 
+        solvers.options['feastol'] (default 1e-7).  The gap is less than 
+        solvers.options['abstol'] (default 1e-7) or the relative gap is 
+        less than solvers.options['reltol'] (default 1e-6).
+      
+        Termination with status 'unknown' indicates that the algorithm 
+        failed to find a solution that satisfies the specified tolerances.
+        In some cases, the returned solution may be fairly accurate.  If 
+        the primal and dual infeasibilities, the gap, and the relative gap
+        are small, then x, y, s, z are close to optimal.  
+
+
+    Output arguments (MOSEK solver).
+
+        The return dictionary has two additional fields 
+        'residual as primal infeasibility certificate' and 
+        'residual as dual infeasibility certificate', and 'status' field 
+        can also have the values 'primal infeasible' or 'dual infeasible'.
+
+        If the exit status is 'optimal', the different fields have the
+        same meaning as for the default solver, but the the magnitude of
+        the residuals and duality gap is controlled by the MOSEK exit 
+        criteria.  The 'residual as primal infeasibility certificate' and 
+        'residual as dual infeasibility certificate' are None.
+
+        Status 'primal infeasible'.  
+        - 'x', 's': None.
+        - 'y', 'z' are an approximate certificate of infeasibility  
+
+              G'*z + A'*y = 0,  h'*z + b'*y = -1,  z >= 0.
+
+        - 'primal objective': None.
+        - 'dual objective': 1.0.
+        - 'gap', 'relative gap': None.
+        - 'primal infeasibility' and 'dual infeasibility': None.
+        - 'primal slack': None.
+        - 'dual slack': the smallest dual slack min z_k.
+        - 'residual as primal infeasibility certificate': the residual in 
+          the condition of the infeasibility certificate, defined as 
+
+              || G'*z + A'*y || / max(1, ||c||).
+
+        - 'residual as dual infeasibility certificate': None.
+
+        Status 'dual infeasible'.  
+        - 'x', 's' are an approximate proof of dual infeasibility 
+
+              P*x = 0,  q'*x = -1,  G*x + s = 0,  A*x = 0,  s >= 0.
+
+        - 'y', 'z': None.
+        - 'primal objective': -1.0.
+        - 'dual objective': None.
+        - 'gap', 'relative gap': None.
+        - 'primal infeasibility' and 'dual infeasibility': None.
+        - 'primal slack': the smallest primal slack min_k s_k .
+        - 'dual slack': None.
+        - 'residual as primal infeasibility certificate': None. 
+        - 'residual as dual infeasibility certificate: the residual in 
+          the conditions of the infeasibility certificate, defined as 
+          the maximum of 
+
+              || P*x || / max(1, ||q||), 
+              || G*x + s || / max(1, ||h||),   
+              || A*x || / max(1, ||b||).
+
+
+        If status is 'unknown', all the other fields are None.
+
+
+    Control parameters.
+
+        The control parameters for the different solvers can be modified 
+        by adding an entry to the dictionary cvxopt.solvers.options.  The 
+        following parameters control the execution of the default solver.
+
+            options['show_progress'] True/False (default: True)
+            options['maxiters'] positive integer (default: 100)
+            options['refinement']  positive integer (default: 0)
+            options['abstol'] scalar (default: 1e-7)
+            options['reltol'] scalar (default: 1e-6)
+            options['feastol'] scalar (default: 1e-7).
+
+        The MOSEK parameters can me modified by adding an entry 
+        options['MOSEK'], containing a dictionary with MOSEK 
+        parameter/value pairs, as described in the MOSEK documentation.  
+
+        Options that are not recognized are replaced by their default 
+        values.
     """
 
     from cvxopt import base, blas
     from cvxopt.base import matrix, spmatrix
 
     if solver == 'mosek':
+        from cvxopt import misc
         try: 
             from cvxopt import mosek
             import pymosek 
@@ -2817,31 +4277,124 @@ def qp(P, q, G = None, h = None, A = None, b = None, solver = None,
         solsta, x, z, y = mosek.qp(P, q, G, h, A, b)
         m = G.size[0]
 
+        resx0 = max(1.0, blas.nrm2(q))
+        resy0 = max(1.0, blas.nrm2(b))
+        resz0 = max(1.0, blas.nrm2(h))
+
         if solsta == pymosek.solsta.optimal:
-            s = matrix(0.0, (m,1))
-            blas.copy(h, s)    
-            base.gemv(G, x, s, alpha = -1.0, beta = 1.0)
             status = 'optimal'
 
+            s = matrix(h)
+            base.gemv(G, x, s, alpha = -1.0, beta = 1.0)
+
+            # rx = q + P*x + G'*z + A'*y
+            # pcost = 0.5 * x'*P*x + q'*x
+            rx = matrix(q)
+            base.symv(P, x, rx, beta = 1.0)
+            pcost = 0.5 * (blas.dot(x, rx) + blas.dot(x, q))
+            base.gemv(A, y, rx, beta = 1.0, trans = 'T')
+            base.gemv(G, z, rx, beta = 1.0, trans = 'T')
+            resx = blas.nrm2(rx) / resx0
+
+            # ry = A*x - b
+            ry = matrix(b)
+            base.gemv(A, x, ry, alpha = 1.0, beta = -1.0)
+            resy = blas.nrm2(ry) / resy0
+
+            # rz = G*x + s - h
+            rz = matrix(0.0, (m,1))
+            base.gemv(G, x, rz)
+            blas.axpy(s, rz)
+            blas.axpy(h, rz, alpha = -1.0)
+            resz = blas.nrm2(rz) / resz0 
+
+            gap = blas.dot(s, z)
+            dcost = pcost + blas.dot(y, ry) + blas.dot(z, rz) - gap
+            if pcost < 0.0:
+                relgap = gap / -pcost
+            elif dcost > 0.0:
+                relgap = gap / dcost
+            else: 
+                relgap = None
+   
+            dims = {'l': m, 's': [], 'q': []}
+            pslack = -misc.max_step(s, dims)
+            dslack = -misc.max_step(z, dims)
+
+            pres, dres = max(resy, resz), resx
+            pinfres, dinfres = None, None
+
         elif solsta == pymosek.solsta.prim_infeas_cer:
             status = 'primal infeasible'
-            ducost = -blas.dot(h,z) - blas.dot(b,y)
-            blas.scal(1.0/ducost, y);
-            blas.scal(1.0/ducost, z);
+
+            hz, by = blas.dot(h, z),  blas.dot(b, y)
+            blas.scal(1.0 / (-hz - by), y)
+            blas.scal(1.0 / (-hz - by), z)
+
+            # rx = -A'*y - G'*z 
+            rx = matrix(0.0, (q.size[0],1)) 
+            base.gemv(A, y, rx, alpha = -1.0, trans = 'T') 
+            base.gemv(G, z, rx, alpha = -1.0, beta = 1.0, trans = 'T') 
+            pinfres =  blas.nrm2(rx) / resx0 
+            dinfres = None
+
             x, s = None, None
+            pres, dres = None, None
+            pcost, dcost = None, 1.0
+            gap, relgap = None, None
+
+            dims = {'l': m, 's': [], 'q': []}
+            dslack = -misc.max_step(z, dims)
+            pslack = None
 
-        elif solsta == pymosek.solsta_dual_infeas_cer:
+        elif solsta == pymosek.solsta.dual_infeas_cer:
             status = 'dual infeasible'
             qx = blas.dot(q,x)
-            if qx:  x /= (-qx)
+            blas.scal(-1.0/qx, x)
             s = matrix(0.0, (m,1))
             base.gemv(G, x, s, alpha=-1.0)
             z, y = None, None
+         
+            # rz = P*x 
+            rx = matrix(0.0, (q.size[0],1))
+            base.symv(P, x, rx, beta = 1.0)
+            resx = blas.nrm2(rx) / resx0
+
+            # ry = A*x 
+            ry = matrix(0.0, (b.size[0],1))
+            base.gemv(A, x, ry)
+            resy = blas.nrm2(ry) / resy0
+
+            # rz = s + G*x  
+            rz = matrix(s)
+            base.gemv(G, x, rz, beta = 1.0)
+            resz = blas.nrm2(rz) / resz0
+           
+            pres, dres = None, None
+            dinfres, pinfres = max(resx, resy, resz), None
+            z, y = None, None
+            pcost, dcost = -1.0, None
+            gap, relgap = None, None
+
+            dims = {'l': m, 's': [], 'q': []}
+            pslack = -misc.max_step(s, dims)
+            dslack = None
 
         else: 
             status = 'unknown'
             x, s, y, z = None, None, None, None
-
-        return {'status': status, 'x': x, 's': s, 'y': y, 'z': z}
+            pcost, dcost = None, None
+            gap, relgap = None, None
+            pres, dres = None, None
+            pslack, dslack = None, None
+            pinfres, dinfres = None, None
+            
+        return {'status': status, 'x': x, 's': s, 'y': y, 'z': z, 
+            'primal objective': pcost, 'dual objective': dcost, 
+            'gap': gap, 'relative gap': relgap,
+            'primal infeasibility': pres, 'dual infeasibility': dres, 
+            'primal slack': pslack, 'dual slack': dslack,
+            'residual as primal infeasibility certificate': pinfres, 
+            'residual as dual infeasibility certificate': dinfres} 
 
     return coneqp(P, q, G, h, None, A,  b, initvals)
diff --git a/src/python/cvxprog.py b/src/python/cvxprog.py
index ee2e5c1..fd50f10 100644
--- a/src/python/cvxprog.py
+++ b/src/python/cvxprog.py
@@ -8,7 +8,7 @@ to the quadratic programming solver from MOSEK.
 
 # Copyright 2004-2008 J. Dahl and L. Vandenberghe.
 # 
-# This file is part of CVXOPT version 1.0.
+# This file is part of CVXOPT version 1.1.
 #
 # CVXOPT is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -139,24 +139,89 @@ def cpl(c, F, G = None, h = None, dims = None, A = None, b = None,
 
     Output arguments.
 
-        cpl() returns a dictionary with keys 'status', 'x', 'snl', 'sl',
-        'znl', 'zl', 'y'.
+        Returns a dictionary with keys 'status', 'x', 'snl', 'sl', 'znl', 
+        'zl', 'y', 'primal objective', 'dual objective', 'gap', 
+        'relative gap', 'primal infeasibility', 'dual infeasibility',
+        'primal slack', 'dual slack'.
 
-        If status is 'optimal', x, snl, sl are approximate solutions of
-        the primal problem 
+        The 'status' field has values 'optimal' or 'unknown'.
+        If status is 'optimal', x, snl, sl, y, znl, zl are an approximate 
+        solution of the primal and dual optimality conditions 
 
-            minimize    c'*x 
-            subject to  f(x) + snl = 0
-                        G*x + sl  = h
-                        A*x =  b,
-                        snl >= 0, sl >= 0
+            f(x) + snl = 0,  G*x + sl = h,  A*x = b 
+            Df(x)'*znl + G'*zl + A'*y + c = 0 
+            snl >= 0,  znl >= 0,  sl >= 0,  zl >= 0
+            snl'*znl + sl'* zl = 0.
 
-        and y, znl, zl are approximate solutions of the dual problem
+        If status is 'unknown', x, snl, sl, y, znl, zl are the last
+        iterates before termination.  They satisfy snl > 0, znl > 0, 
+        sl > 0, zl > 0, but are not necessarily feasible.
 
-            maximize    inf_x (c'*x + znl'*f(x) + zl'*(G*x-h) + y'*(A*x-b))
-            subject to  znl >= 0, zl >= 0.
+        The values of the other fields are defined as follows.
 
-        If status is 'unknown', x, s, snl, sl, y, znl, zl are None.
+        - 'primal objective': the primal objective c'*x.
+
+        - 'dual objective': the dual objective 
+
+              L(x,y,znl,zl) = c'*x + znl'*f(x) + zl'*(G*x-h) + y'*(A*x-b).
+
+        - 'gap': the duality gap snl'*znl + sl'*zl.
+
+        - 'relative gap': the relative gap, defined as 
+
+              gap / -primal objective
+
+          if the primal objective is negative, 
+
+              gap / dual objective
+
+          if the dual objective is positive, and None otherwise.
+
+        - 'primal infeasibility': the residual in the primal constraints,
+          defined as 
+
+              || (f(x) + snl, G*x + sl - h, A*x-b) ||_2  
+
+          divided by 
+
+              max(1, || (f(x0) + 1, G*x0 + 1 - h, A*x0 - b) ||_2 )
+
+          where x0 is the point returned by F().
+
+        - 'dual infeasibility': the residual in the dual constraints,
+          defined as
+
+              || c + Df(x)'*znl + G'*zl + A'*y ||_2
+ 
+          divided by 
+
+              max(1, || c + Df(x0)'*1 + G'*1 ||_2 ).
+
+        - 'primal slack': the smallest primal slack, min( min_k sl_k,
+          sup {t | sl >= te} ) where 
+
+              e = ( e_0, e_1, ..., e_N, e_{N+1}, ..., e_{M+N} )
+    
+          is the identity vector in C.  e_0 is an ml-vector of ones, 
+          e_k, k = 1,..., N, is the unit vector (1,0,...,0) of length
+          mq[k], and e_k = vec(I) where I is the identity matrix of order
+          ms[k].
+
+        - 'dual slack': the smallest dual slack, min( min_k zl_k,
+          sup {t | zl >= te} ).
+               
+
+        If the exit status is 'optimal', then the primal and dual
+        infeasibilities are guaranteed to be less than 
+        solvers.options['feastol'] (default 1e-7).  The gap is less than
+        solvers.options['abstol'] (default 1e-7) or the relative gap is 
+        less than solvers.options['reltol'] (defaults 1e-6).     
+
+        Termination with status 'unknown' indicates that the algorithm 
+        failed to find a solution that satisfies the specified tolerances.
+        In some cases, the returned solution may be fairly accurate.  If
+        the primal and dual infeasibilities, the gap, and the relative gap
+        are small, then x, y, snl, sl, znl, zl are close to optimal.
 
 
     Advanced usage.
@@ -343,8 +408,14 @@ def cpl(c, F, G = None, h = None, dims = None, A = None, b = None,
     try: FEASTOL = options['feastol']
     except KeyError: FEASTOL = 1e-7
     else: 
-        if type(FEASTOL) is not float and type(FEASTOL) is not int: 
-            raise ValueError, "options['feastol'] must be a scalar"
+        if (type(FEASTOL) is not float and type(FEASTOL) is not int) or \
+            FEASTOL <= 0.0: 
+            raise ValueError, "options['feastol'] must be a positive "\
+                "scalar"
+
+    if RELTOL <= 0.0 and ABSTOL <= 0.0:
+        raise ValueError, "at least one of options['reltol'] and " \
+            "options['abstol'] must be positive"
 
     try: show_progress = options['show_progress']
     except KeyError: show_progress = True
@@ -552,7 +623,7 @@ def cpl(c, F, G = None, h = None, dims = None, A = None, b = None,
             "dres")
 
     relaxed_iters = 0
-    for iters in xrange(MAXITERS):  
+    for iters in xrange(MAXITERS + 1):  
 
         if refinement or DEBUG:  
             # We need H to compute residuals of KKT equations.
@@ -622,6 +693,8 @@ def cpl(c, F, G = None, h = None, dims = None, A = None, b = None,
         fG(x, rzl, beta = 1.0)
         reszl = misc.snrm2(rzl, dims)
 
+        # Statistics for stopping criteria.
+
         # pcost = c'*x
         # dcost = c'*x + y'*(A*x-b) + znl'*f(x) + zl'*(G*x-h)
         #       = c'*x + y'*(A*x-b) + znl'*(f(x)+snl) + zl'*(G*x-h+sl) 
@@ -630,7 +703,12 @@ def cpl(c, F, G = None, h = None, dims = None, A = None, b = None,
         pcost = xdot(c,x)
         dcost = pcost + ydot(y, ry) + blas.dot(z[:mnl], rznl) + \
             misc.sdot(z[mnl:], rzl, dims) - gap
-
+        if pcost < 0.0:
+            relgap = gap / -pcost
+        elif dcost > 0.0:
+            relgap = gap / dcost
+        else:
+            relgap = None
         pres = math.sqrt( resy**2 + resznl**2 + reszl**2 )
         dres = resx
         if iters == 0: 
@@ -642,24 +720,42 @@ def cpl(c, F, G = None, h = None, dims = None, A = None, b = None,
             theta1 = 1.0 / gap0
             theta2 = 1.0 / resx0
             theta3 = 1.0 / resznl0
-
         phi = theta1 * gap + theta2 * resx + theta3 * resznl
+        pres = pres / pres0
+        dres = dres / dres0
+
         if show_progress:
             print "%2d: % 8.4e % 8.4e % 4.0e% 7.0e% 7.0e" \
-                %(iters, pcost, dcost, gap, pres/pres0, dres/dres0) 
+                %(iters, pcost, dcost, gap, pres, dres) 
 
         # Stopping criteria.    
-        if pres/pres0 <= FEASTOL and dres/dres0 <= FEASTOL and \
-            ( gap <= ABSTOL or (dcost > 0 and gap/dcost <= RELTOL) or
-            (pcost < 0 and gap/(-pcost) <= RELTOL) ):
+        if ( pres <= FEASTOL and dres <= FEASTOL and ( gap <= ABSTOL or 
+            (relgap is not None and relgap <= RELTOL) )) or \
+            iters == MAXITERS:
             sl, zl = s[mnl:], z[mnl:]
             ind = dims['l'] + sum(dims['q'])
             for m in dims['s']:
                 misc.symm(sl, m, ind)
                 misc.symm(zl, m, ind)
                 ind += m**2
-            return {'status': 'optimal', 'x': x,  'y': y, 'znl': z[:mnl],  
-                'zl': zl, 'snl': s[:mnl], 'sl': sl}
+            ts = misc.max_step(s, dims, mnl)
+            tz = misc.max_step(z, dims, mnl)
+            if iters == MAXITERS:
+                if show_progress:
+                    print "Terminated (maximum number of iterations "\
+                        "reached)."
+                status = 'unknown'
+            else:
+                if show_progress:
+                    print "Optimal solution found."
+                status = 'optimal'
+
+            return {'status': status, 'x': x,  'y': y, 'znl': z[:mnl],  
+                'zl': zl, 'snl': s[:mnl], 'sl': sl, 'gap': gap, 
+                'relative gap': relgap, 'primal objective': pcost, 
+                'dual objective': dcost,  'primal slack': -ts, 
+                'dual slack': -tz, 'primal infeasibility': pres,
+                'dual infeasibility': dres }
 
 
         # Compute initial scaling W: 
@@ -684,6 +780,7 @@ def cpl(c, F, G = None, h = None, dims = None, A = None, b = None,
         
         try: f3 = kktsolver(x, z[:mnl], W)
         except ArithmeticError: 
+            singular_kkt_matrix = False
             if iters == 0:
                 raise ValueError, "Rank(A) < p or "\
                     "Rank([H(x); A; Df(x); G]) < n"
@@ -720,10 +817,30 @@ def cpl(c, F, G = None, h = None, dims = None, A = None, b = None,
 
                 try: f3 = kktsolver(x, z[:mnl], W)
                 except ArithmeticError: 
-                    raise ArithmeticError, "singular KKT matrix"
+                     singular_kkt_matrix = True
 
             else:  
-                raise ArithmeticError, "singular KKT matrix"
+                 singular_kkt_matrix = True
+
+            if singular_kkt_matrix:
+                sl, zl = s[mnl:], z[mnl:]
+                ind = dims['l'] + sum(dims['q'])
+                for m in dims['s']:
+                    misc.symm(sl, m, ind)
+                    misc.symm(zl, m, ind)
+                    ind += m**2
+                ts = misc.max_step(s, dims, mnl)
+                tz = misc.max_step(z, dims, mnl)
+                if show_progress:
+                    print "Terminated (singular KKT matrix)."
+                status = 'unknown'
+                return {'status': status, 'x': x,  'y': y, 
+                    'znl': z[:mnl],  'zl': zl, 'snl': s[:mnl], 
+                    'sl': sl, 'gap': gap, 'relative gap': relgap, 
+                    'primal objective': pcost, 'dual objective': dcost,  
+                    'primal infeasibility': pres, 
+                    'dual infeasibility': dres, 'primal slack': -ts,
+                    'dual slack': -tz }
 
 
         # f4_no_ir(x, y, z, s) solves
@@ -893,7 +1010,23 @@ def cpl(c, F, G = None, h = None, dims = None, A = None, b = None,
                     raise ValueError, "Rank(A) < p or "\
                         "Rank([H(x); A; Df(x); G]) < n"
                 else:
-                    raise ArithmeticError, "singular KKT matrix"
+                    sl, zl = s[mnl:], z[mnl:]
+                    ind = dims['l'] + sum(dims['q'])
+                    for m in dims['s']:
+                        misc.symm(sl, m, ind)
+                        misc.symm(zl, m, ind)
+                        ind += m**2
+                    ts = misc.max_step(s, dims, mnl)
+                    tz = misc.max_step(z, dims, mnl)
+                    if show_progress:
+                        print "Terminated (singular KKT matrix)."
+                    return {'status': 'unknown', 'x': x,  'y': y, 
+                        'znl': z[:mnl],  'zl': zl, 'snl': s[:mnl], 
+                        'sl': sl, 'gap': gap, 'relative gap': relgap, 
+                        'primal objective': pcost, 'dual objective': dcost,
+                        'primal infeasibility': pres, 
+                        'dual infeasibility': dres, 'primal slack': -ts,
+                        'dual slack': -tz }
 
             # Inner product ds'*dz and unscaled steps are needed in the 
             # line search.
@@ -1230,8 +1363,6 @@ def cpl(c, F, G = None, h = None, dims = None, A = None, b = None,
 
         gap = blas.dot(lmbda, lmbda) 
 
-    return {'status': 'unknown', 'x': None,  'y': None, 'znl': None, 
-        'zl': None, 'snl': None, 'sl': None}
 
 
 def cp(F, G = None, h = None, dims = None, A = None, b = None,
@@ -1344,23 +1475,37 @@ def cp(F, G = None, h = None, dims = None, A = None, b = None,
     Output arguments.
 
         cp() returns a dictionary with keys 'status', 'x', 'snl', 'sl',
-        'znl', 'zl', 'y'.
+        'znl', 'zl', 'y', 'primal objective', 'dual objective', 'gap',
+        'relative gap', 'primal infeasibility', 'dual infeasibility',
+        'primal slack', 'dual slack'.
 
-        If status is 'optimal', x, snl, sl are approximate solutions of
-        the primal problem 
+        The 'status' field has values 'optimal' or 'unknown'.
+        If status is 'optimal', x, snl, sl, y, znl, zl  are approximate 
+        solutions of the primal and dual optimality conditions
 
-            minimize    f0(x)  
-            subject to  fk(x) + snl_k = 0, k = 1,...,mnl
-                        G*x + sl  = h
-                        A*x =  b,
-                        snl >= 0, sl >= 0
+            f(x)[1:] + snl = 0,  G*x + sl = h,  A*x = b 
+            Df(x)'*[1; znl] + G'*zl + A'*y + c = 0 
+            snl >= 0,  znl >= 0,  sl >= 0,  zl >= 0
+            snl'*znl + sl'* zl = 0.
 
-        and y, znl, zl are approximate solutions of the dual problem
+        If status is 'unknown', x, snl, sl, y, znl, zl are the last
+        iterates before termination.  They satisfy snl > 0, znl > 0, 
+        sl > 0, zl > 0, but are not necessarily feasible.
 
-            maximize    inf_x ( (1,znl)'*f(x) + zl'*(G*x-h) + y'*(A*x-b) )
-            subject to  znl >= 0, zl >= 0.
+        The values of the other fields are the values returned by cpl()
+        applied to the epigraph form problem
 
-        If status is 'unknown' x, s, snl, sl, y, znl, zl are None.
+            minimize   t 
+            subjec to  f0(x) <= t
+                       fk(x) <= 0, k = 1, ..., mnl
+                       G*x <= h
+                       A*x = b.
+
+        Termination with status 'unknown' indicates that the algorithm 
+        failed to find a solution that satisfies the specified tolerances.
+        In some cases, the returned solution may be fairly accurate.  If
+        the primal and dual infeasibilities, the gap, and the relative gap
+        are small, then x, y, snl, sl, znl, zl are close to optimal.
 
 
     Advanced usage.
@@ -1500,6 +1645,7 @@ def cp(F, G = None, h = None, dims = None, A = None, b = None,
 
        options['show_progress'] True/False (default: True)
        options['maxiters'] positive integer (default: 100)
+       options['refinement'] nonnegative integer (default: 1)
        options['abstol'] scalar (default: 1e-7)
        options['reltol'] scalar (default: 1e-6)
        options['feastol'] scalar (default: 1e-7).
@@ -1609,7 +1755,9 @@ def cp(F, G = None, h = None, dims = None, A = None, b = None,
     #     subject to f0(x) - t <= 0
     #                f1(x) <= 0
     #                ...
-    #                fmnl(x) <= 0.
+    #                fmnl(x) <= 0
+    #                G*x <= h
+    #                A*x = b.
     #
     # The epigraph form variable is stored as a list [x, t].
 
@@ -1815,13 +1963,8 @@ def cp(F, G = None, h = None, dims = None, A = None, b = None,
     sol = cpl(c, F_e, G_e, h, dims, A_e, b, kktsolver_e, xnewcopy_e, 
          xdot_e, xaxpy_e, xscal_e)
 
-    if sol['status'] == 'optimal':
-        sol['x'] = sol['x'][0]
-        sol['znl'], sol['snl'] = sol['znl'][1:], sol['snl'][1:]
-    else:
-        sol['x'] = None
-        sol['zl'], sol['sl'] = None, None
-        sol['znl'], sol['snl'] = None, None 
+    sol['x'] = sol['x'][0]
+    sol['znl'], sol['snl'] = sol['znl'][1:], sol['snl'][1:]
     return sol
 
 
@@ -1835,7 +1978,7 @@ def gp(K, F, g, G=None, h=None, A=None, b=None):
                     G*x <= h      
                     A*x = b
 
-    Input arguments
+    Input arguments.
 
         K is a list of positive integers [K0, K1, K2, ..., Km].
 
@@ -1857,17 +2000,55 @@ def gp(K, F, g, G=None, h=None, A=None, b=None):
         zero rows.
 
 
-    Returns a dictionary with keys 'status', 'x', 'snl', 'sl, 'y', 
-        'znl', 'zl'.
+    Output arguments.
+
+        Returns a dictionary with keys 'status', 'x', 'snl', 'sl',
+        'znl', 'zl', 'y', 'primal objective', 'dual objective', 'gap',
+        'relative gap', 'primal infeasibility', 'dual infeasibility',
+        'primal slack', 'dual slack'.
+
+        The 'status' field has values 'optimal' or 'unknown'.
+        If status is 'optimal', x, snl, sl, y, znl, zl  are approximate 
+        solutions of the primal and dual optimality conditions
+
+            f(x)[1:] + snl = 0,  G*x + sl = h,  A*x = b 
+            Df(x)'*[1; znl] + G'*zl + A'*y + c = 0 
+            snl >= 0,  znl >= 0,  sl >= 0,  zl >= 0
+            snl'*znl + sl'* zl = 0,
+
+        where fk(x) = log sum exp (Fk*x + gk). 
+
+        If status is 'unknown', x, snl, sl, y, znl, zl are the last
+        iterates before termination.  They satisfy snl > 0, znl > 0, 
+        sl > 0, zl > 0, but are not necessarily feasible.
+
+        The values of the other fields are the values returned by cpl()
+        applied to the epigraph form problem
 
-        If status is 'optimal', x is the optimal solution, snl and sl
-        are the optimal slacks for the nonlinear and linear 
-        inequalities, znl and zl are the optimal dual veriables 
-        associated with the nonlinear and linear inequalities, and 
-        y are the optimal variables associated with the equality 
-        constraints.
+            minimize   t 
+            subjec to  f0(x) <= t
+                       fk(x) <= 0, k = 1, ..., mnl
+                       G*x <= h
+                       A*x = b.
 
-        If status is 'unknown', x, snl, sl, y, znl and zl are None.
+        Termination with status 'unknown' indicates that the algorithm 
+        failed to find a solution that satisfies the specified tolerances.
+        In some cases, the returned solution may be fairly accurate.  If
+        the primal and dual infeasibilities, the gap, and the relative gap
+        are small, then x, y, snl, sl, znl, zl are close to optimal.
+
+
+    Control parameters.
+
+       The following control parameters can be modified by adding an
+       entry to the dictionary options.
+
+       options['show_progress'] True/False (default: True)
+       options['maxiters'] positive integer (default: 100)
+       options['refinement'] nonnegative integer (default: 1)
+       options['abstol'] scalar (default: 1e-7)
+       options['reltol'] scalar (default: 1e-6)
+       options['feastol'] scalar (default: 1e-7).
     """
 
     import math 
diff --git a/src/python/info.py b/src/python/info.py
index ed4e178..d08a03f 100644
--- a/src/python/info.py
+++ b/src/python/info.py
@@ -1,8 +1,8 @@
-version = '1.0'
+version = '1.1'
 
 def license(): print(
 """
-CVXOPT version 1.0.  Copyright (c) 2004-2008 J. Dahl and L. Vandenberghe.
+CVXOPT version 1.1.  Copyright (c) 2004-2008 J. Dahl and L. Vandenberghe.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@ suite of sparse matrix algorithms, including:
 
 - AMD Version 2.2.0. Copyright (c) 2007 by Timothy A. Davis, Patrick R. 
   Amestoy, and Iain S. Duff.
-- CHOLMOD Version 1.6.0. Copyright (c) 2005-2007 by University of Florida, 
+- CHOLMOD Version 1.7.0. Copyright (c) 2005-2007 by University of Florida, 
   Timothy A. Davis and W. Hager.
 - COLAMD version 2.7.0. Copyright (c) 1998-2007 by Timothy A. Davis.
 - UMFPACK Version 5.2.0. Copyright (c) 1995-2006 by Timothy A. Davis.
diff --git a/src/python/misc.py b/src/python/misc.py
index 1b7378d..21d33aa 100644
--- a/src/python/misc.py
+++ b/src/python/misc.py
@@ -1,6 +1,6 @@
 # Copyright 2004-2008 J. Dahl and L. Vandenberghe.
 # 
-# This file is part of CVXOPT version 1.0.
+# This file is part of CVXOPT version 1.1.
 #
 # CVXOPT is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
diff --git a/src/python/modeling.py b/src/python/modeling.py
index aa5dc88..8f3fd11 100644
--- a/src/python/modeling.py
+++ b/src/python/modeling.py
@@ -7,7 +7,7 @@ piecewise-linear objective and constraint functions.
 
 # Copyright 2004-2008 J. Dahl and L. Vandenberghe.
 # 
-# This file is part of CVXOPT version 1.0.
+# This file is part of CVXOPT version 1.1.
 #
 # CVXOPT is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -1425,7 +1425,7 @@ class _minmax(object):
                 elif 1 == lg != lgf: 
                     lg = lgf
 
-            if cnst: self._flist += [_function()+cnst]
+            if cnst is not None: self._flist += [_function()+cnst]
 
 
     def __len__(self):
diff --git a/src/python/mosek.py b/src/python/mosek.py
index 7592b7e..b0b7dab 100644
--- a/src/python/mosek.py
+++ b/src/python/mosek.py
@@ -4,7 +4,7 @@ CVXOPT interface for MOSEK 5.0
 
 # Copyright 2004-2008 J. Dahl and L. Vandenberghe.
 # 
-# This file is part of CVXOPT version 1.0.
+# This file is part of CVXOPT version 1.1.
 #
 # CVXOPT is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -43,7 +43,7 @@ def lp(c, G, h, A=None, b=None):
                     
     using MOSEK 5.0.
 
-    (solsta, x, y, z) = lp(c, G, h, A=None, b=None).
+    (solsta, x, z, y) = lp(c, G, h, A=None, b=None).
 
     Input arguments 
 
@@ -56,31 +56,29 @@ def lp(c, G, h, A=None, b=None):
     Return values
 
         solsta   the solution status.
-                 If solsta is solsta.optimal,
-                   then (x, y, z) contains the primal-dual solution.
-                 If solsta is solsta.prim_infeas_cer,
-                   then (x, y, z) is a certificate of primal infeasibility.
-                 If solsta is solsta.dual_infeas_cer,
-                   then (x, y, z) is a certificate of dual infeasibility.
-                 If solsta is solsta.unknown,
-                   then (x, y, z) are all None
+                 If solsta is solsta.optimal, then (x, y, z) contains the 
+                     primal-dual solution.
+                 If solsta is solsta.prim_infeas_cer, then (x, y, z) is a 
+                     certificate of primal infeasibility.
+                 If solsta is solsta.dual_infeas_cer, then (x, y, z) is a 
+                     certificate of dual infeasibility.
+                 If solsta is solsta.unknown, then (x, y, z) are all None.
 
                  Other return values for solsta include:  
-                   solsta.dual_feas  
-                   solsta.near_dual_feas
-                   solsta.near_optimal
-                   solsta.near_prim_and_dual_feas
-                   solsta.near_prim_feas
-                   solsta.prim_and_dual_feas
-                   solsta.prim_feas
+                     solsta.dual_feas  
+                     solsta.near_dual_feas
+                     solsta.near_optimal
+                     solsta.near_prim_and_dual_feas
+                     solsta.near_prim_feas
+                     solsta.prim_and_dual_feas
+                     solsta.prim_feas
                  in which case the (x,y,z) value may not be well-defined,
                  c.f., the MOSEK documentation.
         
         x, y, z  the primal-dual solution.                    
 
-    Options are passed to MOSEK solvers via the mosek.options
-    dictionary, e.g., the following turns off output from 
-    the MOSEK solvers
+    Options are passed to MOSEK solvers via the mosek.options dictionary. 
+    For example, the following turns off output from the MOSEK solvers
     
     >>> mosek.options = {iparam.log:0} 
     
@@ -185,6 +183,7 @@ def lp(c, G, h, A=None, b=None):
     else:
         return (solsta[0], x, z, y)
 
+
 def conelp(c, G, h, dims = None):
     """
     Solves a pair of primal and dual SOCPs
diff --git a/src/python/printing.py b/src/python/printing.py
index f5f00a0..6b6008c 100644
--- a/src/python/printing.py
+++ b/src/python/printing.py
@@ -1,6 +1,6 @@
 # Copyright 2004-2008 J. Dahl and L. Vandenberghe.
 # 
-# This file is part of CVXOPT version 1.0.
+# This file is part of CVXOPT version 1.1.
 #
 # CVXOPT is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -190,5 +190,5 @@ def spmatrix_str_triplet(X):
     return s
 
 def spmatrix_repr_default(X):
-    return "<%ix%i matrix, tc='%c', nnz=%i>" \
+    return "<%ix%i sparse matrix, tc='%c', nnz=%i>" \
         %(X.size[0],X.size[1],X.typecode,len(X.V))
diff --git a/src/python/solvers.py b/src/python/solvers.py
index 757984f..f1981a2 100644
--- a/src/python/solvers.py
+++ b/src/python/solvers.py
@@ -15,7 +15,7 @@ options:  dictionary with customizable algorithm parameters.
 
 # Copyright 2004-2008 J. Dahl and L. Vandenberghe.
 # 
-# This file is part of CVXOPT version 1.0.
+# This file is part of CVXOPT version 1.1.
 #
 # CVXOPT is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
diff --git a/src/setup.py b/src/setup.py
index d6e2e24..d5000b6 100644
--- a/src/setup.py
+++ b/src/setup.py
@@ -157,7 +157,7 @@ extmods += [base, blas, lapack, umfpack, cholmod, amd, misc_solvers]
 
 setup (name = 'cvxopt', 
     description = 'Convex optimization package',
-    version = '1.0', 
+    version = '1.1', 
     long_description = '''
 CVXOPT is a free software package for convex optimization based on the 
 Python programming language. It can be used with the interactive Python 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/cvxopt.git



More information about the debian-science-commits mailing list