diff --git a/README b/README
index 14e446295e0e2acedd7a0e9664bd29a71e7a2642..622d1698d11de1785aa81552a31e9c61ad97b351 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-MONDRIAAN version 4.1 (this version released November 2016):
+MONDRIAAN version 4.2 (this version released September 2017):
 ------------------------
 Copyright May 2002 (version 1.0): Brendan Vastenhouw and Rob H. Bisseling
 Copyright July 2008 (version 2.0): Rob H. Bisseling, Wouter Meesen, Tristan van Leeuwen, 
@@ -9,7 +9,9 @@ Copyright July 2010 (version 3.0): Rob H. Bisseling, Bas Fagginger Auer,
 Copyright August 2013 (version 4.0): Rob H. Bisseling, Bas Fagginger Auer,
                      Daniel Pelt, Albert-Jan Yzelman.
 Copyright November 2016 (version 4.1): Rob H. Bisseling, Bas Fagginger Auer,
-                     Marco van Oort, Daniel Pelt, Albert-Jan Yzelman,
+                     Marco van Oort, Daniel Pelt, Albert-Jan Yzelman.
+Copyright September 2017 (version 4.2): Rob H. Bisseling, Bas Fagginger Auer,
+                     Marco van Oort, Daniel Pelt, Albert-Jan Yzelman.
 
 Extensive documentation is found in the `docs' directory.
                      
diff --git a/docs/HYPERGRAPH.html b/docs/HYPERGRAPH.html
index 9d4e1efe39da0888aee058ea80ec5799b6540bb8..57d17da0bf0b12ca798d41f20f72f576d1c1bde8 100644
--- a/docs/HYPERGRAPH.html
+++ b/docs/HYPERGRAPH.html
@@ -4,13 +4,24 @@
 <html>
 
 <head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 <link href="style.css" rel="stylesheet" type="text/css">
+<link href="print.css" rel="stylesheet" type="text/css" media="print">
+
 <title>Partitioning a hypergraph using Mondriaan</title>
 </head>
 
 <body>
 
+<div id="mainContainer">
+
+<div id="pageNav">
+	<div><a href="USERS_GUIDE.html">Mondriaan</a></div>
+	<div><a href="MATLAB.html">MATLAB</a></div>
+	<div><a href="USERS_GUIDE_OPT.html">MondriaanOpt</a></div>
+</div>
+
 <h2>Partitioning a hypergraph using Mondriaan</h2>
 <hr>
 <p>
@@ -29,15 +40,15 @@ Download the latest version of
 Mondriaan</a>. Uncompress with
 </p>
 <ul>
-<li><tt>% tar xzvf mondriaan4.tar.gz</tt></li>
+<li><code>% tar xzvf mondriaan4.tar.gz</code></li>
 </ul>
 <p>
-This will create a directory <tt>Mondriaan4</tt>
+This will create a directory <code>Mondriaan4</code>
 which contains all the files of the Mondriaan package. 
 Run
 </p>
 <ul>
-<li><tt>% make</tt></li>
+<li><code>% make</code></li>
 </ul>
 <p>
 which will build Mondriaan and the associated tools.
@@ -53,9 +64,9 @@ for each vertex, and a row for each hyperedge or net.
 </p>
 <div class="image squareimage"><img src="hypergraph.gif" alt=""><div class="caption">Figure 1</div></div>
 <p>
-For example, we can consider the hypergraph <tt>G = (V, E)</tt> from Figure 1 with
-vertices <tt>V = {1, 2, 3, 4, 5}</tt> and nets <tt>E = {{1}, {1, 2}, {2, 3, 4}, {3, 4}}</tt>
-as a matrix <tt>A</tt> with 5 columns and 4 rows in Matrix Market format:
+For example, we can consider the hypergraph <code>G = (V, E)</code> from Figure 1 with
+vertices <code>V = {1, 2, 3, 4, 5}</code> and nets <code>E = {{1}, {1, 2}, {2, 3, 4}, {3, 4}}</code>
+as a matrix <code>A</code> with 5 columns and 4 rows in Matrix Market format:
 </p>
 <pre>
 %%MatrixMarket weightedmatrix coordinate pattern general
@@ -75,8 +86,8 @@ as a matrix <tt>A</tt> with 5 columns and 4 rows in Matrix Market format:
 1
 </pre>
 <p>
-Here the values <tt>4 5 8 2</tt> indicate that this is a matrix with 4 rows (nets), 5 columns (vertices), 8 entries (total number of vertices in all nets), and weighted columns (the value 2 equals 10 in binary: weighted columns, unweighted rows).
-Then after all nonzeroes, <tt>1 1, 2 1, ...</tt>, we find the vertex weights, which are set to 1 for all five vertices.
+Here the values <code>4 5 8 2</code> indicate that this is a matrix with 4 rows (nets), 5 columns (vertices), 8 entries (total number of vertices in all nets), and weighted columns (the value 2 equals 10 in binary: weighted columns, unweighted rows).
+Then after all nonzeroes, <code>1 1, 2 1, ...</code>, we find the vertex weights, which are set to 1 for all five vertices.
 </p>
 <p>
 Providing the vertex weights is <b>essential</b>, because otherwise Mondriaan will by default weigh all the columns by the number of nonzeroes contained in them, which will lead to unbalanced hypergraph partitions.
@@ -84,23 +95,23 @@ Providing the vertex weights is <b>essential</b>, because otherwise Mondriaan wi
 
 <h3>Setting the proper Mondriaan options</h3>
 <p>
-Now that we have our hypergraph as a Matrix Market file, say <a href="hypergraph.mtx"><tt>foo.mtx</tt></a>, we can use Mondriaan to partition it.
-First we go to the <tt>tools/</tt> directory.
+Now that we have our hypergraph as a Matrix Market file, say <a href="hypergraph.mtx"><code>foo.mtx</code></a>, we can use Mondriaan to partition it.
+First we go to the <code>tools/</code> directory.
 </p>
 <ul>
-<li><tt>% cd tools</tt></li>
+<li><code>% cd tools</code></li>
 </ul>
 <p>
-Here the options file <tt>Mondriaan.defaults</tt> should set <tt>SplitStrategy</tt> to <tt>onedimcol</tt>, because we want Mondriaan to partition the matrix columns, which correspond to the hypergraph vertices.
-Then we partition <tt>foo.mtx</tt> in two parts with a maximum imbalance of 10% by running
+Here the options file <code>Mondriaan.defaults</code> should set <code>SplitStrategy</code> to <code>onedimcol</code>, because we want Mondriaan to partition the matrix columns, which correspond to the hypergraph vertices.
+Then we partition <code>foo.mtx</code> in two parts with a maximum imbalance of 10% by running
 </p>
 <ul>
-<li><tt>% ./Mondriaan foo.mtx 2 0.1</tt></li>
+<li><code>% ./Mondriaan foo.mtx 2 0.1</code></li>
 </ul>
 
 <h3>Extracting the hypergraph partitioning from the matrix partitioning</h3>
 <p>
-After performing the matrix partitioning, the file <tt>foo.mtx-v2</tt> contains the vector distribution of the columns
+After performing the matrix partitioning, the file <code>foo.mtx-v2</code> contains the vector distribution of the columns
 </p>
 <pre>
 5 2
@@ -111,10 +122,13 @@ After performing the matrix partitioning, the file <tt>foo.mtx-v2</tt> contains
 5 1
 </pre>
 <p>
-The first line <tt>5 2</tt> contains the number of columns (5) and the number of parts to which they have been assigned (2).
+The first line <code>5 2</code> contains the number of columns (5) and the number of parts to which they have been assigned (2).
 Following this line are the column indices and the parts to which the columns have been assigned.
-Because the column indices correspond directly to the vertices of our hypergraph, we see that our hypergraph has been partitioned into two parts: <tt>{1, 2, 5}</tt> and <tt>{3, 4}</tt>, which was to be expected if you look at Figure 1.
+Because the column indices correspond directly to the vertices of our hypergraph, we see that our hypergraph has been partitioned into two parts: <code>{1, 2, 5}</code> and <code>{3, 4}</code>, which was to be expected if you look at Figure 1.
 </p>
+
+</div>
+
 </body>
 
 </html>
diff --git a/docs/MATLAB.html b/docs/MATLAB.html
index a02a9d61766c540a69d67f0ce14f67cc39cd3f6d..70cb2764a4a3da8019f6c0aacf6f17cf81e600e3 100644
--- a/docs/MATLAB.html
+++ b/docs/MATLAB.html
@@ -4,13 +4,24 @@
 <html>
 
 <head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 <link href="style.css" rel="stylesheet" type="text/css">
+<link href="print.css" rel="stylesheet" type="text/css" media="print">
+
 <title>Mondriaan and MATLAB</title>
 </head>
 
 <body>
 
+<div id="mainContainer">
+
+<div id="pageNav">
+	<div><a href="USERS_GUIDE.html">Mondriaan</a></div>
+	<div><a href="HYPERGRAPH.html">Hypergraphs</a></div>
+	<div><a href="USERS_GUIDE_OPT.html">MondriaanOpt</a></div>
+</div>
+
 <h2>Mondriaan and MATLAB</h2>
 <hr>
 <p>
@@ -28,46 +39,46 @@ Download the latest version from the
 Mondriaan software homepage</a>. Uncompress with
 </p>
 <ul>
-<li><tt>% tar xzvf mondriaan4.tar.gz</tt></li>
+<li><code>% tar xzvf mondriaan4.tar.gz</code></li>
 </ul>
 <p>
-This will create a directory <tt>Mondriaan4</tt>
+This will create a directory <code>Mondriaan4</code>
 which contains all the files of the Mondriaan package. 
-To enable MATLAB support, open the file <tt>Mondriaan4/mondriaan.mk</tt>
+To enable MATLAB support, open the file <code>Mondriaan4/mondriaan.mk</code>
 with a text-editor and look for a line which looks similar to
 </p>
 <ul>
-<li><tt>#MATLABHOMEDIR := /usr/local/matlab</tt></li>
+<li><code>#MATLABHOMEDIR := /usr/local/matlab</code></li>
 </ul>
 <p>
 Change the directory on the right-hand side to your installation
-directory of MATLAB and remove the <tt>#</tt> in front of the line,
+directory of MATLAB and remove the <code>#</code> in front of the line,
 such that it looks similar to
 </p>
 <ul>
-<li><tt>MATLABHOMEDIR := /your/matlab/installation/directory</tt></li>
+<li><code>MATLABHOMEDIR := /your/matlab/installation/directory</code></li>
 </ul>
 <p>
-Furthermore make sure that the variable <tt>MEXSUFFIX</tt> is set to the proper
+Furthermore make sure that the variable <code>MEXSUFFIX</code> is set to the proper
 extension for MATLAB binary files for your system (from the Mathworks <a href="http://www.mathworks.nl/help/matlab/ref/mexext.html">site</a>):
 </p>
 <table border="1">
-<tr><td><b>Platform</b></td><td><b><tt>MEXSUFFIX</tt></b></td></tr>
-<tr><td>Linux (32-bit)</td><td><tt>mexglx</tt></td></tr>
-<tr><td>Linux (64-bit)</td><td><tt>mexa64</tt></td></tr>
-<tr><td>Apple Macintosh (32-bit)</td><td><tt>mexmaci</tt></td></tr>
-<tr><td>Apple Macintosh (64-bit)</td><td><tt>mexmaci64</tt></td></tr>
-<tr><td>Microsoft Windows (32-bit)</td><td><tt>mexw32</tt></td></tr>
-<tr><td>Microsoft Windows (64-bit)</td><td><tt>mexw64</tt></td></tr>
+<tr><td><b>Platform</b></td><td><b><code>MEXSUFFIX</code></b></td></tr>
+<tr><td>Linux (32-bit)</td><td><code>mexglx</code></td></tr>
+<tr><td>Linux (64-bit)</td><td><code>mexa64</code></td></tr>
+<tr><td>Apple Macintosh (32-bit)</td><td><code>mexmaci</code></td></tr>
+<tr><td>Apple Macintosh (64-bit)</td><td><code>mexmaci64</code></td></tr>
+<tr><td>Microsoft Windows (32-bit)</td><td><code>mexw32</code></td></tr>
+<tr><td>Microsoft Windows (64-bit)</td><td><code>mexw64</code></td></tr>
 </table>
 <p>
-For example: on a 32-bit Macintosh system we would have <tt>MEXSUFFIX := mexmaci</tt>.
+For example: on a 32-bit Macintosh system we would have <code>MEXSUFFIX := mexmaci</code>.
 </p>
 <p>
 Now we are ready to compile Mondriaan, run
 </p>
 <ul>
-<li><tt>% make</tt></li>
+<li><code>% make</code></li>
 </ul>
 <p>
 which will build Mondriaan and the associated tools.
@@ -80,35 +91,35 @@ MATLAB interface of Mondriaan.
 </p>
 <p>
 As test matrix we can use <a href="http://www.staff.science.uu.nl/~bisse101/Matrices/tbdmatlab.mtx.gz">tbdmatlab.mtx.gz</a>
-from the Mondriaan website. The archive should be extracted to the <tt>Mondriaan4/tools</tt> directory.
+from the Mondriaan website. The archive should be extracted to the <code>Mondriaan4/tools</code> directory.
 </p>
 <p>
-Start MATLAB and navigate to the <tt>Mondriaan4/tools</tt> directory in the <i>Current Directory</i>
+Start MATLAB and navigate to the <code>Mondriaan4/tools</code> directory in the <i>Current Directory</i>
 subwindow.
-To read and view <tt>tbdmatlab.mtx</tt>, issue
+To read and view <code>tbdmatlab.mtx</code>, issue
 </p>
 <ul>
-<li><tt>A = mmread('tbdmatlab.mtx');</tt></li>
-<li><tt>spy(A)</tt></li>
+<li><code>A = mmread('tbdmatlab.mtx');</code></li>
+<li><code>spy(A)</code></li>
 </ul>
 <p>
-We can partition the matrix <tt>A</tt> among 30 processors with a maximum imbalance of 3% by using
-the <tt>mondriaan</tt> function in MATLAB
+We can partition the matrix <code>A</code> among 30 processors with a maximum imbalance of 3% by using
+the <code>mondriaan</code> function in MATLAB
 </p>
 <ul>
-<li><tt>[I, s] = mondriaan(A, 30, 0.03);</tt></li>
+<li><code>[I, s] = mondriaan(A, 30, 0.03);</code></li>
 </ul>
 <p>
-where <tt>I</tt> is the same matrix as <tt>A</tt>, only with the real values
+where <code>I</code> is the same matrix as <code>A</code>, only with the real values
 of all the matrix nonzeroes set to the index of the processor to which
-the nonzero was assigned, and <tt>s</tt> contains partitioning information.
+the nonzero was assigned, and <code>s</code> contains partitioning information.
 Full output can be generated with
 </p>
 <ul>
-<li><tt>[I, s, p, q, r, c, rh, ch, B, u, v] = mondriaan(A, 30, 0.03, 2);</tt></li>
+<li><code>[I, s, p, q, r, c, rh, ch, B, u, v] = mondriaan(A, 30, 0.03, 2);</code></li>
 </ul>
 <p>
-where the last parameter (<tt>2</tt>) is the desired permutation method (see below).
+where the last parameter (<code>2</code>) is the desired permutation method (see below).
 Here, p and q are permutation vectors, r and c are row-boundaries and column-boundaries
 corresponding to the ordering's block structure, rh and ch store the separator hierarchy 
 information, the matrix B stores the reordered matrix PAQ (in MATLAB terminology:
@@ -116,7 +127,7 @@ B=A(p,q)) and finally u and v contain the indices of the processors to which the
 components are assigned (for parallel multiplication of <i>u = A*v</i>).
 See the <a href="USERS_GUIDE.html">User's Guide</a> for full details on these output 
 vectors and matrices. For particulars on the boundary and hierarchy functions, jump to
-the appriopiate section <a href="USERS_GUIDE.html#SBDoutput">here</a>.
+the appropriate section <a href="USERS_GUIDE.html#SBDoutput">here</a>.
 </p>
 <table border="1">
 <tr><td><b>Value</b><td><b>Ordering</b></td></tr>
@@ -134,10 +145,10 @@ of the input matrix A. This is done by setting a fifth parameter, such that the
 call becomes:
 </p>
 <ul>
-<li><tt>[I, s, p, q, r, c, rh, ch, B, u, v] = mondriaan(A, 30, 0.03, 2, symm);</tt></li>
+<li><code>[I, s, p, q, r, c, rh, ch, B, u, v] = mondriaan(A, 30, 0.03, 2, symm);</code></li>
 </ul>
 <p>where symm is 0 by default (if the parameter is not given), and indicates A is
-not symmetric. If <tt>symm</tt> takes a value 1 or 2, A is assumed symmetric
+not symmetric. If <code>symm</code> takes a value 1 or 2, A is assumed symmetric
 and <em>only the lower triangular part of A is passed through to Mondriaan</em>. This
 is exactly the same as using the regular (terminal-based) Mondriaan application on a
 symmetric matrix with the options SymmetricMatrix_UseSingleEntry set to <b>yes</b> and
@@ -148,14 +159,14 @@ taken as usual from the Mondriaan.defaults file. Recommended is to use the fineg
 or symmetric finegrain strategies. Others will work, but may not minimise the 
 communication volume during parallel sparse matrix-vector multiplication when 
 considering the full matrix A.</p>
-<p>Setting <tt>symm</tt> to 2 will indicate the matrix is structurally symmetric,
+<p>Setting <code>symm</code> to 2 will indicate the matrix is structurally symmetric,
 but as said before, still only the lower triangular part of A is passed through to
 Mondriaan. This makes no difference for any of the output parameters, except for B,
-which would, for <tt>symm</tt>=1, return an incorrect full matrix PAQ as the full
-reordered matrix is inferred only from the lower triangular part. Setting <tt>symm</tt>
+which would, for <code>symm</code>=1, return an incorrect full matrix PAQ as the full
+reordered matrix is inferred only from the lower triangular part. Setting <code>symm</code>
 to 2 prevents this by automatically postprocessing B by rebuilding PAQ using the 
 output parameters p and q.</p>
-<p>Note that setting <tt>symm</tt> equal to 1 or 2 yields symmetric permutations 
+<p>Note that setting <code>symm</code> equal to 1 or 2 yields symmetric permutations 
 (B=PAP<sup>T</sup>). Also note that it is not checked whether the input matrix really 
 is symmetric, and as such unsymmetric matrices can also be passed through this method.
 This probably does not yield any meaningful results.</p>
@@ -164,7 +175,7 @@ This probably does not yield any meaningful results.</p>
 <p>We present two small examples of using Matlab in conjunction with Mondriaan; the
 first will be on speeding up the sequential sparse matrix-vector multiply, the
 second will illustrate speeding up the sequential sparse LU decomposition.
-Assumed is that the working directory is <tt>Mondriaan4/tools</tt>. Also available
+Assumed is that the working directory is <code>Mondriaan4/tools</code>. Also available
 should be:
 <ul>
 <li>the tbdlinux matrix (available through Rob Bisseling's
@@ -181,14 +192,14 @@ system jitter.
 [<a href="#cite1">1</a>], 
 [<a href="#cite2">2</a>]):</h5>
 <p>
-<tt>
+<code>
 &gt;&gt; A=mmread('tbdlinux.mtx');<br>
 &gt;&gt; [I, s, p, q, r, c, rh, ch, B, u, v] = mondriaan(A,50,0.1,2);<br>
 &gt;&gt; x=rand(size(A,2),1);<br>
 &gt;&gt; tic, for i=1:1000 A*x; end, toc<br>
 Elapsed time is 24.707203 seconds.<br>
 &gt;&gt; tic, z=x(q), for i=1:1000 B*z; end, toc<br>
-Elapsed time is 19.786526 seconds.</tt>
+Elapsed time is 19.786526 seconds.</code>
 </p>
 <p>
 Using Mondriaan to transform the tbdlinux matrix into SBD form thus yields a modest 20 percent
@@ -203,7 +214,7 @@ for details.</p>
 [<a href="#cite3">3</a>], 
 [<a href="#cite4">4</a>]):</h5>
 <p>
-<tt>
+<code>
 &gt;&gt; A=mmread('west0497.mtx');<br>
 &gt;&gt; [I, s, p, q, r, c, rh, ch, B, u, v] = mondriaan(A,10,0.1,3);<br>
 &gt;&gt; tic, for i=1:1000 [L,U,lu_P] = lu(A); end, toc<br>
@@ -222,53 +233,56 @@ ans =<br>
 <br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4647<br>
 <br>
-</tt>
+</code>
 </p>
 <p>Here the use of Mondriaan with BBD ordering lets the stock MATLAB 
 LU algorithm run almost a factor 2 faster, and reduces the fill-in
 with almost a factor 3. Note that this is not the UMFPACK version of
 the LU algorithm, which employs its own reordering techniques
-(amongst others); see <tt>help lu</tt> within MATLAB.</p>
+(amongst others); see <code>help lu</code> within MATLAB.</p>
 
 
 <h3>Visualisation</h3>
 <p>
-We can also directly visualise the partitioning process by using <tt>mondriaanplot</tt>
+We can also directly visualise the partitioning process by using <code>mondriaanplot</code>
 in the following fashion:
 </p>
 <ul>
-<li><tt>mondriaanplot(A, 30, 0.03, 2);</tt></li>
+<li><code>mondriaanplot(A, 30, 0.03, 2);</code></li>
 </ul>
 <p>
 This concludes this small tutorial.
-More information is available through issueing <tt>help mondriaan</tt> from within MATLAB.
+More information is available through issuing <code>help mondriaan</code> from within MATLAB.
 </p>
 
 <h3>MondriaanOpt</h3>
+<p>
 Apart from Mondriaan itself, also MondriaanOpt is available in MATLAB through the MatlabMondriaanOpt MEX routine.
 Example matlab functions are given in mondriaanOpt.m and mondriaanOptPlot.m.
 The interface of mondriaanOpt is as follows:
+</p>
 <ul>
-<li><tt>[I, s] = mondriaanOpt(A, Imbalance, Volume)</tt></li>
+<li><code>[I, s] = mondriaanOpt(A, Imbalance, Volume)</code></li>
 </ul>
-Here, <tt>A</tt> is the sparse matrix to be partitioned, <tt>Imbalance</tt> is the maximum allowed load imbalance,
-<tt>Volume</tt> is the initial upper bound on the volume, <tt>I</tt> contains the partitioning information and
-<tt>s</tt> contains statistics about the run.
-For more information, type <tt>help mondriaanOpt</tt> or <tt>help mondriaanOptPlot</tt> in MATLAB.
-
+<p>
+Here, <code>A</code> is the sparse matrix to be partitioned, <code>Imbalance</code> is the maximum allowed load imbalance,
+<code>Volume</code> is the initial upper bound on the volume, <code>I</code> contains the partitioning information and
+<code>s</code> contains statistics about the run.
+For more information, type <code>help mondriaanOpt</code> or <code>help mondriaanOptPlot</code> in MATLAB.
+</p>
 
 <h3>References</h3>
 <p>
-[<a name="cite1" href="http://www.staff.science.uu.nl/~bisse101/Mondriaan/yzelman09.pdf">1</a>]
+[<a id="cite1" href="http://www.staff.science.uu.nl/~bisse101/Mondriaan/yzelman09.pdf">1</a>]
 <em>Cache-oblivious sparse matrix-vector multiplication by using sparse matrix partitioning methods</em>,
 A. N. Yzelman and Rob H. Bisseling, SIAM Journal of Scientific Computation, Vol. 31, Issue 4, pp. 3128-3154 (2009).<br>
-[<a name="cite2" href="http://www.sciencedirect.com/science/article/pii/S0167819111001062">2</a>]
+[<a id="cite2" href="http://www.sciencedirect.com/science/article/pii/S0167819111001062">2</a>]
 <em>Two-dimensional cache-oblivious sparse matrix-vector multiplication</em>,
 A. N. Yzelman and Rob H. Bisseling, Parallel Computing, Vol. 37, Issue 12, pp. 806-819 (2011).<br>
-[<a name="cite3" href="http://www.cerfacs.fr/files/cerfacs_algo/conferences/PastWorkshops/CSC05/11_Catalyurek_Aykanat.pdf">3</a>]
+[<a id="cite3" href="http://www.cerfacs.fr/files/cerfacs_algo/conferences/PastWorkshops/CSC05/11_Catalyurek_Aykanat.pdf">3</a>]
 <em>Hypergraph-partitioning-based sparse matrix ordering</em>,
 &Uuml;mit V. &Ccedil;ataly&uuml;rek and C. Aykanat, Second International Workshop on Combinatorial Scientic Computing, CERFACS, 2005.<br>
-[<a name="cite4" href="http://www.sandia.gov/~egboman/papers/HUND.pdf">4</a>]
+[<a id="cite4" href="http://www.sandia.gov/~egboman/papers/HUND.pdf">4</a>]
 <em>Hypergraph-based Unsymmetric Nested Dissection Ordering for Sparse LU Factorization</em>,
 L. Grigori, E. G. Boman, S. Donfack, and T. A. Davis, SIAM Journal of Scientific Computation,
 Vol. 32, Issue 6, pp. 3426-3446 (2010).<br>
@@ -294,6 +308,8 @@ Home page Mondriaan package</a>.</p>
 </p>
 <hr>
 
+</div>
+
 </body>
 
 </html>
diff --git a/docs/USERS_GUIDE.html b/docs/USERS_GUIDE.html
index 3fac12c2973101689e96c79f060d1214fab7acb0..110b7bed283dd5bfa9368f82efce91132ae2fe7f 100644
--- a/docs/USERS_GUIDE.html
+++ b/docs/USERS_GUIDE.html
@@ -4,32 +4,46 @@
 <html>
 
 <head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 <link href="style.css" rel="stylesheet" type="text/css">
-<title>User's guide Mondriaan version 4.1</title>
+<link href="print.css" rel="stylesheet" type="text/css" media="print">
+<script type="text/javascript" src="script.js"></script>
+
+<title>User's guide Mondriaan version 4.2</title>
+
 </head>
 
 <body>
 
-<h2>User's guide Mondriaan version 4.1</h2>
-
-<div id="top">
-<div><a href="#inst">Installing</a></div>
-<div><a href="#comp">Compiling</a></div>
-<div><a href="#run">Running</a></div>
-<div><a href="#outp">Output</a></div>
-<div><a href="#opts">Options</a></div>
-<div><a href="#libr">Library use</a></div>
-<div><a href="MATLAB.html">MATLAB</a></div>
-<div><a href="HYPERGRAPH.html">Hypergraphs</a></div>
-<div><a href="USERS_GUIDE_OPT.html">MondriaanOpt</a></div>
-<div><a href="#prof">Profiling</a></div>
-<div><a href="#dev">Developers</a></div>
-<div><a href="#moar">More...</a></div>
+<div id="mainContainer">
+
+<div id="pageNav">
+	<div><a href="MATLAB.html">MATLAB</a></div>
+	<div><a href="HYPERGRAPH.html">Hypergraphs</a></div>
+	<div><a href="USERS_GUIDE_OPT.html">MondriaanOpt</a></div>
+</div>
+
+<h2 class="mainTitle">User's guide Mondriaan version 4.2</h2>
+
+
+<div id="menuPos"></div>
+<div id="menu">
+	<div id="menuItems">
+		<div><a href="#inst">Installing</a></div>
+		<div><a href="#comp">Compiling</a></div>
+		<div><a href="#run">Running</a></div>
+		<div><a href="#outp">Output</a></div>
+		<div><a href="#opts">Options</a></div>
+		<div><a href="#libr">Library use</a></div>
+		<div><a href="#prof">Profiling</a></div>
+		<div><a href="#dev">Developers</a></div>
+		<div><a href="#moar">More...</a></div>
+	</div>
 </div>
 
 <hr>
-<p>
+<p class="updateNote">
 This page is continuously being improved and updated;
 therefore, a more recent version may be obtained 
 <a href="http://www.staff.science.uu.nl/~bisse101/Mondriaan/Docs/USERS_GUIDE.html">
@@ -38,17 +52,17 @@ This offline version is bundled with the software for your convenience.
 </p>
 <hr>
 
-<h3><a name="inst">How to download and install Mondriaan</a></h3>
+<h3><a class="anchor" id="inst">How to download and install Mondriaan</a></h3>
 <p>
 Download the latest version from the
 <a href="http://www.staff.science.uu.nl/~bisse101/Mondriaan/">
 Mondriaan software homepage</a>. Uncompress with, e.g.,
 </p>
 <ul>
-<li><tt>% tar xzvf mondriaan4.tar.gz</tt><br><small>(Here, '<tt>%</tt>' is the prompt of your operating system.)</small></li>
+<li><code>% tar xzvf mondriaan4.tar.gz</code><br><small>(Here, '<code>%</code>' is the prompt of your operating system.)</small></li>
 </ul>
 <p>
-This will create a directory <tt>Mondriaan4</tt>
+This will create a directory <code>Mondriaan4</code>
 which contains all the files of the Mondriaan package. 
 </p>
 
@@ -56,46 +70,46 @@ which contains all the files of the Mondriaan package.
 Important files are:
 </p>
 <ul>
-<li><tt>README</tt>, which tells you about copyright and how to cite the work.</li>
-<li><tt>COPYING</tt>, the GNU General Public License (GNU GPL).</li>
-<li><tt>COPYING.LESSER</tt>, the GNU Lesser General Public License (GNU LGPL),
+<li><code>README</code>, which tells you about copyright and how to cite the work.</li>
+<li><code>COPYING</code>, the GNU General Public License (GNU GPL).</li>
+<li><code>COPYING.LESSER</code>, the GNU Lesser General Public License (GNU LGPL),
 which is an addition to GNU GPL, making its use more liberal.</li>
-<li><tt>mondriaan.mk</tt>, which contains the Mondriaan compilation options.</li>
+<li><code>mondriaan.mk</code>, which contains the Mondriaan compilation options.</li>
 </ul>
 
-<h3><a name="comp">How to compile and test Mondriaan</a></h3>
+<h3><a class="anchor" id="comp">How to compile and test Mondriaan</a></h3>
 <p>
-Go inside the directory <tt>Mondriaan4</tt> and type
+Go inside the directory <code>Mondriaan4</code> and type
 </p>
 <ul>
-<li><tt>% make</tt></li>
+<li><code>% make</code></li>
 </ul>
 <p>
 This will compile the Mondriaan library, the <a href="USERS_GUIDE_OPT.html">MondriaanOpt</a> library and
 tools included with the Mondriaan library.
 
-After compilation the include files are located in <tt>Mondriaan4/src/include</tt>,
-the compiled library in <tt>Mondriaan4/src/lib</tt>,
-and the stand-alone Mondriaan tools in <tt>Mondriaan4/tools</tt>.
+After compilation the include files are located in <code>Mondriaan4/src/include</code>,
+the compiled library in <code>Mondriaan4/src/lib</code>,
+and the stand-alone Mondriaan tools in <code>Mondriaan4/tools</code>.
 
 An example of how to use the Mondriaan library in your own
-program is provided by <tt>Mondriaan4/docs/example.c</tt> and
-<tt>Mondriaan4/tools/Mondriaan.c</tt>.
+program is provided by <code>Mondriaan4/docs/example.c</code> and
+<code>Mondriaan4/tools/Mondriaan.c</code>.
 </p>
 
 <p>
 For more information about MATLAB usage, please see the <a href="MATLAB.html">Mondriaan MATLAB guide</a>.
 To enable <em>MATLAB support</em> for Mondriaan and MondriaanOpt, edit the file
-<tt>mondriaan.mk</tt> and change the line containing the variable
-<tt>MATLABHOMEDIR</tt> to your current MATLAB install directory and
-remove the '<tt>#</tt>' in front of the line to uncomment it.
-The variable <tt>MEXSUFFIX</tt> should be set to the appropriate
+<code>mondriaan.mk</code> and change the line containing the variable
+<code>MATLABHOMEDIR</code> to your current MATLAB install directory and
+remove the '<code>#</code>' in front of the line to uncomment it.
+The variable <code>MEXSUFFIX</code> should be set to the appropriate
 extension for your platform, as specified on the Mathworks <a href="http://www.mathworks.nl/help/matlab/ref/mexext.html">site</a>.
 This should be done before compiling Mondriaan.
 </p><p>
 Similarly, to enable <i>PaToH support</i> (the PaToH library can be found
 <a href="http://bmi.osu.edu/~umit/software.html#patoh">here</a>),
-edit <tt>mondriaan.mk</tt> to change the line containing the <tt>PATOHHOMEDIR</tt> variable, as detailed above.
+edit <code>mondriaan.mk</code> to change the line containing the <code>PATOHHOMEDIR</code> variable, as detailed above.
 </p>
 
 <p>
@@ -105,35 +119,35 @@ for proper behaviour. The unit tests have been tried successfully on two
 architectures: Linux and Mac Os X. If one or more of the tests fails,
 please try to identify the relevant error message, and inform me
 (R . H . Bisseling @ NOSPAM uu . nl ) about the problem. I will try to help you 
-solve it. The tests called can be found in the script <tt>runtest</tt> 
-residing in the <tt>tests/</tt> subdirectory.
-To compile and run all 102 <em>unit tests</em>, type
+solve it. The tests called can be found in the script <code>runtest</code> 
+residing in the <code>tests/</code> subdirectory.
+To compile and run all 109 <em>unit tests</em>, type
 </p>
 <ul>
-<li><tt>% make test</tt></li>
+<li><code>% make test</code></li>
 </ul>
 
 <h4>Timers</h4>
-<p>The compile option <tt>-DTIME</tt> causes the CPU time used by Mondriaan 
+<p>The compile option <code>-DTIME</code> causes the CPU time used by Mondriaan 
 for the matrix distribution to be printed, and also the time for the vector
 distribution. This timer has a relatively low (guaranteed) accuracy, and
 it is in danger of clock wraparound.
 
-The compile option <tt>-DUNIX</tt> tells Mondriaan
+The compile option <code>-DUNIX</code> tells Mondriaan
 you are using a UNIX system.
-Together with <tt>-DTIME</tt> this also causes the elapsed (wall clock)
+Together with <code>-DTIME</code> this also causes the elapsed (wall clock)
 time to be printed, which is an upper bound on the CPU time.
 Usually the timer accuracy is higher, and there is no danger of clock
 wraparound. This is the best timer if you are the single user of the system.
 </p>
 
-<h4><a name="verb">Levels of verbosity</a></h4>
+<h4><a class="anchor" id="verb">Levels of verbosity</a></h4>
 <p>Mondriaan writes the output distributions to file.
 It can also generate useful statistics about the partitioning
 to the standard output stream stdout (or the screen).
 There are three levels of verbosity: silent, standard, and verbose.
 The silent mode is useful when running the unit tests by 
-<tt>make test</tt>. (If these are not done silently,
+<code>make test</code>. (If these are not done silently,
 the OKs are obscured.)
 The silent mode may also be useful if (parts of) Mondriaan are used as library
 functions.
@@ -142,102 +156,113 @@ a particular run in detail.
 The standard mode generates 1-2 pages of output, and 
 is aimed at easy digestion.
 You can change the verbosity level by commenting and uncommenting
-the appropriate <tt>CFLAGS</tt> lines in <tt>mondriaan.mk</tt>.
-Using the flag <tt>-DINFO</tt> generates standard output,
-and using the flags <tt>-DINFO -DINFO2</tt> generates verbose output.
+the appropriate <code>CFLAGS</code> lines in <code>mondriaan.mk</code>.
+Using the flag <code>-DINFO</code> generates standard output,
+and using the flags <code>-DINFO -DINFO2</code> generates verbose output.
 </p>
 
-<h3><a name="run">How to run Mondriaan</a></h3>
+<h3><a class="anchor" id="run">How to run Mondriaan</a></h3>
 <p>
-Go inside the directory <tt>Mondriaan4</tt> and type
+Go inside the directory <code>Mondriaan4</code> and type
 </p>
 <ul>
-<li><tt>% cd tools</tt></li>
-<li><tt>% ./Mondriaan ../tests/arc130.mtx 8 0.03</tt></li>
+<li><code>% cd tools</code></li>
+<li><code>% ./Mondriaan ../tests/arc130.mtx 8 0.03</code></li>
 </ul>
 <p>
-if you want to partition the <tt>arc130.mtx</tt> matrix (Matrix Market file format)
+if you want to partition the <code>arc130.mtx</code> matrix (Matrix Market file format)
 for 8 processors with at most 3% load imbalance. The matrix should be the full
-relative path; <em>in the above example output is saved in the Mondriaan tests folder</em> (<tt>../tests/</tt>).
+relative path; <em>in the above example output is saved in the Mondriaan tests folder</em> (<code>../tests/</code>).
 <br>
 Mondriaan may also be used to partition matrices provided via the standard
 input, e.g., by using piping:
 </p>
 <ul>
-<li><tt>% cat ../tests/arc130.mtx | ./Mondriaan - 8 0.03</tt></li>
+<li><code>% cat ../tests/arc130.mtx | ./Mondriaan - 8 0.03</code></li>
 </ul>
 <p>
 to obtain the same result as with the previous method, with one difference:
-results are written in the current (<tt>tools</tt>) directory.
+results are written in the current (<code>tools</code>) directory.
 For integration with already existing software you may have, without resorting to writing and reading files (or pipes),
 look at the <a href="#libr">how to use Mondriaan as a library</a> section of this guide.
 </p>
 
-<h3><a name="outp">Output</a></h3>
+<h3><a class="anchor" id="outp">Output</a></h3>
 
-<p>The main <tt>Mondriaan</tt> tool yields, after a successful run on an input matrix,
+<p>The main <code>Mondriaan</code> tool yields, after a successful run on an input matrix,
 various output files. All possible output files are described below. Typically,
 the output filenames are that of the input matrix filename, appended with a small
 descriptor and usually the number of parts <i>x</i> Mondriaan was requested to 
 construct.</p>
 
-<h4>Distributed matrix (<tt>-Px</tt>)</h4>
-<p> The <tt>Mondriaan</tt> program writes the distributed matrix to a file called
-<tt>input-Px</tt>,
-where <tt>input</tt> is the name of the input matrix, or <tt>stdin</tt> if the
+<div class="indent4">
+<h4>Distributed matrix (<code>-Px</code>)</h4>
+<p> The <code>Mondriaan</code> program writes the distributed matrix to a file called
+<code>input-Px</code>,
+where <code>input</code> is the name of the input matrix, or <code>stdin</code> if the
 matrix was read from the standard input, and x is the number of processors
 used in the distribution.
 
 We use an adapted Matrix Market format, with this structure: 
 <br>
-<tt>%%MatrixMarket distributed-matrix coordinate real general<br>
+<code>%%MatrixMarket distributed-matrix coordinate real general<br>
 m n nnz P<br>
-Pstart[0]</tt> ( this should be 0 )<br>
+Pstart[0]</code> ( this should be 0 )<br>
 ...<br>
 ...<br>
 ...<br>
-<tt>Pstart[P]</tt>( this should be nnz )<br>
-<tt>A.i[0] A.j[0] A.value[0]</tt>
+<code>Pstart[P]</code>( this should be nnz )<br>
+<code>A.i[0] A.j[0] A.value[0]</code>
 ...<br>
 ...<br>
 ...<br>
-<tt>A.i[nnz-1] A.j[nnz-1] A.value[nnz-1]</tt>
+<code>A.i[nnz-1] A.j[nnz-1] A.value[nnz-1]</code>
 <br>
-Here, <tt>Pstart[k]</tt> points to the start of the nonzeroes
+Here, <code>Pstart[k]</code> points to the start of the nonzeroes
 of processor k.
 </p>
+</div>
 
 
-
-<h4>Processor indices (<tt>-Ix</tt>)</h4>
-<p> The <tt>Mondriaan</tt> program also writes the processor indices of each
-nonzero to the Matrix Market file <tt>input-Ix</tt> where the value of each
+<div class="indent4">
+<h4>Processor indices (<code>-Ix</code>)</h4>
+<p> The <code>Mondriaan</code> program also writes the processor indices of each
+nonzero to the Matrix Market file <code>input-Ix</code> where the value of each
 nonzero is replaced by the processor index to which the nonzero has been assigned.
-The order of the nonzeroes is exactly that of the distributed matrix (<tt>-Px</tt>).
+The order of the nonzeroes is exactly that of the distributed matrix (<code>-Px</code>).
 </p>
+</div>
+
 
-<h4>Row and column permutations (<tt>-rowx</tt>, <tt>-colx</tt>)</h4>
-<p><tt>Mondriaan</tt> writes the row and column permutations determined by the
-Mondriaan algorithm (set by the <tt>Permute</tt> option) to <tt>input-rowx</tt>
-and <tt>input-colx</tt>. The goal of these permutations is to bring the input
+<div class="indent4">
+<h4>Row and column permutations (<code>-rowx</code>, <code>-colx</code>)</h4>
+<p><code>Mondriaan</code> writes the row and column permutations determined by the
+Mondriaan algorithm (set by the <code>Permute</code> option) to <code>input-rowx</code>
+and <code>input-colx</code>. The goal of these permutations is to bring the input
 matrix <i>A</i> into (doubly) Separated Block Diagonal (SBD) or Bordered Block 
 Diagonal (BBD) form, after applying the found permutations. This can have many 
 possible applications, including, but not limited to, cache-oblivious sparse 
 matrix vector multiplication (SBD form) or minimising fill-in in sparse LU 
-decomposition (BBD form). These files are not written if the <tt>Permute</tt> 
-option is set to <tt>none</tt>.
+decomposition (BBD form). These files are not written if the <code>Permute</code> 
+option is set to <code>none</code>.
 </p>
+</div>
+
 
-<h4>Reordered matrix (<tt>-reor-Px</tt>)</h4>
-<p><tt>Mondriaan</tt> also directly writes the permuted matrix <i>PAQ</i> to 
+<div class="indent4">
+<h4>Reordered matrix (<code>-reor-Px</code>)</h4>
+<p><code>Mondriaan</code> also directly writes the permuted matrix <i>PAQ</i> to 
 file, where the permutation matrix <i>P</i> corresponds to the row permutation 
 determined by the Mondriaan algorithm, as described in the previous paragraph. 
 The permutation matrix <i>Q</i> is similarly inferred from the column 
-permutation. This file is not written if the <tt>Permute</tt> option is set 
-to <tt>none</tt>.
+permutation. This file is not written if the <code>Permute</code> option is set 
+to <code>none</code>.
+</div>
 
+
+<div class="indent4">
 <h4>Separator boundary indices &amp; hierarchy 
-(<tt>-rowblocksx</tt>, <tt>-colblocksx</tt>)<a name="SBDoutput"></a></h4>
+(<code>-rowblocksx</code>, <code>-colblocksx</code>)<a id="SBDoutput"></a></h4>
 
 <p>These two files store two column-vectors each. The first column-vector 
 relates to the <em>separator boundary indices</em>, the second to the 
@@ -259,7 +284,7 @@ blocks</em>. The indices where these blocks start and end in the reordered
 matrix are given in these two files; this is done by consecutively listing
 the start index and end index of each block, both separator and 
 non-separator, as they occur in the row and column direction. These files 
-are not written if the <tt>Permute</tt> option is set to <tt>none</tt>.
+are not written if the <code>Permute</code> option is set to <code>none</code>.
 </p>
 
 <p>A more detailed explanation follows from the (idealised) Figures 1 and 2.
@@ -289,7 +314,7 @@ be viewed as a <em>child</em> of the largest separator blocks; in this
 way a binary tree of separator blocks can be defined. See Figure 3 for
 clarification.</p>
 
-<p>Each row of the <tt>rowblocks</tt> and <tt>columnblocks</tt> file 
+<p>Each row of the <code>rowblocks</code> and <code>columnblocks</code> file 
 corresponds to the start of a block (excluding the very last row which 
 always equals m+1, resp., n+1, where m by n is the matrix size). 
 Integers from 1 to m or 1 to n thus indicate blocks, and each separator 
@@ -305,20 +330,23 @@ array <em>(2,4,2,8,6,4,6,0,10,12,10,8,14,12,14)</em>.</p>
 
 <p>The rowblocks file in this instance would then look as follows (only
 partially shown):<br>
-<tt>
+<code>
 0 2<br>
 2 4<br>
 3 2<br>
 5 8<br>
 ...<br>
 21 14<br>
-23</tt></p>
+23</code></p>
+</div>
 
-<h4>Input/output vector distributions (<tt>-ux</tt>, <tt>-vx</tt>)</h4>
+
+<div class="indent4">
+<h4>Input/output vector distributions (<code>-ux</code>, <code>-vx</code>)</h4>
 
 <p>The program writes the processor numbers of the vector components
-to the files called <tt>input-ux</tt> and <tt>input-vx</tt>,
-where <tt>input</tt> is the name of the input matrix 
+to the files called <code>input-ux</code> and <code>input-vx</code>,
+where <code>input</code> is the name of the input matrix 
 and x is the number of processors used in the distribution.
 The vectors u and v are the output and input vectors
 of the sparse matrix-vector multiplication <i>u=A*v</i>.
@@ -330,19 +358,25 @@ following the Matrix Market conventions.
 In I/O, the processors are numbered 1 to P. Internally, the indices are 
 converted to the standard C-numbering starting from 0.
 </p>
+</div>
 
-<h4>Cartesian submatrices (<tt>-Cx</tt>)</h4>
+
+<div class="indent4">
+<h4>Cartesian submatrices (<code>-Cx</code>)</h4>
 <p>The program writes the row index sets I(q) 
 and column index sets J(q) of the Cartesian submatrix I(q) x J(q)
-for the processors q=1,...,P to the file called <tt>input-Cx</tt>,
-where <tt>input</tt> is the name of the input matrix 
+for the processors q=1,...,P to the file called <code>input-Cx</code>,
+where <code>input</code> is the name of the input matrix 
 and x is the number of processors used in the distribution.
 This file is additional information, useful e.g. for visualisation,
 and you may not need it.
 </p>
+</div>
 
+
+<div class="indent4">
 <h4>Statistics on standard output</h4>
-<p>Provided the library is compiled with the <tt>-DINFO</tt> or <tt>-DINFO2</tt> option,
+<p>Provided the library is compiled with the <code>-DINFO</code> or <code>-DINFO2</code> option,
 the program prints plenty of useful statistics to standard output.
 The <b>communication volume</b> is given for the two phases
 of the matrix-vector multiplication separately:
@@ -355,55 +389,59 @@ sent and the number of data words received, over all processors.
 This metric is also called the <b>BSP cost</b>; it is the cost
 metric of the <a href="http://www.bsp-worldwide.org/">Bulk Synchronous Parallel</a> model.
 </p>
+</div>
+
 
+<div class="indent4">
 <h4>Graphical output</h4>
-<p>In the <tt>tools/</tt> subdirectory the program <tt>MondriaanPlot</tt>
+<p>In the <code>tools/</code> subdirectory the program <code>MondriaanPlot</code>
 can be used to get insight in the Mondriaan partitioning algorithm.
 This program creates a series of Truevision TGA image files named
-<tt>img0000.tga</tt>, <tt>img0001.tga</tt>, ..., up to the number of
+<code>img0000.tga</code>, <code>img0001.tga</code>, ..., up to the number of
 processors over which the matrix is divided.
 To use this program, issue
 </p>
 <ul>
-<li><tt>% cd tools</tt></li>
-<li><tt>% ./MondriaanPlot ../tests/arc130.mtx 8 0.03</tt></li>
+<li><code>% cd tools</code></li>
+<li><code>% ./MondriaanPlot ../tests/arc130.mtx 8 0.03</code></li>
 </ul>
 <p>
-If you have <tt>mencoder</tt> installed, you can use the <tt>MondriaanMovie</tt>
+If you have <code>mencoder</code> installed, you can use the <code>MondriaanMovie</code>
 script to generate a small movie of the partitioning process:
 </p>
 <ul>
-<li><tt>% ./MondriaanMovie ../tests/arc130.mtx 8 0.03</tt></li>
+<li><code>% ./MondriaanMovie ../tests/arc130.mtx 8 0.03</code></li>
 </ul>
 <p>
-This will create <tt>../tests/arc130.mtx.avi</tt>.
-If you have <tt>imagemagick</tt> installed (or if the <tt>convert</tt> command is
-otherwise available), you can use <tt>MondriaanGIF</tt> to create an animated
+This will create <code>../tests/arc130.mtx.avi</code>.
+If you have <code>imagemagick</code> installed (or if the <code>convert</code> command is
+otherwise available), you can use <code>MondriaanGIF</code> to create an animated
 GIF of the partitioning process:
 </p>
 <ul>
-<li><tt>% ./MondriaanGIF ../tests/arc130.mtx 8 0.03</tt></li>
+<li><code>% ./MondriaanGIF ../tests/arc130.mtx 8 0.03</code></li>
 </ul>
 <p>
-This will create <tt>../tests/arc130.mtx.gif</tt>.
+This will create <code>../tests/arc130.mtx.gif</code>.
 </p>
 <p>
-When creating images with <tt>MondriaanPlot</tt>, also an SVG file is generated.
-Just as the <tt>.tga</tt> files, the <tt>.svg</tt> file also contains a visualisation of the partitioning.
-In the above examples, the svg file created will be <tt>../tests/arc130.mtx-8.svg</tt>.
+When creating images with <code>MondriaanPlot</code>, also an SVG file is generated.
+Just as the <code>.tga</code> files, the <code>.svg</code> file also contains a visualisation of the partitioning.
+In the above examples, the svg file created will be <code>../tests/arc130.mtx-8.svg</code>.
 </p>
+</div>
 
-<h3><a name="opts">Program options</a></h3>
+<h3><a class="anchor" id="opts">Program options</a></h3>
 <p>
 The Mondriaan options can be set in the
-<tt>Mondriaan.defaults</tt> file. If no such file exists, it is created
+<code>Mondriaan.defaults</code> file. If no such file exists, it is created
 at run time. Afterwards, this file can be edited for further runs.
 The default values of the options are given below in <b>boldface</b>. 
 It is possible to overrule the defaults from the command line,
 e.g. by typing
 </p>
 <ul>
-<li><tt>% ./Mondriaan ../tests/arc130.mtx 8 0.03 -SplitStrategy=onedimrow</tt></li>
+<li><code>% ./Mondriaan ../tests/arc130.mtx 8 0.03 -SplitStrategy=onedimrow</code></li>
 </ul>
 <p>
 you can force Mondriaan to split the matrix in one dimension only,
@@ -416,9 +454,9 @@ The nonnumerical options are used to choose partitioning methods.
 You may need to change them from the defaults to explore 
 different partitioning methods.
 </p>
-<ul>
-<li><tt>SplitStrategy</tt>: alternate, localbest, localratio, 
-onedimrow, onedimcol, finegrain, hybrid, symfinegrain, <b>mediumgrain</b><br>
+<ul class="options">
+<li><code class="option_name">SplitStrategy</code>: <code class="option_values">alternate, localbest, localratio, 
+onedimrow, onedimcol, finegrain, hybrid, symfinegrain, <b>mediumgrain</b></code><br>
 The main choice of strategy. Alternate forces alternating splits in row
 and column direction; localbest tries both directions and 
 chooses the best (this is also called the pure Mondriaan strategy);
@@ -444,89 +482,101 @@ Separated Block Diagonal, using symmetric finegrain, will always result in symme
 permutations. Structurally symmetric matrices can be handled as well, but requires 
 some deftness from the user: only the lower triangular part (including diagonal) 
 should be given to Mondriaan, and the resulting permutation should be manually 
-applied to the original matrix. The terminal program (<tt>tools/Mondriaan</tt>) 
+applied to the original matrix. The terminal program (<code>tools/Mondriaan</code>) 
 cannot perform this procedure automatically. The Matlab interface, in contrast, can.
-For symmetric permutations, see also the <tt>EnforceSymmetricPermutation</tt>
+For symmetric permutations, see also the <code>EnforceSymmetricPermutation</code>
 option.</li>
-<li><tt>Partitioner</tt>: <b>mondriaan</b>, patoh<br>
+<li><code class="option_name">Partitioner</code>: <code class="option_values"><b>mondriaan</b>, patoh</code><br>
 Permits the user to choose between the Mondriaan or PaToH hypergraph partitioner.
-Selecting <tt>patoh</tt> will use PaToH in a bipartitioning mode, effectively using
+Selecting <code>patoh</code> will use PaToH in a bipartitioning mode, effectively using
 it instead of the built-in hypergraph partitioner. This will also use the PaToH
 coarsening scheme instead of the one employed by Mondriaan.</li>
-<li><tt>Alternate_FirstDirection</tt>: row, col, <b>ratio</b><br>
+<li><code class="option_name">Alternate_<wbr/>FirstDirection</code>: <code class="option_values">row, col, <b>ratio</b></code><br>
 How to start the alternating strategy.</li>
-<li><tt>LoadbalanceStrategy</tt>: <b>constant</b>, increase, decrease<br>
+<li><code class="option_name">LoadbalanceStrategy</code>: <code class="option_values"><b>constant</b>, increase, decrease</code><br>
 Determines how to adjust the allowed imbalance epsilon for each split.</li>
-<li><tt>LoadbalanceAdjust</tt>: no, <b>yes</b><br>
+<li><code class="option_name">LoadbalanceAdjust</code>: <code class="option_values">no, <b>yes</b></code><br>
 Adjusting may change the number of processors assigned to each
 current part, to reflect the nonzero loads better.</li>
-<li><tt>SplitMethod</tt>: simple, <b>KLFM</b><br>
+<li><code class="option_name">SplitMethod</code>: <code class="option_values">simple, <b>KLFM</b></code><br>
 Simple is just meant for debugging, since it bypasses the sophisticated
 multilevel partitioning. It just partitions the nonzeroes into two nearly equal sets
 (in case of two processors) without looking at the communication costs incurred.</li>
-<li><tt>Metric</tt>: <b>lambda1</b>, cutnet, lambdalambda1<br>
+<li><code class="option_name">Metric</code>: <code class="option_values"><b>lambda1</b>, cutnet, lambdalambda1</code><br>
 Determines the metric with respect to which the communication volume is being
 minimised: the Lambda-minus-one, cut-net, and Lambda-times-Lambda-minus-one [<a href="#cite4">4</a>] metrics are available.
 For the Lambda-times-Lambda-minus-one metric, the PaToH bipartitioner must be used.
 The Lambda-minus-one metric is the default, since it precisely measures the communication volume of a parallel sparse matrix-vector multiplication.
 It is also called the connectivity metric.
 </li>
-<li><tt>DiscardFreeNets</tt>: <b>yes</b>, no<br>
+<li><code class="option_name">DiscardFreeNets</code>: <code class="option_values"><b>yes</b>, no</code><br>
 Discard nets (and vertices) of the hypergraph that have no influence on the communication volume (i.e. when they
-contain at most one nonzero, or when they are cut and <tt>Metric</tt> is cutnet).
+contain at most one nonzero, or when they are cut and <code>Metric</code> is cutnet).
 This option should be <b>enabled</b> whenever the cut-net metric is used.</li>
-<li><tt>SquareMatrix_DistributeVectorsEqual</tt>: <b>no</b>, yes<br>
+<li><code class="option_name">ZeroVolumeSearch</code>: <code class="option_values"><b>yes</b>, no</code><br>
+Before applying the simple or KLFM split method, first check whether a split is possible with zero communication.
+Whenever such a zero volume split is possible, this method is more likely to find it than the other methods, while at the same time being faster than most other methods.
+</li>
+<li><code class="option_name">ImproveFreeNonzeros</code>: <code class="option_values"><b>yes</b>, no</code><br>
+After a split is performed, improve the load balance by moving free nonzeros between parts.
+In this context, free nonzeros are nonzeros that may be moved between parts without increasing the communication volume.
+</li>
+<li><code class="option_name">CheckUpperBound</code>: <code class="option_values"><b>yes</b>, no</code><br>
+After finishing, check whether the computed partitioning has a communication volume of at most (min(m,n)+1)(P-1).
+If not, this option makes sure an alternative method is used that does produce a partitioning with volume at most (min(m,n)+1)(P-1).
+This option will only take effect if UseSingleEntry, dummies and column weights are not used.</li>
+<li><code class="option_name">SquareMatrix_<wbr/>DistributeVectorsEqual</code>: <code class="option_values"><b>no</b>, yes</code><br>
 If yes, force the distribution of the vectors u and v to be the same.</li>
-<li><tt>SquareMatrix_DistributeVectorsEqual_AddDummies</tt>: no, <b>yes</b><br>
+<li><code class="option_name">SquareMatrix_<wbr/>DistributeVectorsEqual_<wbr/>AddDummies</code>: <code class="option_values">no, <b>yes</b></code><br>
 If yes, and the vectors must be distributed the same, 
 add dummy nonzeroes for diagonal elements a(i,i) = 0.</li>
-<li><tt>SymmetricMatrix_UseSingleEntry</tt>: <b>no</b>, yes<br>
+<li><code class="option_name">SymmetricMatrix_<wbr/>UseSingleEntry</code>: <code class="option_values"><b>no</b>, yes</code><br>
 If yes, feed the lower triangular part of a symmetric matrix to Mondriaan,
 partition it, and then assign a(i,j) for i &lt; j to the same processor as a(j,i).
-If the <tt>SplitStrategy</tt> is symfinegrain, this option must be set to yes.</li>
-<li><tt>SymmetricMatrix_SingleEntryType</tt>: <b>lower</b>, random<br>
+If the <code>SplitStrategy</code> is symfinegrain, this option must be set to yes.</li>
+<li><code class="option_name">SymmetricMatrix_<wbr/>SingleEntryType</code>: <code class="option_values"><b>lower</b>, random</code><br>
 If random, and if the matrix is symmetric and a single entry is used,
 then choose either a(i,j) or a(j,i) randomly to be fed into Mondriaan.
-If the <tt>SplitStrategy</tt> is symfinegrain, this option must be set to lower.</li>
-<li><tt>Coarsening_MatchingStrategy</tt>: random, inproduct, <b>ata</b><br>
+If the <code>SplitStrategy</code> is symfinegrain, this option must be set to lower.</li>
+<li><code class="option_name">Coarsening_<wbr/>MatchingStrategy</code>: <code class="option_values">random, inproduct, <b>ata</b></code><br>
 Random causes matching of a random neighbouring column when merging
 columns in the multilevel coarsening. Inproduct takes the neighbouring column
 with the highest inner product. This works better, but is slower.
-ATA combines a specific graph matching algorithm with a specific neighbor finding algorithm in the hypergraph to generate matchings (see <tt>Coarsening_MatchingATAMatcher</tt> and <tt>Coarsening_MatchingATAFinder</tt>).</li>
-<li><tt>Coarsening_MatchingATAMatcher</tt>: greedy, <b>pga</b><br>
-If <tt>Coarsening_MatchingStrategy</tt> is set to ata, this is the used graph matching algorithm.
+ATA combines a specific graph matching algorithm with a specific neighbor finding algorithm in the hypergraph to generate matchings (see <code>Coarsening_MatchingATAMatcher</code> and <code>Coarsening_MatchingATAFinder</code>).</li>
+<li><code class="option_name">Coarsening_<wbr/>MatchingATAMatcher</code>: <code class="option_values">greedy, <b>pga</b></code><br>
+If <code>Coarsening_MatchingStrategy</code> is set to ata, this is the used graph matching algorithm.
 Greedy is the standard Mondriaan algorithm which greedily matches vertices in-order to their most heavy unmatched neighbors.
 PGA is the PGA' (1/2)-approximation algorithm developed by Drake and Hougardy, which offers higher quality matchings.
-<li><tt>Coarsening_MatchingATAFinder</tt>: <b>inproduct</b>, stairway<br>
-If <tt>Coarsening_MatchingStrategy</tt> is set to ata, this is the used hypergraph neighbor finding algorithm.
+<li><code class="option_name">Coarsening_<wbr/>MatchingATAFinder</code>: <code class="option_values"><b>inproduct</b>, stairway</code><br>
+If <code>Coarsening_<wbr/>MatchingStrategy</code> is set to ata, this is the used hypergraph neighbor finding algorithm.
 Inproduct always yields the neighbors of a given vertex with exact inner products.
 Stairway provides an efficient heuristic to approximate these inner products.
 It is much faster for matrices that have dense rows.
-<li><tt>Coarsening_InprodMatchingOrder</tt>: <b>decrwgt</b>, incrwgt, decrdeg,
-                                             incrdeg, natural, random<br>
+<li><code class="option_name">Coarsening_<wbr/>InprodMatchingOrder</code>: <code class="option_values"><b>decrwgt</b>, incrwgt, decrdeg,
+                                             incrdeg, natural, random</code><br>
 Determines the order in which columns are visited in the matching.
 This is either by decreasing column weight, increasing column weight, 
 increasing column degree,
 decreasing column degree, the natural given order, or a random order.
 The column weight represents the total number of nonzeroes merged into the column,
 whereas the degree represents the current sparsity pattern.</li>
-<li><tt>Coarsening_NetScaling</tt>: no, <b>linear</b><br>
+<li><code class="option_name">Coarsening_<wbr/>NetScaling</code>: <code class="option_values">no, <b>linear</b></code><br>
 Linear scaling gives each overlapping nonzero in the inner product matching
 a weight inversely proportional to the number of nonzeroes present in its row.</li>
-<li><tt>Coarsening_InprodScaling</tt>: no, cos, <b>min</b>, max, jaccard<br>
+<li><code class="option_name">Coarsening_<wbr/>InprodScaling</code>: <code class="option_values">no, cos, <b>min</b>, max, jaccard</code><br>
 Minimum scaling scales the inner product IP for the match between columns j0 and j1 
 by a factor 1/min(deg(j0, j1)). Maximum scaling uses 1/max(deg(j0, j1)).
 Cosine scaling uses 1/sqrt(min*max), which represents the cosine of the angle
 between the corresponding vectors.
 Jaccard uses 1/(min+max-IP), and is a metric often used in information retrieval.</li>
-<li><tt>Coarsening_MatchIdenticalFirst</tt>: no, <b>yes</b><br>
+<li><code class="option_name">Coarsening_<wbr/>MatchIdenticalFirst</code>: <code class="option_values">no, <b>yes</b></code><br>
 If yes, try to match identical columns first, before looking at inner products.</li>
-<li><tt>VectorPartition_Step3</tt>: increase, decrease, <b>random</b><br>
+<li><code class="option_name">VectorPartition_<wbr/>Step3</code>: <code class="option_values">increase, decrease, <b>random</b></code><br>
 Remainder from original Mondriaan vector distribution. 
 Determines the order in which matrix columns with at least 3 processors owning nonzeroes therein,
 are visited in Step 3 of the vector distribution algorithm (Algorithm 2 in the paper 
 by Vastenhouw and Bisseling, 2005).</li>
-<li><tt>Permute</tt>: <b>none</b>, reverseBBD, SBD, BBD<br>
+<li><code class="option_name">Permute</code>: <code class="option_values"><b>none</b>, reverseBBD, SBD, BBD</code><br>
 Generate row and column permutations of the matrix that is being processed, reflecting
 the partitioning process.
 The SBD or (reverse)BBD tags determine where the rows or columns are permuted if they 
@@ -541,32 +591,32 @@ The BBD reordering has applications in sparse LU (reduction of fill-in), whereas
 the SBD permutation is useful in cache-oblivious sparse matrix-vector multiplication;
 see e.g. [<a href="#cite1">1</a>], [<a href="#cite2">2</a>]. Many other applications 
 are expected to appear.</li>
-<li><tt>EnforceSymmetricPermutation</tt>: <b>no</b>, yes<br>
+<li><code class="option_name">EnforceSymmetricPermutation</code>: <code class="option_values"><b>no</b>, yes</code><br>
 Controls whether symmetric row and column permutations are generated; that is,
 P=Q<sup>T</sup>, where the reordered matrix is given by PAQ with A the input matrix.
-Note that A is permuted to PAQ only when the <tt>Permute</tt> option is set to something
-other than <tt>none</tt>. There are no restrictions on A: in particular, it need 
+Note that A is permuted to PAQ only when the <code>Permute</code> option is set to something
+other than <code>none</code>. There are no restrictions on A: in particular, it need 
 <em>not</em> be (structurally) symmetric. When A is symmetric, however, this option 
 ensures PAQ also is symmetric.<br>
 Much as with the symmetric finegrain option discussed in the section on the 
-<tt>SplitStrategy</tt> option, the quality of the separators degrades when symmetry is 
+<code>SplitStrategy</code> option, the quality of the separators degrades when symmetry is 
 forced: the separators will in most cases be wider than optimally required. E.g., the 
 only way an SBD permutation in one dimension (row-net, for instance) can be symmetric,
 is to introduce a vertical separator block in addition to the horizontal separator 
 already there.</li>
-<li><tt>Iterative_Refinement</tt>: never, <b>aftermg</b>, always<br>
+<li><code class="option_name">Iterative_<wbr/>Refinement</code>: <code class="option_values">never, <b>aftermg</b>, always</code><br>
 When to perform iterative refinement to improve a partitioning (see [<a href="#cite3">3</a>]).
 Iterative refinement slightly increases partitioning time, but leads to better results. If set 
 to never, iterative refinement is not performed. If set to aftermg, it is only performed if 
-<tt>SplitStrategy</tt> is set to mediumgrain. If set to always, iterative refinement is always 
+<code>SplitStrategy</code> is set to mediumgrain. If set to always, iterative refinement is always 
 performed after partitioning, which may yield two-dimensional partitionings even if a one-dimensional strategy is used.
 </li>
 </ul>
 
 <h4>Output options</h4>
-<ul>
+<ul class="options">
  <li>
-<tt>OutputFormat</tt>: <b>original</b>, emm<br>
+<code class="option_name">OutputFormat</code>: <code class="option_values"><b>original</b>, emm</code><br>
 Controls the output format of matrix and vector files. Original mode writes the files
 exactly as described in the previous section. The emm mode refers to the <em>Extended
  Matrix-Market</em> (EMM) format; this format is described  <a href="extendedMM.pdf">
@@ -577,13 +627,13 @@ which processor is now 1-based, as are most other values in the Matrix-Market fo
 Also, all files generated by Mondriaan are now preceded by an EMM banner; for example,
 previously, vector files would not possess such a header.
  </li><li>
-<tt>OutputMode</tt>: <b>original</b>, onefile, DIMACS<br>
+<code class="option_name">OutputMode</code>: <code class="option_values"><b>original</b>, onefile, DIMACS</code><br>
 Controls how file output is generated. Original mode causes Mondriaan to write a new
 file for each different object (distributed matrix, Cartesian matrix, vector 
 distributions, row-permutation, column permutation, et cetera). See also the 
 previous section on output files. The new Extended Matrix-Market format, however, 
 has support to store all these data in a single  file instead of many; to enable 
-this, select onefile as the <tt>OutputMode</tt>. This does require the OutputFormat 
+this, select onefile as the <code>OutputMode</code>. This does require the OutputFormat 
 to be set to EMM. See the <a href="extendedMM.pdf">pdf</a> file on the EMM format 
 for specifics on how all data is stored in a single file.
 The DIMACS output format will generate partitioning output file appropriate for the 10th DIMACS
@@ -594,68 +644,70 @@ challenge on graph partitioning and clustering.
 <h4>Numerical options</h4>
 <p>
 The numerical options are often used to optimise given partitioning methods.
-Setting values such as <tt>NrRestarts</tt>, 
-<tt>MaxNrLoops</tt>, or
-<tt>MaxNrNoGainMoves</tt> to a higher number, often results in better quality
+Setting values such as <code>NrRestarts</code>, 
+<code>MaxNrLoops</code>, or
+<code>MaxNrNoGainMoves</code> to a higher number, often results in better quality
 of the partitioning solution, at the expense of increased run-time.
 </p>
-<ul>
-<li><tt>Seed</tt>: <b>99</b><br>
+<ul class="options">
+<li><code class="option_name">Seed</code>: <code class="option_values"><b>99</b></code><br>
 Integer. Range &gt;= 0. Set the random seed. 
 You can also set Seed=random to set the seed depending on your system time.
-This can only be done on a UNIX system, compiled with the flag <tt>-DUNIX</tt>.
+This can only be done on a UNIX system, compiled with the flag <code>-DUNIX</code>.
 This is useful, for instance, if you want to obtain an average 
 communication volume over 10 runs, each time
 with a different seed.</li>
-<li><tt>Coarsening_NrVertices</tt>: <b>200</b><br>
+<li><code class="option_name">Coarsening_<wbr/>NrVertices</code>: <code class="option_values"><b>200</b></code><br>
 Integer. Range &gt;= 1. Recommended range : 100-500.
 Determines when to stop coarsening, as the current number
 of vertices is small enough.</li>
-<li><tt>Coarsening_MaxCoarsenings</tt>: <b>128</b><br>
+<li><code class="option_name">Coarsening_<wbr/>MaxCoarsenings</code>: <code class="option_values"><b>128</b></code><br>
 The maximum number of graph coarsenings that may be performed by Mondriaan.</li>
-<li><tt>Coarsening_NrMatchArbitrary</tt>: <b>0</b><br>
+<li><code class="option_name">Coarsening_<wbr/>NrMatchArbitrary</code>: <code class="option_values"><b>0</b></code><br>
 The number of matching levels that should be performed arbitrarily during coarsening (i.e. irrespective of the actual inner products between vertices).
 Set to 0 to disable.</li>
-<li><tt>Coarsening_MaxNrVtxInMatch</tt>: <b>2</b><br>
+<li><code class="option_name">Coarsening_<wbr/>MaxNrVtxInMatch</code>: <code class="option_values"><b>2</b></code><br>
 Integer. Range &gt;= 2. Recommended range : 2-10.
-The default value of 2 represents pairwise matching.</li>
-<li><tt>Coarsening_StopRatio</tt>: <b>0.05</b><br>
+The default value of 2 represents pairwise matching.
+When using PGA as ATA matcher, you can also set MaxNrVtxInMatch=log or -1, in which case MaxNrVtxInMatch will equal either 2, 3 or 4,
+given by the formula MaxNrVtxInMatch = floor(log2(floor(NrVerticesInGraph / Coarsening_NrVertices))), clamped between 2 and 4.</li>
+<li><code class="option_name">Coarsening_<wbr/>StopRatio</code>: <code class="option_values"><b>0.05</b></code><br>
 Float. Range = [0,1]. 
 The contraction ratio is defined as: [NrVtx(old)-NrVtx(new)] / NrVtx(old). 
 Stop coarsening if the ratio drops below the stopping value.</li>
-<li><tt>Coarsening_VtxMaxFractionOfWeight</tt>: <b>0.2</b><br>
+<li><code class="option_name">Coarsening_<wbr/>VtxMaxFractionOfWeight</code>: <code class="option_values"><b>0.2</b></code><br>
 Float. Range = (0,1]. To ensure load balance : fraction &lt; 0.5.
 This parameter is set to prevent matching all vertices into one huge vertex.</li>
-<li><tt>Coarsening_FineSwitchLevel</tt>: <b>2</b><br>
+<li><code class="option_name">Coarsening_<wbr/>FineSwitchLevel</code>: <code class="option_values"><b>2</b></code><br>
 If a finegrain split is performed (either in the finegrain, symfinegrain,
 or hybrid strategy) 
 we use a specialised, faster matching function for the first <b>2</b> splitting
 levels of the graph coarsening to improve performance.
 This value can be set to <b>0</b> to disable this optimisation.</li>
-<li><tt>KLFM_InitPart_NrRestarts</tt>: <b>8</b><br>
+<li><code class="option_name">KLFM_InitPart_<wbr/>NrRestarts</code>: <code class="option_values"><b>8</b></code><br>
 Integer. Range &gt;= 1. 
 Number of times the Kernighan-Lin Fiduccia-Mathheyses algorithm is run,
 each time with a different initial partitioning.</li>
-<li><tt>KLFM_InitPart_MaxNrLoops</tt>: <b>25</b><br>
+<li><code class="option_name">KLFM_InitPart_<wbr/>MaxNrLoops</code>: <code class="option_values"><b>25</b></code><br>
 Integer. Range &gt;= 1. 
 Maximum number of loops within one run.</li>
-<li><tt>KLFM_InitPart_MaxNrNoGainMoves</tt>: <b>200</b><br>
+<li><code class="option_name">KLFM_InitPart_<wbr/>MaxNrNoGainMoves</code>: <code class="option_values"><b>200</b></code><br>
 Integer. Range &gt;= 0. 
 Maximum number of successive no-gain moves allowed in one loop of a KLFM run.</li>
-<li><tt>KLFM_Refine_MaxNrLoops</tt>: <b>25</b><br>
+<li><code class="option_name">KLFM_Refine_<wbr/>MaxNrLoops</code>: <code class="option_values"><b>25</b></code><br>
 Integer. Range &gt;= 1. 
 Maximum number of loops within the refinement run of KLFM.</li>
-<li><tt>KLFM_Refine_MaxNrNoGainMoves</tt>: <b>200</b><br>
+<li><code class="option_name">KLFM_Refine_<wbr/>MaxNrNoGainMoves</code>: <code class="option_values"><b>200</b></code><br>
 Integer. Range &gt;= 0. 
 Maximum number of successive no-gain moves allowed in one loop of 
 the refinement run of KLFM.</li>
-<li><tt>VectorPartition_MaxNrLoops</tt>: <b>10</b><br>
+<li><code class="option_name">VectorPartition_<wbr/>MaxNrLoops</code>: <code class="option_values"><b>10</b></code><br>
 Integer. Range &gt;= 1. 
 Number of times a vector partitioning is tried.
 Each time, the matrix columns are randomly reordered on input.
 Vector partitioning is much cheaper than matrix partitioning,
 so trying this several times is justified.</li>
-<li><tt>VectorPartition_MaxNrGreedyImproves</tt>: <b>10</b><br>
+<li><code class="option_name">VectorPartition_<wbr/>MaxNrGreedyImproves</code>: <code class="option_values"><b>10</b></code><br>
 Integer. Range &gt;= 0.
 Each vector partitioning can be improved by a very cheap
 greedy improvement procedure (described in [<a href="#cite6">6</a>]).
@@ -663,126 +715,114 @@ MaxNrGreedyImproves is the number of times this is done
 for each vector partitioning.</li>
 </ul>
 
-<h3><a name="libr">How to use Mondriaan as a library</a></h3>
+<h3><a class="anchor" id="libr">How to use Mondriaan as a library</a></h3>
 
 <p>
-After successful compilation (using <tt>make</tt>),
-all relevant header files are exported to the <tt>src/include</tt> directory,
-and a static library is available in the <tt>src/lib</tt> directory.
+After successful compilation (using <code>make</code>),
+all relevant header files are exported to the <code>src/include</code> directory,
+and a static library is available in the <code>src/lib</code> directory.
 </p>
 <p>
 To illustrate the use of Mondriaan we provide a small <a href="example.c">example program</a> together with this guide.
 This example can be compiled (provided the Mondriaan library was successfully built) by executing
 </p>
 <ul>
-<li><tt>% gcc example.c -I../src/include -L../src/lib -lMondriaan4 -lm</tt></li>
+<li><code>% gcc example.c -I../src/include -L../src/lib -lMondriaan4 -lm</code></li>
 </ul>
 <p>
-in the <tt>docs/</tt> directory which will generate the executable <tt>a.out</tt>.
-More advanced use of the library is illustrated in <tt>tools/Mondriaan.c</tt>.
+in the <code>docs/</code> directory which will generate the executable <code>a.out</code>.
+More advanced use of the library is illustrated in <code>tools/Mondriaan.c</code>.
 </p>
 <p>
 Mondriaan uses a triplet-based datastructure (the Matrix Market format) to store sparse matrices;
 that is, for each nonzero, its row- and column-index are stored, as well as its numerical value (in general).
-The exact representation is detailed in <tt>src/SparseMatrix.c</tt>/<tt>.h</tt>; and to successfully interface,
+The exact representation is detailed in <code>src/SparseMatrix.c</code>/<code>.h</code>; and to successfully interface,
 your sparse matrix scheme has to be translated into this format.
 </p>
 <p>
 Since the Compressed Row Storage (CRS), or alternatively known as Compressed Sparse Row (CSR), is a more prevailing standard,
-<tt>SparseMatrix</tt> comes with a translation function specifically for that datastructure: <tt>CRSSparseMatrixInit</tt>.
-It takes as input an uninitialised Mondriaan <tt>SparseMatrix</tt> struct, the matrix dimensions and number of nonzeroes, and 
-the CRS datastructure arrays. The <tt>base</tt> parameter automatically translates from, e.g., 1-based arrays (base=1) as
+<code>SparseMatrix</code> comes with a translation function specifically for that datastructure: <code>CRSSparseMatrixInit</code>.
+It takes as input an uninitialised Mondriaan <code>SparseMatrix</code> struct, the matrix dimensions and number of nonzeroes, and 
+the CRS datastructure arrays. The <code>base</code> parameter automatically translates from, e.g., 1-based arrays (base=1) as
 they may appear in for example Fortran, to 0-based arrays as used in Mondriaan.
 </p>
 <p>
-Next is setting any options for Mondriaan. Recommended is to use an options file, storing all the defaults tuned to your application (e.g. as provided by <tt>tools/Mondriaan.defaults</tt>).
-These defaults can be read from file and stored in the Mondriaan <tt>Options</tt> struct by using the function <tt>SetOptionsFromFile</tt>;
-see <tt>src/Options.c</tt>/<tt>.h</tt>.
+Next is setting any options for Mondriaan. Recommended is to use an options file, storing all the defaults tuned to your application (e.g. as provided by <code>tools/Mondriaan.defaults</code>).
+These defaults can be read from file and stored in the Mondriaan <code>Options</code> struct by using the function <code>SetOptionsFromFile</code>;
+see <code>src/Options.c</code>/<code>.h</code>.
 </p><p>
-Once both the sparse matrix and the options are ready the main Mondriaan distribution function, <tt>DistributeMatrixMondriaan</tt>, can be used.
+Once both the sparse matrix and the options are ready the main Mondriaan distribution function, <code>DistributeMatrixMondriaan</code>, can be used.
 This function takes as parameters the sparse matrix struct, the number of processors, the load imbalance parameter, and the options struct.
-The callback function is for advanced use, and is usually kept a <tt>NULL</tt> pointer (an example of using the callback is given in <tt>tools/MondriaanPlot.c</tt>). This function is a blocking call.
-After partitioning, all relevant information can be extracted from the <tt>SparseMatrix</tt> struct (see the source code for details).
-The struct can be freed by calling <tt>MMDeleteSparseMatrix</tt>.
+The callback function is for advanced use, and is usually kept a <code>NULL</code> pointer (an example of using the callback is given in <code>tools/MondriaanPlot.c</code>). This function is a blocking call.
+After partitioning, all relevant information can be extracted from the <code>SparseMatrix</code> struct (see the source code for details).
+The struct can be freed by calling <code>MMDeleteSparseMatrix</code>.
 </p><p>
-Interfacing with non-C code is best achieved by writing a custom interface between your code and Mondriaan, using <tt>extern "C"</tt>-style additions when, e.g., interfacing between C++ and C; or using any other specific translation required (e.g., writing all parameters as pointers (Fortran), using <tt>jni</tt> (Java), etc.).
-As an example, the MATLAB interface is given in <tt>tools/MatlabMondriaan.c</tt>.
+Interfacing with non-C code is best achieved by writing a custom interface between your code and Mondriaan, using <code>extern "C"</code>-style additions when, e.g., interfacing between C++ and C; or using any other specific translation required (e.g., writing all parameters as pointers (Fortran), using <code>jni</code> (Java), etc.).
+As an example, the MATLAB interface is given in <code>tools/MatlabMondriaan.c</code>.
 </p>
 
-<h3><a name="matl">Using Mondriaan in MATLAB</a></h3>
-
-<p>
-For more information about MATLAB usage, please see the <a href="MATLAB.html">Mondriaan MATLAB guide</a>.
-</p>
-
-<h3><a name="hyper">Partitioning hypergraphs using Mondriaan</a></h3>
-
-<p>
-For more information about hypergraph partitioning, please see the <a href="HYPERGRAPH.html">Mondriaan hypergraph guide</a>.
-</p>
-
-<h3><a name="prof">Profiling Mondriaan</a></h3>
+<h3><a class="anchor" id="prof">Profiling Mondriaan</a></h3>
 <p>To profile the Mondriaan library for various configurations, please use
-the <tt>Profile</tt> program and <tt>RunProfiler</tt> script both included
-in the <tt>tools/</tt> directory.
-Entering the <tt>tools/</tt> directory and executing</p>
+the <code>Profile</code> program and <code>RunProfiler</code> script both included
+in the <code>tools/</code> directory.
+Entering the <code>tools/</code> directory and executing</p>
 <ul>
-<li><tt>% ./Profile 8 0.03 10 a.mtx b.mtx c.mtx &gt; out.tex</tt></li>
+<li><code>% ./Profile 8 0.03 10 a.mtx b.mtx c.mtx &gt; out.tex</code></li>
 </ul>
 <p>
-partitions the matrices <tt>a.mtx</tt>, <tt>b.mtx</tt>, and <tt>c.mtx</tt>
+partitions the matrices <code>a.mtx</code>, <code>b.mtx</code>, and <code>c.mtx</code>
 among 8 processors with at most 3% imbalance, taking the average over 10
 runs with a different random seed.
-The results are written, via <tt>stdout</tt>, to the LaTeX file <tt>out.tex</tt>
+The results are written, via <code>stdout</code>, to the LaTeX file <code>out.tex</code>
 which then contains a table with the averaged results of the partitioning
 process.</p>
 
-<h3><a name="dev">For code developers</a></h3>
+<h3><a class="anchor" id="dev">For code developers</a></h3>
 <p>
 If you develop new code to <b>add to Mondriaan</b>, you probably would like to add possibilities
 to use your code through a new option. In that case you need to adjust a few files:
 </p>
 <ul>
-<li><tt>src/Options.h</tt>: you should add the option and its possible values.
-<li><tt>src/Options.c</tt>: you should make a choice for the default value
-and set it in the function <tt>SetDefaultOptions</tt>. 
+<li><code>src/Options.h</code>: you should add the option and its possible values.
+<li><code>src/Options.c</code>: you should make a choice for the default value
+and set it in the function <code>SetDefaultOptions</code>. 
 If the option is a numerical option,
-you should check its range in the function <tt>SetDefaultOptions</tt>.
+you should check its range in the function <code>SetDefaultOptions</code>.
 Finally, you should add the option and all its possible values
-as an if-statement in the function <tt>SetOption</tt>. 
-<li>subdirectory <tt>tests</tt>: it may happen that adding the options
+as an if-statement in the function <code>SetOption</code>. 
+<li>subdirectory <code>tests</code>: it may happen that adding the options
 will break a few unit tests, in particular those connected to functions
-<tt>GetParameters</tt>, <tt>SetDefaultOptions</tt>, <tt>SetOption</tt>.
+<code>GetParameters</code>, <code>SetDefaultOptions</code>, <code>SetOption</code>.
 This is quite harmless, and easy to repair.
 </ul>
 
-<h3><a name="moar">More information</a></h3>
+<h3><a class="anchor" id="moar">More information</a></h3>
 <p>
-All functions of Mondriaan have extensive documentation in the source code (<tt>.c</tt> files).
+All functions of Mondriaan have extensive documentation in the source code (<code>.c</code> files).
 Please have a look there for more details.
 </p>
 
 <h3>References</h3>
 <p>
-[<a name="cite1" href="http://www.staff.science.uu.nl/~bisse101/Mondriaan/yzelman09.pdf">1</a>]
+[<a id="cite1" href="http://www.staff.science.uu.nl/~bisse101/Mondriaan/yzelman09.pdf">1</a>]
 <em>Cache-oblivious sparse matrix-vector multiplication by using sparse matrix partitioning methods</em>,
 A. N. Yzelman and Rob H. Bisseling, SIAM Journal of Scientific Computation, Vol. 31, Issue 4, pp. 3128-3154 (2009).<br>
-[<a name="cite2" href="http://www.sciencedirect.com/science/article/pii/S0167819111001062">2</a>]
+[<a id="cite2" href="http://www.sciencedirect.com/science/article/pii/S0167819111001062">2</a>]
 <em>Two-dimensional cache-oblivious sparse matrix-vector multiplication</em>,
 A. N. Yzelman and Rob H. Bisseling, Parallel Computing, Vol. 37, Issue 12, pp. 806-819 (2011).<br>
-[<a name="cite3" href="">3</a>]
+[<a id="cite3" href="">3</a>]
 <em>A medium-grain method for fast 2D bipartitioning of sparse matrices</em>,
 Dani&euml;l M. Pelt and Rob H. Bisseling, submitted for publication (2013).<br>
-[<a name="cite4" href="http://www.sciencedirect.com/science/article/pii/S0167819113000690">4</a>]
+[<a id="cite4" href="http://www.sciencedirect.com/science/article/pii/S0167819113000690">4</a>]
 <em>A new metric enabling an exact hypergraph model for the communication volume in distributed-memory parallel applications</em>
 O. Fortmeier, H. M. B&uuml;cker, B. O. Fagginger Auer, R. H. Bisseling, Parallel Computing, Vol. 39, Issue 8, pp. 319-335 (2013).<br>
-[<a name="cite5" href="http://dl.acm.org/citation.cfm?id=663255">5</a>]
+[<a id="cite5" href="http://dl.acm.org/citation.cfm?id=663255">5</a>]
 <em>A Fine-Grain Hypergraph Model for 2D Decomposition of Sparse Matrices</em>
 &Uuml;mit V. &Ccedil;ataly&uuml;rek and Cevdet Aykanat, IPDPS 2001, p. 118 (2001).<br>
-[<a name="cite6" href="http://emis.library.cornell.edu/journals/ETNA/vol.21.2005/index.html">6</a>]
+[<a id="cite6" href="http://emis.library.cornell.edu/journals/ETNA/vol.21.2005/index.html">6</a>]
 <em>Communication balancing in parallel sparse matrix-vector multiplication</em>
 Rob H. Bisseling and Wouter Meesen, ETNA, pp. 47-65 (2005).<br>
-[<a name="cite7" href="http://www.crcnetbase.com/doi/abs/10.1201/b11644-13">7</a>]
+[<a id="cite7" href="http://www.crcnetbase.com/doi/abs/10.1201/b11644-13">7</a>]
 <em>Two-Dimensional Approaches to Sparse Matrix Partitioning</em>
 Rob H. Bisseling, Bas O. Fagginger Auer, A. N. Yzelman, Tristan van Leeuwen and &Uuml;mit V. &Ccedil;ataly&uuml;rek,
 Chapter 12 of Combinatorial Scientific Computing, Chapman and Hall/CRC, pp. 321-349 (2012).<br>
@@ -811,6 +851,7 @@ the Mondriaan package home page</a>.</p>
 </p>
 
 <hr>
+</div>
 
 </body>
 
diff --git a/docs/USERS_GUIDE_OPT.html b/docs/USERS_GUIDE_OPT.html
index 58d693915a2876dacfd5709a359c88b7fc782298..c285c5f0905fc2cf0e27b529ec9f68998c11ceaf 100644
--- a/docs/USERS_GUIDE_OPT.html
+++ b/docs/USERS_GUIDE_OPT.html
@@ -4,25 +4,40 @@
 <html>
 
 <head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 <link href="style.css" rel="stylesheet" type="text/css">
+<link href="print.css" rel="stylesheet" type="text/css" media="print">
+<script type="text/javascript" src="script.js"></script>
+
 <title>User's guide MondriaanOpt</title>
 </head>
 
 <body>
 
+<div id="mainContainer">
+
+<div id="pageNav">
+	<div><a href="USERS_GUIDE.html">Mondriaan</a></div>
+	<div><a href="MATLAB.html">MATLAB</a></div>
+	<div><a href="HYPERGRAPH.html">Hypergraphs</a></div>
+</div>
+
 <h2>User's guide MondriaanOpt</h2>
 
-<div id="top">
-<div><a href="USERS_GUIDE.html">&laquo; Mondriaan</a></div>
-<div><a href="#inst">Installing</a></div>
-<div><a href="#outp">Output</a></div>
-<div><a href="#opts">Options</a></div>
-<div><a href="#matl">MATLAB</a></div>
+<div id="menuPos"></div>
+<div id="menu">
+	<div id="menuItems">
+		<div><a href="#inst">Installing</a></div>
+		<div><a href="#outp">Output</a></div>
+		<div><a href="#opts">Options</a></div>
+		<div><a href="#constraint">Imbalance constraint</a></div>
+		<div><a href="#matl">MATLAB</a></div>
+	</div>
 </div>
 
 <hr>
-<p>
+<p class="updateNote">
 This page is continuously being improved and updated;
 therefore, a more recent version may be obtained 
 <a href="http://www.staff.science.uu.nl/~bisse101/Mondriaan/Docs/USERS_GUIDE_OPT.html">
@@ -33,25 +48,27 @@ This offline version is bundled with the software for your convenience.
 
 <p>
 Whereas Mondriaan uses heuristics to obtain good partitionings for sparse matrix-vector multiplication for any number of processors,
-MondriaanOpt will calculate an actual optimal solution for this partitioning problem with 2 processors. More precisely, it will
-calculate a partitioning with minimum volume among all solutions that obey the imbalance constraint.
+MondriaanOpt calculates an optimal solution to this partitioning problem with 2 processors. More precisely, it 
+calculates a partitioning with minimum communication volume among all solutions that obey the imbalance constraint.
 </p>
 
 <p>
 A database with already solved problems with use of MondriaanOpt can be found <a href="http://www.staff.science.uu.nl/~bisse101/Mondriaan/Opt/">online</a>.
 </p>
 
-<h3><a name="inst">How to install MondriaanOpt</a></h3>
+<h3><a class="anchor" id="inst">How to install MondriaanOpt</a></h3>
 <p>
 MondriaanOpt comes packaged with the Mondriaan software. Refer to <a href="./USERS_GUIDE.html">this page</a> for
 instructions on using Mondriaan. MondriaanOpt is automatically compiled when you compile Mondriaan. The executable
-is then available at <tt>tools/MondriaanOpt</tt>.
+is then available at <code>tools/MondriaanOpt</code>.
 </p>
 
-<h3><a name="run">How to run MondriaanOpt</a></h3>
+<h3><a class="anchor" id="run">How to run MondriaanOpt</a></h3>
 <p>
 The MondriaanOpt program has the following interface:
-<ul><li><tt>% ./tools/MondriaanOpt matrix [P [eps]] [options]</tt></li></ul>
+</p>
+<ul><li><code>% ./tools/MondriaanOpt matrix [P [eps]] [options]</code></li></ul>
+<p>
 One, two or three parameters may be passed, after which further options may be given.
 Either [eps], -e or -k must be passed, and it is advised to pass -v (see <a href="#opts">options</a>).
 Take note that while MondriaanOpt may be called with the same parameters as Mondriaan, the actual problem
@@ -62,21 +79,21 @@ being solved may be <a href="#constraint">slightly different</a>.
 Some equivalent examples are:
 </p>
 <ul>
-	<li><tt>% ./tools/MondriaanOpt tests/arc130.mtx 2 0.03 -v 17</tt></li>
-	<li><tt>% ./tools/MondriaanOpt tests/arc130.mtx -e 0.03 -v 17</tt></li>
-	<li><tt>% ./tools/MondriaanOpt tests/arc130.mtx -k 660 -v 17</tt></li>
+	<li><code>% ./tools/MondriaanOpt tests/arc130.mtx 2 0.03 -v 17</code></li>
+	<li><code>% ./tools/MondriaanOpt tests/arc130.mtx -e 0.03 -v 17</code></li>
+	<li><code>% ./tools/MondriaanOpt tests/arc130.mtx -k 660 -v 17</code></li>
 </ul>
 
 <p>
-The above examples partition the <tt>arc130.mtx</tt> matrix (Matrix Market file format)
+The above examples partition the <code>arc130.mtx</code> matrix (Matrix Market file format)
 for 2 processors with at most 3% load imbalance, knowing that solutions must exist with
 volume at most 17. The matrix should be the full relative path; <em>in the above example 
-output is saved in the Mondriaan tests folder</em> (<tt>../tests/</tt>).
+output is saved in the Mondriaan tests folder</em> (<code>../tests/</code>).
 </p>
 
-<h3><a name="outp">Output</a></h3>
+<h3><a class="anchor" id="outp">Output</a></h3>
 
-<p>The <tt>MondriaanOpt</tt> tool yields, after a successful run on an input matrix,
+<p>The <code>MondriaanOpt</code> tool yields, after a successful run on an input matrix,
 various output files. All possible output files are described below. Typically,
 the output filenames are that of the input matrix filename, modified with a small
 descriptor and the number of parts <i>(=2)</i>.</p>
@@ -88,109 +105,132 @@ All free nonzeros will be assigned index 3.
 (Free nonzeros are nonzeros that are not assigned to a processor because assigning
 it to either one will not influence communication volume.)
 To stress the potential presence of free nonzeros, the number of processors (2) in the
-filename is followed by a suffix <tt>f</tt>.
+filename is followed by a suffix <code>f</code>.
 </i></p>
 
-<h4>Processor indices (<tt>-I2f</tt>)</h4>
-<p> The <tt>MondriaanOpt</tt> program writes the processor indices of each
-nonzero to the Matrix Market file <tt>input-2f.mtx</tt> where the value of each
+<div class="indent4">
+<h4>Processor indices (<code>-I2f</code>)</h4>
+<p> The <code>MondriaanOpt</code> program writes the processor indices of each
+nonzero to the Matrix Market file <code>input-2f.mtx</code> where the value of each
 nonzero is replaced by the processor index to which the nonzero has been assigned.
 </p>
+</div>
 
-<h4>Graphical output (<tt>-2f.svg</tt>)</h4>
-<p>If the option <tt>-svg</tt> is given, at the end of the algorithm an SVG graphic is written to the file <tt>input-2f.svg</tt>,
+<div class="indent4">
+<h4>Graphical output (<code>-2f.svg</code>)</h4>
+<p>If the option <code>-svg</code> is given, at the end of the algorithm an SVG graphic is written to the file <code>input-2f.svg</code>,
 containing a visualisation of the partitioning.
 </p>
+</div>
 
 <h4><u>Formats without free nonzeros</u></h4>
 <p><i>
 Here, the free nonzeros of a partitioning are distributed among the two processors in
 such a way that load imbalance is kept at a minimum.
-Note that whenever we write <tt>P</tt> for the number of processors below, it implicitly equals 2.
+Note that whenever we write <code>P</code> for the number of processors below, it implicitly equals 2.
 </i></p>
-<h4>Distributed matrix (<tt>-P2</tt>)</h4>
-<p> The <tt>MondriaanOpt</tt> program
-writes the distributed matrix to a file called <tt>input-P2</tt>,
-where <tt>input</tt> is the name of the input matrix.
+
+<div class="indent4">
+<h4>Distributed matrix (<code>-P2</code>)</h4>
+<p> The <code>MondriaanOpt</code> program
+writes the distributed matrix to a file called <code>input-P2</code>,
+where <code>input</code> is the name of the input matrix.
 
 We use an adapted Matrix Market format, with this structure: 
 <br>
-<tt>%%MatrixMarket distributed-matrix coordinate real general<br>
+<code>%%MatrixMarket distributed-matrix coordinate real general<br>
 m n nnz P<br>
-Pstart[0]</tt> ( this should be 0 )<br>
+Pstart[0]</code> ( this should be 0 )<br>
 ...<br>
 ...<br>
 ...<br>
-<tt>Pstart[P]</tt>( this should be nnz )<br>
-<tt>A.i[0] A.j[0] A.value[0]</tt>
+<code>Pstart[P]</code>( this should be nnz )<br>
+<code>A.i[0] A.j[0] A.value[0]</code>
 ...<br>
 ...<br>
 ...<br>
-<tt>A.i[nnz-1] A.j[nnz-1] A.value[nnz-1]</tt>
+<code>A.i[nnz-1] A.j[nnz-1] A.value[nnz-1]</code>
 <br>
-Here, <tt>Pstart[k]</tt> points to the start of the nonzeroes
+Here, <code>Pstart[k]</code> points to the start of the nonzeroes
 of processor k.
 </p>
+</div>
 
-<h4>Processor indices (<tt>-I2</tt>)</h4>
-<p> The <tt>MondriaanOpt</tt> program
-also writes the processor indices of each nonzero to the Matrix Market file <tt>input-I2</tt>
+<div class="indent4">
+<h4>Processor indices (<code>-I2</code>)</h4>
+<p> The <code>MondriaanOpt</code> program
+also writes the processor indices of each nonzero to the Matrix Market file <code>input-I2</code>
 where the value of each nonzero is replaced by the processor index to which
-the nonzero has been assigned. The order of the nonzeroes is exactly that of the distributed matrix (<tt>-P2</tt>).
+the nonzero has been assigned. The order of the nonzeroes is exactly that of the distributed matrix (<code>-P2</code>).
 </p>
+</div>
 
-<h4>Cartesian submatrices (<tt>-C2</tt>)</h4>
+<div class="indent4">
+<h4>Cartesian submatrices (<code>-C2</code>)</h4>
 <p> The program writes the row index sets I(q) 
 and column index sets J(q) of the Cartesian submatrix I(q) x J(q)
-for the processors q=1,...,P to the file called <tt>input-C2</tt>.
+for the processors q=1,...,P to the file called <code>input-C2</code>.
 This file is additional information, useful e.g. for visualisation,
 and you may not need it.
 </p>
+</div>
 
-<h4>Graphical output (<tt>-2.svg</tt>)</h4>
-<p>If the option <tt>-svg</tt> is given, at the end of the algorithm an SVG graphic is written to the file <tt>input-2.svg</tt>,
+<div class="indent4">
+<h4>Graphical output (<code>-2.svg</code>)</h4>
+<p>If the option <code>-svg</code> is given, at the end of the algorithm an SVG graphic is written to the file <code>input-2.svg</code>,
 containing a visualisation of the partitioning.
 </p>
+</div>
 
-<h4><u>Output to <tt>stdout</tt>/<tt>stderr</tt></u></h4>
+<h4><u>Output to <code>stdout</code>/<code>stderr</code></u></h4>
 <p>
-In a succesful run, at the end of execution general statistics are written to <tt>stdout</tt>.
-Also, during such a run, every <tt>2^23 = 8388608</tt> iterations the current depth in the tree is written to <tt>stderr</tt>, in the format <tt>`current depth`/`maximum depth`</tt>.
-Last but not least, every time a new solution is found which improves on the previous solution regarding total volume, a message is written to <tt>stderr</tt> reporting the newly found volume and load distribution in the format <tt>`load P0`, `load P1`, `load Free`</tt>.
+In a successful run, at the end of execution general statistics are written to <code>stdout</code>.
+Also, during such a run, every <code>2^23 = 8388608</code> iterations the current depth in the tree is written to <code>stderr</code>, in the format <code>`current depth`/`maximum depth`</code>.
+Last but not least, every time a new solution is found which improves on the previous solution regarding total volume, a message is written to <code>stderr</code> reporting the newly found volume and load distribution in the format <code>`load P0`, `load P1`, `load Free`</code>.
 </p>
 
-<h3><a name="opts">Program options</a></h3>
+<h3><a class="anchor" id="opts">Program options</a></h3>
 <p>
 The MondriaanOpt program has the following interface:
-<ul><li><tt>% ./tools/MondriaanOpt matrix [P [eps]] [options]</tt></li></ul>
+</p>
+<ul><li><code>% ./tools/MondriaanOpt matrix [P [eps]] [options]</code></li></ul>
+<p>
 One, two or three parameters may be passed, after which further options may be given.
 An overview of the available parameters and options is given below.
 </p>
-<h4>Parameters</h4>
+
+<div class="indent4">
+<h4><u>Parameters</u></h4>
 <table>
 	<thead>
 		<tr>
 			<th>Parameter</th>
+			<th>Name</th>
 			<th>Description</th>
 		</tr>
 	</thead>
 	<tbody>
 		<tr>
+			<td><code>matrix</code></td>
 			<td>Matrix file</td>
 			<td><i>Required.</i> The input matrix file in Matrix Market (.mtx) format</td>
 		</tr>
 		<tr>
+			<td><code>P</code></td>
 			<td>Number of processors</td>
 			<td>Present for consistency with other Mondriaan* commands. This parameter, if given, must be equal to 2.</td>
 		</tr>
 		<tr>
+			<td><code>eps</code></td>
 			<td>Load imbalance</td>
 			<td>The maximum allowed load imbalance</td>
 		</tr>
 	</tbody>
 </table>
+</div>
 
-<h4>Options</h4>
+<div class="indent4">
+<h4><u>Options</u></h4>
 <p>
 Apart from the matrix, at least one of [eps], -e or -k must be given, defining the maximum allowed load imbalance.
 </p>
@@ -208,7 +248,7 @@ Apart from the matrix, at least one of [eps], -e or -k must be given, defining t
 			<td>Volume</td>
 			<td>
 				<i>Recommended.</i> The starting upper bound volume.
-				This defaults to <tt>min(m,n)+1</tt>, with <tt>m</tt> and <tt>n</tt> denoting the dimensions of the matrix to be partitioned.
+				This defaults to <code>min(m,n)+1</code>, with <code>m</code> and <code>n</code> denoting the dimensions of the matrix to be partitioned.
 				While this is a valid upper bound, you may wish to pass a tighter upper bound to reduce computing time.
 			</td>
 		</tr>
@@ -235,35 +275,40 @@ Apart from the matrix, at least one of [eps], -e or -k must be given, defining t
 		<tr>
 			<td>-svg</td>
 			<td><i>None</i></td>
-			<td>Write visualisations of the partitioning to <tt>.svg</tt> files</td>
+			<td>Write visualisations of the partitioning to <code>.svg</code> files</td>
 		</tr>
 	</tbody>
 </table>
+</div>
 
-<h3><a name="constraint">Difference in imbalance constraints</a></h3>
+<h3><a class="anchor" id="constraint">Difference in imbalance constraints</a></h3>
+<p>
 While the command line interface of MondriaanOpt can be used just as Mondriaan, there is a subtle difference in the problem being solved in these two.
-More precisely, with <tt>N</tt> being the total number of nonzeros, <tt>p</tt> being the total number of processors (which equals 2) and
-<tt>load</tt> the number of nonzeros assigned to a processor, compare:
+More precisely, with <code>N</code> being the total number of nonzeros, <code>p</code> being the total number of processors (which equals 2) and
+<code>load</code> the number of nonzeros assigned to a processor, compare:
+</p>
 <ul>
-	<li>the imbalance constraint <tt>load &lt;= (1+epsilon) (N/p)</tt> which is used in Mondriaan (In the code, this amounts to <tt>floor( ((1+epsilon)*N)/p )</tt>.), and</li>
-	<li>the imbalance constraint <tt>load &lt;= (1+epsilon) ceil(N/p)</tt> which is used in MondriaanOpt.</li>
+	<li>the imbalance constraint <code>load &lt;= (1+epsilon) (N/p)</code> which is used in Mondriaan (In the code, this amounts to <code>floor( ((1+epsilon)*N)/p )</code>.), and</li>
+	<li>the imbalance constraint <code>load &lt;= (1+epsilon) ceil(N/p)</code> which is used in MondriaanOpt.</li>
 </ul>
-As <tt>p=2</tt>, this difference may only be of importance whenever <tt>N</tt> is odd.
-In [<a href="#cite1">1,p.2</a>] it is explained that this different choice was made to ensure feasibility of the problem, even if <tt>epsilon=0</tt>.
+<p>
+As <code>p=2</code>, this difference may only be of importance whenever <code>N</code> is odd.
+In [<a href="#cite1">1,p.2</a>] it is explained that this different choice was made to ensure feasibility of the problem, even if <code>epsilon=0</code>.
+</p>
 
 
-<h3><a name="matl">Using MondriaanOpt in MATLAB</a></h3>
+<h3><a class="anchor" id="matl">Using MondriaanOpt in MATLAB</a></h3>
 
 <p>
 For more information about MATLAB usage, please see the <a href="MATLAB.html">Mondriaan MATLAB guide</a>.
-MondriaanOpt is available in Matlab using the <tt>MatlabMondriaanOpt</tt> MEX routine. Example matlab files
-are given as <tt>mondriaanOpt.m</tt> and <tt>mondriaanOptPlot.m</tt>.
+MondriaanOpt is available in Matlab using the <code>MatlabMondriaanOpt</code> MEX routine. Example matlab files
+are given as <code>mondriaanOpt.m</code> and <code>mondriaanOptPlot.m</code>.
 </p>
 
 
 <h3>References</h3>
 <p>
-[<a name="cite1" href="http://doi.org/10.1016/j.jpdc.2015.06.005">1</a>]
+[<a id="cite1" href="http://doi.org/10.1016/j.jpdc.2015.06.005">1</a>]
 <em>An exact algorithm for sparse matrix bipartitioning</em>,
 Daniel M. Pelt and Rob H. Bisseling, <i>Journal of Parallel and Distributed Computing</i>, <b>85</b> (2015) pp. 79-90.
 </p>
@@ -286,6 +331,8 @@ the Mondriaan package home page</a>.</p>
 
 <hr>
 
+</div>
+
 </body>
 
 </html>
diff --git a/docs/print.css b/docs/print.css
new file mode 100644
index 0000000000000000000000000000000000000000..4901d588101f2f73a875287ca47a836cbaa38607
--- /dev/null
+++ b/docs/print.css
@@ -0,0 +1,58 @@
+
+body {
+	text-align: left;
+	background-color: #DDD;
+	color: black;
+}
+
+#pageNav {
+	display: none;
+}
+
+#menu {
+	position: relative;
+	height: 40px;
+	width: 100%;
+	text-align: center;
+}
+
+#menu A {
+	color: black;
+}
+
+div#menu div {
+	display: inline;
+	position: relative;
+	margin-right: 17px;
+}
+
+.center {
+	width:100%;
+	text-align:center;
+}
+
+.image {
+	float:left;
+	padding:20px;
+}
+
+.squareimage img {
+	width:256px;
+	height:256px;
+	padding:10px;
+	border-style:solid;
+	border-width:2px;
+}
+
+.wideimage img {
+	width:572px;
+	height:207px;
+	padding:10px;
+	border-style:solid;
+	border-width:2px;
+}
+
+.image .caption {
+	font-size:small;
+}
+
diff --git a/docs/script.js b/docs/script.js
new file mode 100644
index 0000000000000000000000000000000000000000..b32b567b4e818f96e3d9063f71ba7420430503fc
--- /dev/null
+++ b/docs/script.js
@@ -0,0 +1,29 @@
+var menuPos = 0, isFixed = false;
+window.addEventListener('load', function(evt) {
+	
+	// Check whether we have a menuPos element
+	menuPosEl = document.getElementById('menuPos');
+	if(typeof(menuPosEl) == 'undefined') {
+		return;
+	}
+	
+	// Get position of menu
+	menuPos = menuPosEl.getBoundingClientRect().top - document.getElementsByTagName('html')[0].getBoundingClientRect().top;
+	
+	// Fix menu when we scroll under it
+	document.addEventListener('scroll', function(evt) {
+		var isDown = document.body.scrollTop > menuPos || document.documentElement.scrollTop > menuPos;
+		if (isDown && !isFixed) {
+			isFixed = true;
+			var menu = document.getElementById('menu');
+			document.getElementById('menuPos').style.height = menu.offsetHeight+"px";
+			menu.className = "fixed";
+		}
+		else if(!isDown && isFixed) {
+			isFixed = false;
+			document.getElementById('menuPos').style.height = "0px";
+			document.getElementById('menu').className = "";
+		}
+	});
+
+});
diff --git a/docs/style.css b/docs/style.css
index 783a871e3b5f3822b1af5979a5fac6932d066cc6..d1bfd10c43e3bd1955dd917a1f5f6267394b6a60 100644
--- a/docs/style.css
+++ b/docs/style.css
@@ -1,27 +1,112 @@
+* {
+	margin: 0px;
+	padding: 0px;
+	border: 0px;
+}
 
 body {
 	text-align: left;
 	background-color: #DDD;
-	color: black;
+	color: #333;
+	font-family: Verdana;
+	font-size: 10pt;
+}
+
+h2 {
+	margin-bottom: 0.5em;
+}
+
+h3 {
+	border-bottom: 1px solid #888;
+	margin-top: 30px;
+}
+
+h4 {
+	margin-top: 1em;
+}
+
+li {
+	margin-left: 45px;
 }
 
-#top {
+p {
+	margin: 1em 0px;
+}
+
+div#mainContainer {
+	width: 80%;
+	margin: 10px auto;
+	background-color: #fff;
+	border-radius: 20px;
+	padding: 20px;
+	position: relative;
+}
+
+#menu {
 	position: relative;
-	height: 40px;
-	width: 100%;
+	background-color: #f8f8f8;
+	border: 1px solid #999;
+	border-width: 1px 0px;
+	padding: 8px 0px;
+}
+
+#menuItems {
+	max-width: 800px;
 	text-align: center;
+	margin: 0px auto;
+	height: 16px;
+	overflow: hidden;
+}
+
+#menuItems:hover {
+	height: auto;
+	overflow: auto;
+}
+
+#menu.fixed {
+	width: 80%;
+	position: fixed;
+	top: 0px;
+	left: 0px;
+	right: 0px;
+	margin: 0px auto;
 }
 
-#top A {
+#menu A {
 	color: black;
 }
 
-div#top div {
+div#menuItems div {
+	display: inline;
+	position: relative;
+	margin-right: 17px;
+}
+
+/* Make sure anchor links arrive where they should (+2em due to fixed menu) */
+a.anchor:before { content: ''; display: block; position: relative; width: 0; height: 2em; margin-top: -2em }
+
+
+#pageNav {
+	float: right;
+	text-align: right;
+}
+#pageNav a {
+	color: black;
+}
+div#pageNav div {
 	display: inline;
 	position: relative;
 	margin-right: 17px;
 }
 
+p.updateNote {
+	border: 1px solid #ccc;
+	font-style: italic;
+	padding: 5px;
+	margin: 10px 5px;
+	background-color: #f8f8f8;
+}
+
 .center {
 	width:100%;
 	text-align:center;
@@ -34,7 +119,7 @@ div#top div {
 
 .squareimage img {
 	width:256px;
-	height:256px;
+	max-width: 100%;
 	padding:10px;
 	border-style:solid;
 	border-width:2px;
@@ -42,7 +127,7 @@ div#top div {
 
 .wideimage img {
 	width:572px;
-	height:207px;
+	max-width: 100%;
 	padding:10px;
 	border-style:solid;
 	border-width:2px;
@@ -52,3 +137,32 @@ div#top div {
 	font-size:small;
 }
 
+div.indent4 {
+	border-left: 1px solid #888;
+	padding-left: 10px;
+	margin-top: 15px;
+	margin-bottom: 25px;
+}
+
+ul.options li {
+	padding-bottom: 10px;
+}
+
+code.option_name {
+	font-style: italic;
+}
+
+@media (max-width:600px) {
+	
+	div#mainContainer {
+		width: auto;
+		margin: 0px;
+		border-radius: 0px;
+		padding: 10px;
+	}
+	
+	#menu.fixed {
+		width: auto;
+		margin: 0px 10px;
+	}
+}
diff --git a/mondriaan.mk b/mondriaan.mk
index 5dac49724c4b3d8671542df291b58323e9e19a08..b37e24140cff992ab0f2b42a5bc539257b90a654 100644
--- a/mondriaan.mk
+++ b/mondriaan.mk
@@ -8,9 +8,12 @@
 # Absolute path of the directory which contains this file (included last in every Makefile in the subdirectories).
 MONDRIAANHOMEDIR:= $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
 
-MONDRIAANCURRENTVERSION := 4.1
+MONDRIAANCURRENTVERSION := 4.2
 MONDRIAANMAJORVERSION := 4
 
+# Gainbucket type must be either LIST or ARRAY
+GAINBUCKET_TYPE := ARRAY
+
 # ==== Matlab support ====
 #
 # To enable Matlab support, please uncomment the following line and insert the correct (global) Matlab home directory and the correct suffix to Matlab MEX files for your architecture.
@@ -29,10 +32,12 @@ MEXSUFFIX := mexa64
 # ==== Compiler flags ====
 # Debug/verbose, standard (default), performance flags.
 
-#CFLAGS := -Wall -Wextra -Wshadow -Wno-unused-parameter -ansi -pedantic -O2 -DTIME -DUNIX -DINFO -g -DMONDRIAANVERSION=\"${MONDRIAANCURRENTVERSION}\"
-#CFLAGS := -Wall -O2 -DMONDRIAANVERSION=\"${MONDRIAANCURRENTVERSION}\"
-CFLAGS := -Wall -O3 -ffast-math -funroll-loops -fomit-frame-pointer -std=c99 -DMONDRIAANVERSION=\"${MONDRIAANCURRENTVERSION}\"
-#CFLAGS := -Wall -O3 -pg -ffast-math -funroll-loops -std=c99 -DMONDRIAANVERSION=\"${MONDRIAANCURRENTVERSION}\"
+CFLAGS := -DMONDRIAANVERSION=\"${MONDRIAANCURRENTVERSION}\" -DGAINBUCKET_${GAINBUCKET_TYPE}
+
+#CFLAGS := ${CFLAGS} -Wall -Wextra -Wshadow -Wno-unused-parameter -ansi -pedantic -O2 -DTIME -DUNIX -DINFO -g
+#CFLAGS := ${CFLAGS} -Wall -O2
+CFLAGS := ${CFLAGS} -Wall -O3 -ffast-math -funroll-loops -fomit-frame-pointer -std=c99
+#CFLAGS := ${CFLAGS} -Wall -O3 -pg -ffast-math -funroll-loops -std=c99
 
 
 # ==== Standard compilation options (it should not be necessary to change these) ====
diff --git a/src/DistributeMat.c b/src/DistributeMat.c
index b1d1a5d4a22a8585d40341221b58e63689aeedb9..fe67956f19fed0d9d85d65b3101f0a01cc6d8380 100644
--- a/src/DistributeMat.c
+++ b/src/DistributeMat.c
@@ -16,6 +16,9 @@ int SplitMatrixKLFM(struct sparsematrix *pT, int k, int i, int dir,
 int SplitMatrixSimple(struct sparsematrix *pT, int k, int i,
                        long weightlo, long weighthi, const struct opts *pOptions);
 
+int SplitMatrixZeroVolume(struct sparsematrix *pT, int k, int i,
+                     long weightlo, long weighthi, const struct opts *pOptions);
+
 #ifdef USE_PATOH
 struct patohnz {
     int P;
@@ -407,6 +410,17 @@ int DistributeMatrixMondriaan(struct sparsematrix *pT, int P, double eps, const
     /* Setup Mondriaan options. */
     maxweight = ((1 + eps) * totweight) / P;  /* rounded down */
     
+#ifdef INFO
+    if(ceil(totweight/(double)P) > maxweight) {
+        /* Compute minimum epsilon, rounded up at 5th decimal place */
+        double eps_min = ceil(totweight/(double)P) * (P/(double)totweight) - 1;
+        eps_min = ceil(eps_min * 100000)/100000;
+        
+        fprintf(stderr, "Info: The posed problem is infeasible, hence the resulting matrix distribution will not satisfy the balance constraint.\n");
+        fprintf(stderr, "      For the problem to be feasible, epsilon should be at least %.5lf.\n", eps_min);
+    }
+#endif
+    
     if (pOptions->SplitStrategy == OneDimRow)
         dir = ROW;
   
@@ -484,7 +498,13 @@ int DistributeMatrixMondriaan(struct sparsematrix *pT, int P, double eps, const
 #ifdef INFO2
         printf("  ******** Split part %d from %d parts******** \n", i, k);
 #endif
-        if (pOptions->SplitMethod == Simple) {
+        int foundZVS = FALSE; /* Whether a zero volume split has been found */
+        if (pOptions->ZeroVolumeSearch == ZeroVolYes && (foundZVS = SplitMatrixZeroVolume(pT, k, i, weightlo, weighthi, pOptions))) {
+#ifdef INFO
+            printf("Found zero volume split!\n");
+#endif
+        }
+        else if (pOptions->SplitMethod == Simple) {
             /* Simple split of the matrix only based on load balance,
                not minimising communication volume. 
                Useful for testing and debugging */
@@ -503,6 +523,43 @@ int DistributeMatrixMondriaan(struct sparsematrix *pT, int P, double eps, const
             fprintf(stderr, "DistributeMatrixMondriaan(): Unknown SplitMethod!\n");
             return FALSE;
         }
+        
+        /* Shift weight and procs */
+        for (j = k; j > i+1; j--) {
+            weight[j] = weight[j-1];
+            procs[j] = procs[j-1];
+        }
+
+        k++; /* new number of parts */
+
+        weight[i] = ComputeWeight(pT, pT->Pstart[i], pT->Pstart[i+1]-1, NULL, pOptions);
+        weight[i+1] = ComputeWeight(pT, pT->Pstart[i+1], pT->Pstart[i+2]-1, NULL, pOptions);
+        
+        if (weight[i] < 0 || weight[i + 1] < 0) {
+            fprintf(stderr, "DistributeMatrixMondriaan(): Unable to compute weights!\n");
+            return FALSE;
+        }
+        
+        procslo = procs[i]/2;
+        procshi = (procs[i]%2==0 ? procslo : procslo+1);
+        
+        if (weight[i] <= weight[i+1]) {
+            procs[i] = procslo;
+            procs[i+1] = procshi;
+        } else {
+            procs[i] = procshi;
+            procs[i+1] = procslo;
+        }
+        
+        /* Apply free nonzero search if enabled, but only for (symmetric) finegrain and mediumgrain strategies */
+        if(pOptions->ImproveFreeNonzeros == FreeNonzerosYes && !foundZVS && (pOptions->SplitStrategy == FineGrain ||
+            pOptions->SplitStrategy == SFineGrain || pOptions->SplitStrategy == MediumGrain)) {
+            ImproveFreeNonzeros(pT, pOptions, procs, i, i+1);
+            
+            weight[i] = ComputeWeight(pT, pT->Pstart[i], pT->Pstart[i+1]-1, NULL, pOptions);
+            weight[i+1] = ComputeWeight(pT, pT->Pstart[i+1], pT->Pstart[i+2]-1, NULL, pOptions);
+        }
+        
 #ifdef INFO2
         printf("  Pstart[%d] = %ld ", i,  pT->Pstart[i]);
         printf("Pstart[%d] = %ld ", i+1,  pT->Pstart[i+1]);
@@ -584,34 +641,6 @@ int DistributeMatrixMondriaan(struct sparsematrix *pT, int P, double eps, const
                 }
             }
         }
-        
-        /* Shift weight and procs */
-        for (j = k; j > i+1; j--) {
-            weight[j] = weight[j-1];
-            procs[j] = procs[j-1];
-        }
-
-        k++; /* new number of parts */
-
-        weight[i] = ComputeWeight(pT, pT->Pstart[i], pT->Pstart[i+1]-1, NULL, pOptions);
-        weight[i+1] = ComputeWeight(pT, pT->Pstart[i+1], pT->Pstart[i+2]-1, NULL, pOptions);
-        
-        if (weight[i] < 0 || weight[i + 1] < 0) {
-            fprintf(stderr, "DistributeMatrixMondriaan(): Unable to compute weights!\n");
-            return FALSE;
-        }
-        
-        procslo = procs[i]/2;
-        procshi = (procs[i]%2==0 ? procslo : procslo+1);
-        
-        if (weight[i] <= weight[i+1]) {
-            procs[i] = procslo;
-            procs[i+1] = procshi;
-        } else { 
-            procs[i] = procshi;
-            procs[i+1] = procslo;
-        }
-
 
         /* Check if there is a part that is too large */
         done = TRUE;
@@ -663,6 +692,59 @@ int DistributeMatrixMondriaan(struct sparsematrix *pT, int P, double eps, const
     printf("  Column lambda histogram:\n");
     VerifyLambdas(pT->ColLambda, pT->n, P);
 #endif
+    
+    if(pOptions->CheckUpperBound == CheckUpperBoundYes) {
+        /* Check whether we can apply the upper bound algorithm */
+        int isSymmetric   = ((pT->MMTypeCode[3]=='S' || pT->MMTypeCode[3]=='K' || pT->MMTypeCode[3]=='H') &&
+                             pOptions->SymmetricMatrix_UseSingleEntry == SingleEntYes);
+        int hasDummies    = (pT->NrDummies > 0);
+        int isColWeighted = (pT->MMTypeCode[0] == 'W' && pT->NrColWeights > 0);
+        
+        /* To support OrderPermutation, new code should be written to recompute the permutation from scratch */
+        int orderPermute  = (pOptions->OrderPermutation != OrderNone);
+        
+        if(!isSymmetric && !hasDummies && !isColWeighted && !orderPermute) {
+            /* Compute volume. It should be at most (min(m,n)+1)(P-1) */
+            long ComVol1, ComVol2, tmp;
+            CalcCom(pT, NULL, (pT->m < pT->n)?ROW:COL, &ComVol1, &tmp, &tmp, &tmp, &tmp);
+            CalcCom(pT, NULL, (pT->m < pT->n)?COL:ROW, &ComVol2, &tmp, &tmp, &tmp, &tmp);
+            long upperBound = (((pT->m < pT->n)?pT->m:pT->n)+1)*(P-1);
+            
+            if(ComVol1+ComVol2 > upperBound) {
+#ifdef INFO
+                printf("Info: Achieved volume %ld is larger than upper bound %ld. Attempting to generate upper bound solution.\n", ComVol1+ComVol2, upperBound);
+#endif
+                if (!SplitMatrixUpperBound(pT, P, pOptions)) {
+                    fprintf(stderr, "DistributeMatrixMondriaan(): Unable to compute upper bound solution!\n");
+                }
+                else {
+                    /* Update variables to reflect new distribution */
+                    k = P;
+                    
+                    for (j = 0; j <= P; j++) {
+                        weight[j] = ComputeWeight(pT, pT->Pstart[j], pT->Pstart[j+1]-1, NULL, pOptions);
+                        procs[j] = 1;
+                    }
+#ifdef INFO2
+                    printf("  Number of parts = %d \n", k);
+                    printf("  Pstart = ");
+                    for (j = 0; j <= P; j++)
+                        printf("%ld ", pT->Pstart[j]);
+                    printf("\n\n");
+#endif      
+
+#ifdef INFO2
+                    /* Print all lambdas. */
+                    printf("  Row lambda histogram:\n");
+                    VerifyLambdas(pT->RowLambda, pT->m, P);
+                    printf("  Column lambda histogram:\n");
+                    VerifyLambdas(pT->ColLambda, pT->n, P);
+#endif
+                }
+            }
+        }
+    }
+    
 
     /* Set matrix type code to distributed */
     pT->MMTypeCode[0] = 'D';
@@ -1505,3 +1587,111 @@ int SplitMatrixSimple(struct sparsematrix *pT, int k, int i,
     return TRUE;
 } /* end SplitMatrixSimple */
 
+
+int SplitMatrixZeroVolume(struct sparsematrix *pT, int k, int i,
+                     long weightlo, long weighthi, const struct opts *pOptions) {
+    
+    /* This function splits part i of the sparse matrix T into two parts,
+       the first with weight <= weightlo and the second with weight <= weighthi.
+       The split is performed by searching for a split with zero communication
+       volume. If such a split is found, it is applied to pT. Otherwise, pT is
+       left untouched.
+
+       Input: T sparse matrix,
+              k current number of parts, 1 <= k < P,
+              i number of part to be split, 0 <= i < k,
+              weightlo = smallest upper bound for part weight, belongs to part 0
+              weighthi = largest upper bound for part weight, belongs to part 1.
+              
+       Output: T sparse matrix.
+               The following applies if a zero volume split is found:
+               The nonzeros of the new part i (the first part) are in positions
+                   pT->Pstart[i], pT->Pstart[i+1]-1.
+               The nonzeros of the new part i+1 (the second) are in positions
+                   pT->Pstart[i+1], pT->Pstart[i+2]-1.
+               All parts > i+1 have been shifted.
+       
+    */
+
+    long lo, hi, mid = 0, nz, weight;
+    int j;
+    struct sparsematrix A;
+    
+    if (!pT || !pOptions) {
+        fprintf(stderr, "SplitMatrixZeroVolume(): Null arguments!\n");
+        return FALSE;
+    }
+    
+    lo = pT->Pstart[i];
+    hi = pT->Pstart[i+1]-1;
+    
+    nz = hi-lo+1;
+    
+    if (nz > 0) {
+        weight = ComputeWeight(pT, lo, hi, NULL, pOptions);
+        
+        if (weight > weightlo + weighthi || weight < 0) {
+            /* Desired split is infeasible */
+            fprintf(stderr, "SplitMatrixZeroVolume(): desired split is infeasible!\n");
+            return FALSE;
+        }
+        
+        /* Copy info from T to A */
+        A = *pT;            /* A has same size as T, and same other parameters, */
+        A.i = &(pT->i[lo]); /* but only a subset of the nonzeros */
+        A.j = &(pT->j[lo]);
+        if (A.MMTypeCode[2] != 'P')
+            A.ReValue = &(pT->ReValue[lo]);
+        if (A.MMTypeCode[2] == 'C')
+            A.ImValue = &(pT->ImValue[lo]);
+        A.NrNzElts = nz;
+        
+#ifdef INFO
+#ifdef TIME
+        clock_t starttime, endtime;
+        double cputime;
+        starttime = clock();
+#ifdef UNIX
+        struct timeval starttime1, endtime1;
+        gettimeofday(&starttime1, NULL);
+#endif
+#endif
+#endif
+        /* Run zero volume search. If a zero volume split is found, ZeroVolumeSearch()
+         * will apply this split directly; we then only need to update Pstart.
+         */
+        int foundZeroVolumePartition = ZeroVolumeSearch(&A, weightlo, weighthi, &mid, pOptions);
+        
+#ifdef INFO
+#ifdef TIME
+        endtime = clock();
+        cputime = ((double) (endtime - starttime)) / CLOCKS_PER_SEC;
+        printf("  ZeroVolumeSearch CPU-time    : %f seconds\n", cputime);
+#ifdef UNIX
+        gettimeofday(&endtime1, NULL);
+        printf("  ZeroVolumeSearch elapsed time: %f seconds\n",
+                (endtime1.tv_sec - starttime1.tv_sec) +
+                (endtime1.tv_usec - starttime1.tv_usec) / 1000000.0);
+#endif
+        fflush(stdout);
+#endif
+#endif
+
+        if(!foundZeroVolumePartition) {
+            return FALSE;
+        }
+    }
+    else {
+        mid = 0; /* Pstart[i] = Pstart[i+1] */
+    }
+    
+    /* Shift Pstart for parts > i */
+    for (j = k; j > i; j--)
+        pT->Pstart[j+1] = pT->Pstart[j];
+    
+    /* Register new splitting point.
+     * mid lies in [0,hi-lo], translate it to [lo,hi] */
+    pT->Pstart[i+1] = lo + mid;
+
+    return TRUE;
+} /* end SplitMatrixZeroVolume */
diff --git a/src/DistributeMat.h b/src/DistributeMat.h
index 5c0570563cc7de376532b1431af24e87897ac1bd..97002b9904d2c5bc09bd2763fbdf2c51f6dd093a 100644
--- a/src/DistributeMat.h
+++ b/src/DistributeMat.h
@@ -7,6 +7,9 @@
 #include "Sort.h"
 #include "SparseMatrix.h"
 #include "Remembrance.h"
+#include "ZeroVolumeSearch.h"
+#include "FreeNonzeros.h"
+#include "SplitMatrixUpperBound.h"
 
 /* Function declarations for DistributeMat.c */
 long ComputeWeight(const struct sparsematrix *pT, long lo, long hi, long *wnz, const struct opts *pOptions);
diff --git a/src/DistributeVecLib.c b/src/DistributeVecLib.c
index 82ee14a1468cacadb3c43542883044b9f723291e..c24d8c3a2cd7c19eccc6b75626f4bde2a62a8b26 100644
--- a/src/DistributeVecLib.c
+++ b/src/DistributeVecLib.c
@@ -923,3 +923,75 @@ int WriteVectorCollection(long int **X, const char *name, const long i, const lo
         return TRUE;
 } /* end WriteVectorCollection */
 
+
+long * ReadVector(const char base, long *l, int *P, FILE *fp) {
+
+    /* Base vector-reading function. Automatically adapts base of array to read.
+       Assumes that we are writing a distribution vector with P>0.
+       base is the base value of X (usually 0).
+       l is the length of the vector read (X).
+       P is the number of processors.
+       The return value is the array of vector values (X), or NULL if an error occurs.
+    */
+
+    long t;
+
+    if (!fp) {
+        fprintf(stderr, "ReadVector(): Null arguments!\n");
+        return NULL;
+    }
+
+    if (ferror(fp)) {
+        fprintf(stderr, "ReadVector(): Unable to write to stream!\n");
+        return NULL;
+    }
+    
+    
+    int SizeRead=FALSE, count=0;
+    char *line, linebuffer[MAX_LINE_LENGTH];
+  
+    while (SizeRead == FALSE && 
+           (line = fgets(linebuffer, MAX_LINE_LENGTH, fp)) != NULL) { 
+        /* a new line has been read */
+        if (strlen(line) > 0) {
+            if (linebuffer[0] != '%') {
+                /* The size line */
+
+                /* Read l and P from the line */
+                count = sscanf(line, "%ld %d\n", l, P);
+                
+                if (count < 2 || *l < 1 || *P < 0) {
+                    fprintf(stderr, "ReadVector(): Error in vector size!\n");
+                    return NULL;
+                }
+                else {
+                    SizeRead = TRUE;
+                }
+            }
+        }
+    }
+    
+    if(*P == 0) {
+        fprintf(stderr, "ReadVector(): This function has not been implemented for undistributed vectors!\n");
+        return NULL;
+    }
+    
+    long *X = (long *)malloc((*l)*sizeof(long));
+    long tmp;
+    
+    if (X == NULL) {
+        fprintf(stderr, "ReadVector(): Not enough memory!\n");
+        return NULL;
+    }
+    
+    for (t = 0; t < *l; t++) {
+        if(fscanf(fp, "%ld %ld\n", &tmp, &(X[t])) != 2) {
+            fprintf(stderr, "ReadVector(): Read Error!\n");
+            return NULL;
+        }
+        X[t] -= (1-base);
+    }
+    
+    return X;
+} /* end ReadVector */
+
diff --git a/src/DistributeVecLib.h b/src/DistributeVecLib.h
index c9985f99b5f5c1a95b2fd03d2d9b541915803532..ed49cfc8b8e30d6b2da5508b674ac9b063b80f7d 100644
--- a/src/DistributeVecLib.h
+++ b/src/DistributeVecLib.h
@@ -33,5 +33,7 @@ void PrintVecStatistics(int P, long *Ns, long *Nr, long *Nv);
 int WriteVector(const long int *X, const char base, const char *name, long l, int P, FILE *fp, const struct opts *pOptions);
 int WriteVectorDistribution(const long int *X, const char *name, long l, int P, FILE *fp, const struct opts *pOptions);
 int WriteVectorCollection(long int **X, const char *name, const long i, const long *j, FILE *fp);
+
+long * ReadVector(const char base, long *l, int *P, FILE *fp);
  
 #endif /* __DistributeVecLib_h__ */
diff --git a/src/FreeNonzeros.c b/src/FreeNonzeros.c
new file mode 100644
index 0000000000000000000000000000000000000000..544939d7c0d769803c2a3885eea7bd006003e9c5
--- /dev/null
+++ b/src/FreeNonzeros.c
@@ -0,0 +1,127 @@
+#include "FreeNonzeros.h"
+
+/**
+ * Swap nonzeros s and t of matrix pM
+ */
+void SwapNonzero(struct sparsematrix *pM, long s, long t) {
+	SwapLong(pM->i, s, t);
+	SwapLong(pM->j, s, t);
+	if(pM->MMTypeCode[2] != 'P')
+		SwapDouble(pM->ReValue, s, t);
+	if(pM->MMTypeCode[2] == 'C')
+		SwapDouble(pM->ImValue, s, t);
+} /* end SwapNonzero */
+
+/**
+ * Improve load balance by moving free nonzeros between two specified processors.
+ * This function works for general and symmetric matrices.
+ * It works when dummy nonzeros are present: dummies will not be moved as they do not contribute weight.
+ * It does not work when weights are based on column weights.
+ * 
+ * Input:
+ *   pOptions    Options struct
+ *   procs       abs(procs[i]) = Number of processors each part should still be distributed over
+ *   p1, p2      Processors to condider
+ * 
+ * Input/output:
+ *   pM          The matrix
+ * 
+ * Return value: FALSE if an error occurred, TRUE otherwise
+ */
+
+int ImproveFreeNonzeros(struct sparsematrix *pM, const struct opts *pOptions, const int *procs, int p1, int p2) {
+	
+	long nnz1 = ComputeWeight(pM, pM->Pstart[p1], pM->Pstart[p1+1]-1, NULL, pOptions);
+	long nnz2 = ComputeWeight(pM, pM->Pstart[p2], pM->Pstart[p2+1]-1, NULL, pOptions);
+	long i, j, t, nzWeight;
+	
+	int symmetric = pM->m == pM->n &&
+		(pM->MMTypeCode[3]=='S' || pM->MMTypeCode[3]=='K' || pM->MMTypeCode[3]=='H') &&
+		pOptions->SymmetricMatrix_UseSingleEntry == SingleEntYes;
+		
+	int dummies = pM->m == pM->n && pM->NrDummies > 0;
+	
+	if(nnz1/abs(procs[p1]) > nnz2/abs(procs[p2])) {
+		long tmp = p1;
+		p1 = p2;
+		p2 = tmp;
+		tmp = nnz1;
+		nnz1 = nnz2;
+		nnz2 = tmp;
+	}
+	
+	/* Now p2 is relatively larger than p1 */
+	if((nnz1+1)/(double)abs(procs[p1]) >= (nnz2-1)/(double)abs(procs[p2])) {
+		return TRUE;
+	}
+	
+	char *rowsP1 = (char *)malloc(pM->m * sizeof(char));
+	char *colsP1 = (char *)malloc(pM->n * sizeof(char));
+	
+	if(rowsP1 == NULL || colsP1 == NULL) {
+		fprintf(stderr, "ImproveFreeNonzeros(): Not enough memory!");
+		if(rowsP1 != NULL)
+			free(rowsP1);
+		if(colsP1 != NULL)
+			free(colsP1);
+		return FALSE;
+	}
+	
+	for(i=0; i<pM->m; ++i) {
+		rowsP1[i] = 0;
+	}
+	for(j=0; j<pM->n; ++j) {
+		colsP1[j] = 0;
+	}
+	
+	/* In what columns/rows does P(1) have nonzeros? */
+	for(t=pM->Pstart[p1]; t<pM->Pstart[p1+1]; ++t) {
+		rowsP1[pM->i[t]] = 1;
+		colsP1[pM->j[t]] = 1;
+		
+		if(symmetric) {
+			rowsP1[pM->j[t]] = 1;
+			colsP1[pM->i[t]] = 1;
+		}
+	}
+	
+	for(t=pM->Pstart[p2]; t<pM->Pstart[p2+1]; ++t) {
+		if(rowsP1[pM->i[t]] == 1 && colsP1[pM->j[t]] == 1) {
+			/* This nonzero is free. As p2 is relatively large, move it */
+			
+			if(dummies && pM->i[t] == pM->j[t] && pM->dummy[pM->i[t]]) {
+				continue;
+			}
+			
+			nzWeight = (symmetric && pM->i[t] != pM->j[t])?2:1;
+			
+			if((nnz1+nzWeight)/(double)abs(procs[p1]) > (nnz2-nzWeight)/(double)abs(procs[p2])) {
+				free(rowsP1);
+				free(colsP1);
+				return TRUE;
+			}
+			
+			/* Move the nonzero */
+			if(p2 > p1) {
+				SwapNonzero(pM, t, pM->Pstart[p2]);
+				++pM->Pstart[p2];
+			}
+			else {
+				SwapNonzero(pM, t, pM->Pstart[p2+1]-1);
+				--pM->Pstart[p2+1];
+			}
+			
+			/* Update bookkeeping */
+			nnz1 += nzWeight;
+			nnz2 -= nzWeight;
+			
+		}
+		
+	}
+	
+	/* Finish */
+	free(rowsP1);
+	free(colsP1);
+	
+	return TRUE;
+} /* end ImproveFreeNonzeros */
diff --git a/src/FreeNonzeros.h b/src/FreeNonzeros.h
new file mode 100644
index 0000000000000000000000000000000000000000..13939e44b936059a3cd67acc52e5e8258dc801f7
--- /dev/null
+++ b/src/FreeNonzeros.h
@@ -0,0 +1,17 @@
+#ifndef __FreeNonzeros_h__
+#define __FreeNonzeros_h__
+
+#include "Sort.h"
+#include "SparseMatrix.h"
+#include "DistributeMat.h"
+
+struct freeIndex {
+	long t;
+	long numProcs;
+};
+
+void SwapNonzero(struct sparsematrix *pM, long s, long t);
+
+int ImproveFreeNonzeros(struct sparsematrix *pM, const struct opts *pOptions, const int *procs, int p1, int p2);
+
+#endif /* __FreeNonzeros_h__ */
diff --git a/src/GainBucket.c b/src/GainBucket.c
index fb8b1e3285e1bb86a5778bed796e9c712f114724..4bcf43fa7783b3fb7de2bf95bef050f04989ad9e 100644
--- a/src/GainBucket.c
+++ b/src/GainBucket.c
@@ -1,288 +1,9 @@
 #include "GainBucket.h"
 
-struct bucketentry *BucketInsert(struct gainbucket *pGB, long key, long VtxNr) {
-
-    /* This function inserts vertex number VtxNr in the gainbucket GB,
-       in the appropriate bucket with key value. The vertex must not be
-       present already. A bucketentry representing VtxNr is allocated
-       and a pointer to this bucketentry is returned. */
-
-    struct bucket *pB, **ppB;
-    struct bucketentry *pE;
-    
-    if (!pGB) {
-        fprintf(stderr, "BucketInsert(): Null argument!\n");
-        return NULL;
-    }
-    
-    ppB = &(pGB->Root);
-    pB = *ppB;
-  
-    /*## While the key is smaller than the value in the buckets, 
-         go through the bucket list: ##*/
-    while (*ppB != NULL) {
-        pB = *ppB; /* pB points to current bucket */
-  
-        if (key < pB->value)
-            ppB = &(pB->next); /* points to next bucket */
-        else 
-            break;
-    }
-  
-    if (pB == NULL) {
-        /*## Bucket list is empty. Create first bucket: ##*/
-  
-        *ppB = (struct bucket *) malloc(sizeof(struct bucket));
-        
-        if (*ppB == NULL) {
-            fprintf(stderr, "BucketInsert(): Not enough memory for first bucket!\n");
-            return NULL;
-        }
-        
-        (*ppB)->value = key;
-        (*ppB)->entry = NULL;
-        (*ppB)->prev = (*ppB)->next = NULL;
-  
-        pGB->NrBuckets++;
-    } else if (key < pB->value) { 
-        /*## Create new bucket at the end of the current list: ##*/
-        /* *ppB == NULL */
-  
-        *ppB = (struct bucket *) malloc(sizeof(struct bucket));
-        
-        if (*ppB == NULL) {
-            fprintf(stderr, "BucketInsert(): Not enough memory for new bucket!\n");
-            return NULL;
-        }
-  
-        (*ppB)->value = key;
-        (*ppB)->entry = NULL;
-        (*ppB)->prev = pB;
-        (*ppB)->next = NULL;
-  
-        pGB->NrBuckets++;  
-    } else if (key > pB->value) {
-        /*## Create new bucket between the previous and the current
-             bucket in the list: ##*/
-  
-        *ppB = (struct bucket *) malloc(sizeof(struct bucket));
-        
-        if (*ppB == NULL) {
-            fprintf(stderr, "BucketInsert(): Not enough memory for new bucket!\n");
-            return NULL;
-        }
-      
-        (*ppB)->value = key;
-        (*ppB)->entry = NULL;
-        (*ppB)->prev = pB->prev;
-        (*ppB)->next = pB;
-     
-        if (pB->prev != NULL)
-            (pB->prev)->next = *ppB;
-        else
-            pGB->Root = *ppB;
-      
-        pB->prev = *ppB;
-        pGB->NrBuckets++;
-    }
-        /* otherwise key == pB->value (and hence pB == *ppB) */
-
-
-    /*## Insert the vertex number into a new bucketentry at
-         the start of the bucket: ##*/
-    pE = (struct bucketentry *) malloc(sizeof(struct bucketentry));
-    
-    if (pE == NULL) {
-        fprintf(stderr, "BucketInsert(): Not enough memory for bucket entry!\n");
-        return NULL;
-    }
-  
-    pE->vtxnr = VtxNr;
-    pE->bucket = *ppB;  
-    pE->prev = NULL;
-    pE->next = (*ppB)->entry;
-    
-    if (pE->next != NULL)
-        (pE->next)->prev = pE;
-    
-    (*ppB)->entry = pE;
-
-    return pE;
-} /* end BucketInsert */
-
-
-struct bucketentry *BucketMove(struct gainbucket *pGB, struct bucketentry *pE,
-                               long key) {
-  
-   /* This function moves the vertex number represented by bucketentry pE
-      in the gainbucket GB to the appropriate bucket with key value. 
-      The key value must differ from the current value.  
-      A pointer to the new bucketentry is returned.
-      The memory space of the original bucketentry is freed.
-      If its bucket has become empty, the space of the bucket is also freed. */
-  
-    long VtxNr;
-    struct bucket *pB;
-    
-    if (!pGB || !pE) {
-        fprintf(stderr, "BucketMove(): Null arguments!\n");
-        return NULL;
-    }
-  
-    VtxNr = pE->vtxnr;
-    pB = pE->bucket;
-  
-    if (pB->value == key) {
-        fprintf(stderr, "BucketMove(): Destination bucket equals source!\n");
-        return NULL;
-    }
-  
-    /*## Remove this entry from the bucket: ##*/
-    /* Adjust forward links */
-    if (pE->prev != NULL)
-        (pE->prev)->next = pE->next;  
-    else
-        pB->entry = pE->next;
-  
-    /* Adjust backward links */ 
-    if (pE->next != NULL)
-        (pE->next)->prev = pE->prev;
-  
-    if (pE != NULL) 
-        free(pE);
-  
-    /*## If this bucket is now empty, remove it from the list: ##*/
-    if (pB->entry == NULL) {      
-        if (pB->prev != NULL)
-             (pB->prev)->next = pB->next;
-        else
-             pGB->Root = pB->next;
-  
-        if (pB->next != NULL)
-             (pB->next)->prev = pB->prev;
-        
-        if (pB != NULL)
-          free(pB);
-        pGB->NrBuckets--;
-    }
-    
-    /*## Insert the vertex in the bucket of the new key: ##*/
-    pE = BucketInsert(pGB, key, VtxNr);
-  
-    return pE;
-} /* end BucketMove */
-  
-  
-long BucketDeleteMax(struct gainbucket *pGB) {
-  
-   /* This function deletes the first vertex from the first bucket.
-      This vertex has the maximum gain value. 
-      The function returns the vertex number.
-      The first bucket must exist and it should not be empty.
-      The memory space of the original bucketentry is freed.
-      If its bucket has become empty, the space of the bucket is also freed. */
- 
-
-    long VtxNr;
-    struct bucket *pB;
-    struct bucketentry *pE;
-    
-    if (!pGB) {
-        fprintf(stderr, "BucketDeleteMax(): Null arguments!\n");
-        return -1;
-    }
-    
-    pB = pGB->Root ;
-    pE = pB->entry;  
-    
-    if (!pB || !pE) {
-        fprintf(stderr, "BucketDeleteMax(): Internal error!\n");
-        return -1;
-    }
-  
-    VtxNr = pE->vtxnr;
-  
-    /*## Remove this entry from the bucket: ##*/
-    pB->entry = pE->next;
-   
-    if (pE->next != NULL)
-        (pE->next)->prev = NULL;
-  
-    free(pE);
-  
-    /*## If this bucket is now empty, remove it from the list: ##*/
-    if (pB->entry == NULL) {      
-        pGB->Root = pB->next;
-  
-        if (pB->next != NULL)
-            (pB->next)->prev = NULL;
-        
-        free(pB);
-        pGB->NrBuckets--;
-    }
-  
-    return(VtxNr);
-} /* end BucketDeleteMax */
-
-
-long GainBucketGetMaxVal(struct gainbucket *pGB) {
-
-    /* This function gives the value of the vertex with the maximum
-       gain value, if the gainbucket data structure is not empty.
-       Otherwise, it returns LONG_MIN. */
-    if (!pGB) return LONG_MIN;
-
-    if (pGB->NrBuckets > 0)
-        return((pGB->Root)->value);
-    else
-        return(LONG_MIN);
-
-} /* end GainBucketGetMaxVal */
-
-
-long GainBucketGetMaxValVertexNr(struct gainbucket *pGB) {
-
-    /* This function gives the number of the vertex with the maximum
-       gain value, if the gainbucket data structure is not empty.
-       Otherwise, it returns LONG_MIN. */
-    
-    if (!pGB) return LONG_MIN;
-    
-    if (pGB->NrBuckets > 0)
-        return(((pGB->Root)->entry)->vtxnr);
-    else
-        return(LONG_MIN);
-
-} /* end GainBucketGetMaxValVertexNr */
-
-
-int ClearGainBucket(struct gainbucket *pGB) {
-
-   /* This function deletes all vertices and buckets
-      and frees the corresponding memory space.
-      As a result, pGB->Root = NULL and pGB->NrBuckets = 0. */
-
-    struct bucket *pB;
-    struct bucketentry *pE;
-    
-    if (!pGB) {
-        fprintf(stderr, "ClearGainBucket(): Null argument!\n");
-        return FALSE;
-    }
-    
-    while ((pB = pGB->Root) != NULL) {      
-        pGB->Root = pB->next;
-  
-        /*## Remove all entries from this bucket: ##*/  
-        while ((pE = pB->entry) != NULL) {
-            pB->entry = pE->next;
-            free(pE);
-        }
-  
-        /*## Remove this bucket from the list: ##*/
-        free(pB);
-        pGB->NrBuckets--;
-    }
-
-    return TRUE;
-} /* end ClearGainBucket */
+#if defined(GAINBUCKET_LIST)
+#include "GainBucketList.c"
+#elif defined(GAINBUCKET_ARRAY)
+#include "GainBucketArray.c"
+#else
+#error "A gain bucket structure should be selected in mondriaan.mk"
+#endif
diff --git a/src/GainBucket.h b/src/GainBucket.h
index 2e2a56c398d03b4dd2e1ff0e79e8b143b724030a..a2d410d18d3839f1868c1d69f51bad940b247e72 100644
--- a/src/GainBucket.h
+++ b/src/GainBucket.h
@@ -1,55 +1,13 @@
 /* This file defines Gainbucket, a data structure
    which contains numbers of data items and their values. 
-   The data item can be a vertex and the value its gain in a move.
-   The numbers are integers >=0, and the values are integers
-   without restriction. We call the items vertices and their values gains.
-
-   The vertices are sorted in order of decreasing gain.
-   Vertices with the same gain value are stored together in a bucket,
-   implemented as a doubly linked list. The entries of a bucket,
-   representing vertices, are called bucketentries.
-   The list is terminated by NULL at both ends.
-
-   The buckets themselves are also linked in a doubly linked list.
-   This list is also terminated by NULL at both ends. */
-
-
-#ifndef __GainBucket_h__
-#define __GainBucket_h__
-
-#include "Options.h" 
-
-struct bucketentry {
-    long vtxnr;            /* vertex number for this bucketentry */
-    struct bucket *bucket; /* pointer to the bucket containing this entry */
-    struct bucketentry *prev; /* pointer to previous bucketentry
-                                  in the list of entries*/
-    struct bucketentry *next; /* pointer to next bucketentry */
-};
-  
-struct bucket {
-    long value;           /* value for the entries in this bucket */
-    struct bucket *prev;  /* pointer to previous bucket
-                              in the list of buckets */
-    struct bucket *next;  /* pointer to next bucket */
-    struct bucketentry *entry; /* pointer to first bucketentry in this bucket */
-};
-  
-struct gainbucket {
-  long NrBuckets;       /* number of buckets in the gainbucket */
-  struct bucket *Root;  /* pointer to the first bucket
-                            in the list of buckets */
-};
-
-
-struct bucketentry *BucketInsert(struct gainbucket *pGB, long key, long VtxNr);
-struct bucketentry *BucketMove(struct gainbucket *pGB, struct bucketentry *pE,
-                               long key);
-long BucketDeleteMax(struct gainbucket *pGB);
-
-long GainBucketGetMaxVal(struct gainbucket *pGB);
-long GainBucketGetMaxValVertexNr(struct gainbucket *pGB);
-
-int ClearGainBucket(struct gainbucket *pGB);
-
-#endif /* __GainBucket_h__ */
+   This file is a placeholder that redirects to either
+   the Linked List implementation or the Array implementation,
+   as may be chosen in mondriaan.mk */
+
+#if defined(GAINBUCKET_LIST)
+#include "GainBucketList.h"
+#elif defined(GAINBUCKET_ARRAY)
+#include "GainBucketArray.h"
+#else
+#error "A gain bucket structure should be selected in mondriaan.mk"
+#endif
diff --git a/src/GainBucketArray.c b/src/GainBucketArray.c
new file mode 100644
index 0000000000000000000000000000000000000000..60549453b351e98782c2f0efc21aea716d188d02
--- /dev/null
+++ b/src/GainBucketArray.c
@@ -0,0 +1,296 @@
+#include "GainBucket.h"
+
+int InitGainBucket(struct gainbucket *pGB, long MaxValue) {
+    /* Initialize empty GainBucket structure.
+       Must be called before using any other gainbucket method. */
+    
+    if (!pGB) {
+        fprintf(stderr, "InitGainBucket(): Null parameter!\n");
+        return FALSE;
+    }
+    
+    pGB->MaxValue = MaxValue;
+    pGB->MaxPresentValue = LONG_MIN;
+    pGB->Root = (struct bucket*)calloc(2*MaxValue+1, sizeof(struct bucket));
+    if (pGB->Root == NULL) {
+        fprintf(stderr, "InitGainBucket(): Not enough memory!\n");
+        return FALSE;
+    }
+    
+    long k;
+    for(k=-MaxValue; k<=MaxValue; ++k) {
+        pGB->Root[pGB->MaxValue+k].value = k;
+        pGB->Root[pGB->MaxValue+k].entry = NULL;
+    }
+    return TRUE;
+} /* end InitGainBucket */
+
+
+struct bucketentry *BucketInsert(struct gainbucket *pGB, long key, long VtxNr) {
+    /* This function inserts vertex number VtxNr in the gainbucket GB,
+       in the appropriate bucket with key value. The vertex must not be
+       present already. A bucketentry representing VtxNr is allocated
+       and a pointer to this bucketentry is returned. */
+
+    struct bucket *pB;
+    struct bucketentry *pE;
+    
+    if (!pGB || !pGB->Root) {
+        fprintf(stderr, "BucketInsert(): Null parameter!\n");
+        return NULL;
+    }
+    if(key > pGB->MaxValue || key < -pGB->MaxValue) {
+        fprintf(stderr, "BucketInsert(): Invalid key!\n");
+        return NULL;
+    }
+    
+    pB = &(pGB->Root[pGB->MaxValue+key]);
+
+    /*## Insert the vertex number into a new bucketentry at
+         the start of the bucket: ##*/
+    
+    pE = (struct bucketentry *) malloc(sizeof(struct bucketentry));
+    if (pE == NULL) {
+        return NULL;
+    }
+  
+    pE->vtxnr = VtxNr;
+    pE->bucket = pB;  
+    pE->prev = NULL;
+    pE->next = pB->entry;
+    
+    if (pE->next != NULL) {
+        (pE->next)->prev = pE;
+    }
+    else {
+        pGB->NrBuckets++;
+        if(key > pGB->MaxPresentValue)
+            pGB->MaxPresentValue = key;
+    }
+    
+    pB->entry = pE;
+    
+    return pE;
+} /* end BucketInsert */
+
+
+struct bucketentry *BucketMove(struct gainbucket *pGB, struct bucketentry *pE,
+                               long key) {
+    
+   /* This function moves the vertex number represented by bucketentry pE
+      in the gainbucket GB to the appropriate bucket with key value. 
+      The key value must differ from the current value.  
+      A pointer to the bucketentry is returned. */
+   
+    struct bucket *pB;
+    
+    if (!pGB || !pE || !pGB->Root) {
+        fprintf(stderr, "BucketMove(): Null parameter!\n");
+        return NULL;
+    }
+  
+    pB = pE->bucket;
+  
+    if (pB->value == key) {
+        return NULL;
+    }
+    if(key > pGB->MaxValue || key < -pGB->MaxValue) {
+        fprintf(stderr, "BucketMove(): Invalid key!\n");
+        return NULL;
+    }
+    
+    /*## Remove this entry from the bucket: ##*/
+    /* Adjust forward links */
+    if (pE->prev != NULL)
+        (pE->prev)->next = pE->next;  
+    else
+        pB->entry = pE->next;
+  
+    /* Adjust backward links */ 
+    if (pE->next != NULL)
+        (pE->next)->prev = pE->prev;
+  
+    /*## Check empty bucket: ##*/
+    if (pB->entry == NULL) {
+        
+        pGB->NrBuckets--;
+        /* If this bucket was the max bucket and the new value is lower, find the new max bucket */
+        if(pB->value == pGB->MaxPresentValue && key < pGB->MaxPresentValue) {
+            pGB->MaxPresentValue = LONG_MIN;
+            for(pB--;pB>=pGB->Root;pB--) {
+                if(pB->entry != NULL) {
+                    pGB->MaxPresentValue = pB->value;
+                    break;
+                }
+                if(pB->value < key)
+                    break;
+            }
+        }
+    }
+    
+    pB = &(pGB->Root[pGB->MaxValue+key]);
+
+    /*## Reassign the bucketentry to the new bucket: ##*/
+  
+    pE->bucket = pB;  
+    pE->prev = NULL;
+    pE->next = pB->entry;
+    
+    if (pE->next != NULL) {
+        (pE->next)->prev = pE;
+    }
+    else {
+        pGB->NrBuckets++;
+        if(key > pGB->MaxPresentValue)
+            pGB->MaxPresentValue = key;
+    }
+    
+    pB->entry = pE;
+    
+    return pE;
+} /* end BucketMove */
+  
+  
+long BucketDeleteMax(struct gainbucket *pGB) {
+    
+   /* This function deletes the first vertex from the first bucket.
+      This vertex has the maximum gain value. 
+      The function returns the vertex number.
+      The first bucket must exist and it should not be empty.
+      The memory space of the original bucketentry is freed. */
+
+    long VtxNr;
+    struct bucket *pB;
+    struct bucketentry *pE;
+    
+    if (!pGB || !pGB->Root) {
+        fprintf(stderr, "BucketDeleteMax(): Null parameter!\n");
+        return -1;
+    }
+    
+    pB = &(pGB->Root[pGB->MaxValue+pGB->MaxPresentValue]) ;
+    pE = pB->entry;  
+    
+    if (!pB || !pE) {
+        return -1;
+    }
+  
+    VtxNr = pE->vtxnr;
+  
+    /*## Remove this entry from the bucket: ##*/
+    pB->entry = pE->next;
+   
+    if (pE->next != NULL)
+        (pE->next)->prev = NULL;
+    
+    free(pE);
+  
+    /*## If this bucket is now empty, find the new max bucket: ##*/
+    if (pB->entry == NULL) {
+        pGB->NrBuckets--;
+        pGB->MaxPresentValue = LONG_MIN;
+        for(pB--;pB>=pGB->Root;pB--) {
+            if(pB->entry != NULL) {
+                pGB->MaxPresentValue = pB->value;
+                break;
+            }
+        }
+    }
+    
+    return(VtxNr);
+} /* end BucketDeleteMax */
+
+
+long GainBucketGetMaxVal(struct gainbucket *pGB) {
+
+    /* This function gives the value of the vertex with the maximum
+       gain value, if the gainbucket data structure is not empty.
+       Otherwise, it returns LONG_MIN. */
+    
+    if (!pGB || !pGB->Root) {
+        return LONG_MIN;
+    }
+
+    if (pGB->NrBuckets > 0)
+        return((pGB->Root[pGB->MaxValue+pGB->MaxPresentValue]).value);
+    else
+        return(LONG_MIN);
+
+} /* end GainBucketGetMaxVal */
+
+
+long GainBucketGetMaxValVertexNr(struct gainbucket *pGB) {
+    /* This function gives the number of the vertex with the maximum
+       gain value, if the gainbucket data structure is not empty.
+       Otherwise, it returns LONG_MIN. */
+    
+    if (!pGB || !pGB->Root) {
+        return LONG_MIN;
+    }
+    
+    if (pGB->NrBuckets > 0)
+        return(((pGB->Root[pGB->MaxValue+pGB->MaxPresentValue]).entry)->vtxnr);
+    else
+        return(LONG_MIN);
+
+} /* end GainBucketGetMaxValVertexNr */
+
+
+int ClearGainBucket(struct gainbucket *pGB) {
+   /* This function deletes all bucketentries
+      and frees the corresponding memory space.
+      As a result, pGB->NrBuckets = 0. */
+
+    struct bucket *pB;
+    struct bucketentry *pE;
+    
+    if (!pGB) {
+        fprintf(stderr, "ClearGainBucket(): Null parameter!\n");
+        return FALSE;
+    }
+    
+    if(pGB->Root == NULL) {
+        return TRUE;
+    }
+    
+    for(pB=&(pGB->Root[2*pGB->MaxValue]); pB>=pGB->Root; pB--) {
+        if(pB->entry == NULL) {
+            continue;
+        }
+        
+        /*## Remove all entries from this bucket: ##*/  
+        while ((pE = pB->entry) != NULL) {
+            pB->entry = pE->next;
+            free(pE);
+        }
+        
+        pGB->NrBuckets--;
+    }
+    pGB->MaxPresentValue = LONG_MIN;
+    
+    return TRUE;
+} /* end ClearGainBucket */
+
+int DeleteGainBucket(struct gainbucket *pGB) {
+   /* This function deletes the GainBucket structure
+      and frees all corresponding memory space.
+      As a result, pGB->Root = NULL and the structure
+      cannot be used any more until InitGainBucket() is
+      called. */
+    if (!pGB) {
+        fprintf(stderr, "DeleteGainBucket(): Null parameter!\n");
+        return FALSE;
+    }
+    
+    if(!ClearGainBucket(pGB)) {
+        return FALSE;
+    }
+    
+    if(pGB->Root == NULL)
+        return TRUE;
+    
+    free(pGB->Root);
+    pGB->Root = NULL;
+    
+    return TRUE;
+} /* end DeleteGainBucket */
diff --git a/src/GainBucketArray.h b/src/GainBucketArray.h
new file mode 100644
index 0000000000000000000000000000000000000000..73603d2534468faddbf1efe17d9de71e8750ab63
--- /dev/null
+++ b/src/GainBucketArray.h
@@ -0,0 +1,55 @@
+/* This file defines Gainbucket, a data structure
+   which contains numbers of data items and their values. 
+   The data item can be a vertex and the value its gain in a move.
+   The numbers are integers >=0, and the values are integers
+   without restriction. We call the items vertices and their values gains.
+
+   The vertices are sorted in order of decreasing gain.
+   Vertices with the same gain value are stored together in a bucket,
+   implemented as a doubly linked list. The entries of a bucket,
+   representing vertices, are called bucketentries.
+   The list is terminated by NULL at both ends.
+
+   The buckets themselves are stored in an array. */
+
+
+#ifndef __GainBucket_h__
+#define __GainBucket_h__
+
+#include "Options.h" 
+
+struct bucketentry {
+    long vtxnr;            /* vertex number for this bucketentry */
+    struct bucket *bucket; /* pointer to the bucket containing this entry */
+    struct bucketentry *prev; /* pointer to previous bucketentry
+                                  in the list of entries*/
+    struct bucketentry *next; /* pointer to next bucketentry */
+};
+  
+struct bucket {
+    long value;           /* value for the entries in this bucket */
+    struct bucketentry *entry; /* pointer to first bucketentry in this bucket */
+};
+  
+struct gainbucket {
+  long NrBuckets;       /* number of buckets in the gainbucket */
+  long MaxValue;        /* Maximum bucket value possible */
+  long MaxPresentValue; /* Maximum bucket value present */
+  struct bucket *Root;  /* pointer to the first bucket
+                            in the list of buckets */
+};
+
+int InitGainBucket(struct gainbucket *pGB, long maxKey);
+
+struct bucketentry *BucketInsert(struct gainbucket *pGB, long key, long VtxNr);
+struct bucketentry *BucketMove(struct gainbucket *pGB, struct bucketentry *pE,
+                               long key);
+long BucketDeleteMax(struct gainbucket *pGB);
+
+long GainBucketGetMaxVal(struct gainbucket *pGB);
+long GainBucketGetMaxValVertexNr(struct gainbucket *pGB);
+
+int ClearGainBucket(struct gainbucket *pGB);
+int DeleteGainBucket(struct gainbucket *pGB);
+
+#endif /* __GainBucket_h__ */
diff --git a/src/GainBucketList.c b/src/GainBucketList.c
new file mode 100644
index 0000000000000000000000000000000000000000..4a49f966b132bc626a4ea836334801f4afbbcbb3
--- /dev/null
+++ b/src/GainBucketList.c
@@ -0,0 +1,319 @@
+#include "GainBucket.h"
+
+int InitGainBucket(struct gainbucket *pGB, long MaxValue) {
+    /* Initialize empty GainBucket structure.
+       Must be called before using any other gainbucket method. */
+    
+    /* Empty, this linked list implementation does not require initialization */
+    return TRUE;
+} /* end InitGainBucket */
+
+
+struct bucketentry *_BucketInsert(struct gainbucket *pGB, struct bucketentry *pE, long key, long VtxNr) {
+
+    /* Private function, to be used by functions in this file only.
+       This function inserts vertex number VtxNr in the gainbucket GB,
+       in the appropriate bucket with key value. The vertex must not be
+       present already. It uses the bucket entry pE provided by the
+       calling function. */
+
+    struct bucket *pB, **ppB;
+    
+    if (!pGB || !pE) {
+        fprintf(stderr, "_BucketInsert(): Null argument!\n");
+        return NULL;
+    }
+    
+    ppB = &(pGB->Root);
+    pB = *ppB;
+  
+    /*## While the key is smaller than the value in the buckets, 
+         go through the bucket list: ##*/
+    while (*ppB != NULL) {
+        pB = *ppB; /* pB points to current bucket */
+  
+        if (key < pB->value)
+            ppB = &(pB->next); /* points to next bucket */
+        else 
+            break;
+    }
+  
+    if (pB == NULL) {
+        /*## Bucket list is empty. Create first bucket: ##*/
+  
+        *ppB = (struct bucket *) malloc(sizeof(struct bucket));
+        
+        if (*ppB == NULL) {
+            fprintf(stderr, "_BucketInsert(): Not enough memory for first bucket!\n");
+            return NULL;
+        }
+        
+        (*ppB)->value = key;
+        (*ppB)->entry = NULL;
+        (*ppB)->prev = (*ppB)->next = NULL;
+  
+        pGB->NrBuckets++;
+    } else if (key < pB->value) { 
+        /*## Create new bucket at the end of the current list: ##*/
+        /* *ppB == NULL */
+  
+        *ppB = (struct bucket *) malloc(sizeof(struct bucket));
+        
+        if (*ppB == NULL) {
+            fprintf(stderr, "_BucketInsert(): Not enough memory for new bucket!\n");
+            return NULL;
+        }
+  
+        (*ppB)->value = key;
+        (*ppB)->entry = NULL;
+        (*ppB)->prev = pB;
+        (*ppB)->next = NULL;
+  
+        pGB->NrBuckets++;  
+    } else if (key > pB->value) {
+        /*## Create new bucket between the previous and the current
+             bucket in the list: ##*/
+  
+        *ppB = (struct bucket *) malloc(sizeof(struct bucket));
+        
+        if (*ppB == NULL) {
+            fprintf(stderr, "_BucketInsert(): Not enough memory for new bucket!\n");
+            return NULL;
+        }
+      
+        (*ppB)->value = key;
+        (*ppB)->entry = NULL;
+        (*ppB)->prev = pB->prev;
+        (*ppB)->next = pB;
+     
+        if (pB->prev != NULL)
+            (pB->prev)->next = *ppB;
+        else
+            pGB->Root = *ppB;
+      
+        pB->prev = *ppB;
+        pGB->NrBuckets++;
+    }
+        /* otherwise key == pB->value (and hence pB == *ppB) */
+
+
+    /*## Insert the vertex number into a new bucketentry at
+         the start of the bucket: ##*/
+  
+    pE->vtxnr = VtxNr;
+    pE->bucket = *ppB;  
+    pE->prev = NULL;
+    pE->next = (*ppB)->entry;
+    
+    if (pE->next != NULL)
+        (pE->next)->prev = pE;
+    
+    (*ppB)->entry = pE;
+
+    return pE;
+} /* end _BucketInsert */
+
+struct bucketentry *BucketInsert(struct gainbucket *pGB, long key, long VtxNr) {
+
+    /* This function inserts vertex number VtxNr in the gainbucket GB,
+       in the appropriate bucket with key value. The vertex must not be
+       present already. A bucketentry representing VtxNr is allocated
+       and a pointer to this bucketentry is returned. */
+
+    struct bucketentry *pE;
+    
+    pE = (struct bucketentry *) malloc(sizeof(struct bucketentry));
+    
+    if (pE == NULL) {
+        fprintf(stderr, "BucketInsert(): Not enough memory for bucket entry!\n");
+        return NULL;
+    }
+    
+    return _BucketInsert(pGB, pE, key, VtxNr);
+    
+} /* end BucketInsert */
+
+
+struct bucketentry *BucketMove(struct gainbucket *pGB, struct bucketentry *pE,
+                               long key) {
+  
+   /* This function moves the vertex number represented by bucketentry pE
+      in the gainbucket GB to the appropriate bucket with key value. 
+      The key value must differ from the current value.  
+      A pointer to the bucketentry is returned.
+      If the original bucket has become empty, the space of the bucket is freed. */
+  
+    long VtxNr;
+    struct bucket *pB;
+    
+    if (!pGB || !pE) {
+        fprintf(stderr, "BucketMove(): Null arguments!\n");
+        return NULL;
+    }
+  
+    VtxNr = pE->vtxnr;
+    pB = pE->bucket;
+  
+    if (pB->value == key) {
+        fprintf(stderr, "BucketMove(): Destination bucket equals source!\n");
+        return NULL;
+    }
+  
+    /*## Remove this entry from the bucket: ##*/
+    /* Adjust forward links */
+    if (pE->prev != NULL)
+        (pE->prev)->next = pE->next;  
+    else
+        pB->entry = pE->next;
+  
+    /* Adjust backward links */ 
+    if (pE->next != NULL)
+        (pE->next)->prev = pE->prev;
+  
+    /*## If this bucket is now empty, remove it from the list: ##*/
+    if (pB->entry == NULL) {      
+        if (pB->prev != NULL)
+             (pB->prev)->next = pB->next;
+        else
+             pGB->Root = pB->next;
+  
+        if (pB->next != NULL)
+             (pB->next)->prev = pB->prev;
+        
+        if (pB != NULL)
+          free(pB);
+        pGB->NrBuckets--;
+    }
+    
+    /*## Insert the vertex in the bucket of the new key: ##*/
+    return _BucketInsert(pGB, pE, key, VtxNr);
+    
+} /* end BucketMove */
+  
+  
+long BucketDeleteMax(struct gainbucket *pGB) {
+  
+   /* This function deletes the first vertex from the first bucket.
+      This vertex has the maximum gain value. 
+      The function returns the vertex number.
+      The first bucket must exist and it should not be empty.
+      The memory space of the original bucketentry is freed.
+      If its bucket has become empty, the space of the bucket is also freed. */
+ 
+
+    long VtxNr;
+    struct bucket *pB;
+    struct bucketentry *pE;
+    
+    if (!pGB) {
+        fprintf(stderr, "BucketDeleteMax(): Null arguments!\n");
+        return -1;
+    }
+    
+    pB = pGB->Root ;
+    pE = pB->entry;  
+    
+    if (!pB || !pE) {
+        fprintf(stderr, "BucketDeleteMax(): Internal error!\n");
+        return -1;
+    }
+  
+    VtxNr = pE->vtxnr;
+  
+    /*## Remove this entry from the bucket: ##*/
+    pB->entry = pE->next;
+   
+    if (pE->next != NULL)
+        (pE->next)->prev = NULL;
+  
+    free(pE);
+  
+    /*## If this bucket is now empty, remove it from the list: ##*/
+    if (pB->entry == NULL) {      
+        pGB->Root = pB->next;
+  
+        if (pB->next != NULL)
+            (pB->next)->prev = NULL;
+        
+        free(pB);
+        pGB->NrBuckets--;
+    }
+  
+    return(VtxNr);
+} /* end BucketDeleteMax */
+
+
+long GainBucketGetMaxVal(struct gainbucket *pGB) {
+
+    /* This function gives the value of the vertex with the maximum
+       gain value, if the gainbucket data structure is not empty.
+       Otherwise, it returns LONG_MIN. */
+    if (!pGB) return LONG_MIN;
+
+    if (pGB->NrBuckets > 0)
+        return((pGB->Root)->value);
+    else
+        return(LONG_MIN);
+
+} /* end GainBucketGetMaxVal */
+
+
+long GainBucketGetMaxValVertexNr(struct gainbucket *pGB) {
+
+    /* This function gives the number of the vertex with the maximum
+       gain value, if the gainbucket data structure is not empty.
+       Otherwise, it returns LONG_MIN. */
+    
+    if (!pGB) return LONG_MIN;
+    
+    if (pGB->NrBuckets > 0)
+        return(((pGB->Root)->entry)->vtxnr);
+    else
+        return(LONG_MIN);
+
+} /* end GainBucketGetMaxValVertexNr */
+
+
+int ClearGainBucket(struct gainbucket *pGB) {
+
+   /* This function deletes all vertices and buckets
+      and frees the corresponding memory space.
+      As a result, pGB->Root = NULL and pGB->NrBuckets = 0. */
+
+    struct bucket *pB;
+    struct bucketentry *pE;
+    
+    if (!pGB) {
+        fprintf(stderr, "ClearGainBucket(): Null argument!\n");
+        return FALSE;
+    }
+    
+    while ((pB = pGB->Root) != NULL) {      
+        pGB->Root = pB->next;
+  
+        /*## Remove all entries from this bucket: ##*/  
+        while ((pE = pB->entry) != NULL) {
+            pB->entry = pE->next;
+            free(pE);
+        }
+  
+        /*## Remove this bucket from the list: ##*/
+        free(pB);
+        pGB->NrBuckets--;
+    }
+
+    return TRUE;
+} /* end ClearGainBucket */
+
+
+int DeleteGainBucket(struct gainbucket *pGB) {
+   /* This function deletes the GainBucket structure
+      and frees all corresponding memory space.
+      As a result, pGB->Root = NULL and the structure
+      cannot be used any more until InitGainBucket() is
+      called. */
+    
+    /* Empty, this linked list implementation does not
+       need to be cleared.*/
+    return TRUE;
+} /* end DeleteGainBucket */
diff --git a/src/GainBucketList.h b/src/GainBucketList.h
new file mode 100644
index 0000000000000000000000000000000000000000..703f1629ff0d730d4703e98281a15356a93f722e
--- /dev/null
+++ b/src/GainBucketList.h
@@ -0,0 +1,57 @@
+/* This file defines Gainbucket, a data structure
+   which contains numbers of data items and their values. 
+   The data item can be a vertex and the value its gain in a move.
+   The numbers are integers >=0, and the values are integers
+   without restriction. We call the items vertices and their values gains.
+
+   The vertices are sorted in order of decreasing gain.
+   Vertices with the same gain value are stored together in a bucket,
+   implemented as a doubly linked list. The entries of a bucket,
+   representing vertices, are called bucketentries.
+   The list is terminated by NULL at both ends.
+
+   The buckets themselves are also linked in a doubly linked list.
+   This list is also terminated by NULL at both ends. */
+
+
+#ifndef __GainBucket_h__
+#define __GainBucket_h__
+
+#include "Options.h" 
+
+struct bucketentry {
+    long vtxnr;            /* vertex number for this bucketentry */
+    struct bucket *bucket; /* pointer to the bucket containing this entry */
+    struct bucketentry *prev; /* pointer to previous bucketentry
+                                  in the list of entries*/
+    struct bucketentry *next; /* pointer to next bucketentry */
+};
+  
+struct bucket {
+    long value;           /* value for the entries in this bucket */
+    struct bucket *prev;  /* pointer to previous bucket
+                              in the list of buckets */
+    struct bucket *next;  /* pointer to next bucket */
+    struct bucketentry *entry; /* pointer to first bucketentry in this bucket */
+};
+  
+struct gainbucket {
+  long NrBuckets;       /* number of buckets in the gainbucket */
+  struct bucket *Root;  /* pointer to the first bucket
+                            in the list of buckets */
+};
+
+int InitGainBucket(struct gainbucket *pGB, long maxKey);
+
+struct bucketentry *BucketInsert(struct gainbucket *pGB, long key, long VtxNr);
+struct bucketentry *BucketMove(struct gainbucket *pGB, struct bucketentry *pE,
+                               long key);
+long BucketDeleteMax(struct gainbucket *pGB);
+
+long GainBucketGetMaxVal(struct gainbucket *pGB);
+long GainBucketGetMaxValVertexNr(struct gainbucket *pGB);
+
+int ClearGainBucket(struct gainbucket *pGB);
+int DeleteGainBucket(struct gainbucket *pGB);
+
+#endif /* __GainBucket_h__ */
diff --git a/src/Graph.c b/src/Graph.c
index 313f853488e1c9cfcfb5aba0922886bd31659d46..5735d39e8a63aa45cd6c2a2c24de8ef7c205959d 100644
--- a/src/Graph.c
+++ b/src/Graph.c
@@ -117,7 +117,8 @@ int CreateNewBiPartHyperGraph(long NrVertices, long NrNets,
         pHG->V[t].iStart = 0;
         pHG->V[t].iEnd = 0;
         pHG->V[t].partition = 0;
-        pHG->V[t].GBentry = NULL;      
+        pHG->V[t].Free = FALSE;
+        pHG->V[t].GBentry = NULL;
         pHG->OptPartVtx[t] = 0;
         if (StoreMat)
             pHG->Vtx2MatIndex[t] = 0;
diff --git a/src/HKLFM.c b/src/HKLFM.c
index d298425a2b8c70e70be804e168d362ff222ae1b3..2275091a5989599b69eb177096e08a22c80b6a8d 100644
--- a/src/HKLFM.c
+++ b/src/HKLFM.c
@@ -528,6 +528,22 @@ int HKLFM(struct biparthypergraph *pHG, long weightlo, long weighthi,
         return FALSE;
     }
     
+#ifdef GAINBUCKET_ARRAY
+    /* Compute maximum number of nets per vertex. */
+    long NrNets, MaxNrNets = 0;
+    for (v = 0; v < pHG->NrVertices; v++) {
+        NrNets = pHG->V[v].iEnd-pHG->V[v].iStart;
+        if(NrNets > MaxNrNets) {
+            MaxNrNets = NrNets;
+        }
+    }
+    if ( !InitGainBucket(&(pHG->GBVtx[0]), MaxNrNets) ||
+         !InitGainBucket(&(pHG->GBVtx[1]), MaxNrNets) ) {
+        fprintf(stderr, "HKLFM(): Could not initialize gain buckets!\n");
+        return FALSE;
+    }
+#endif
+    
     /* Main loop */
     iter = 0;
     while (iter < MaxNrLoops && (iter == 0 || pHG->MinComm != LastComm)) {
@@ -669,6 +685,14 @@ int HKLFM(struct biparthypergraph *pHG, long weightlo, long weighthi,
         iter++;
     } /* end main loop */
 
+#ifdef GAINBUCKET_ARRAY
+    if ( !DeleteGainBucket(&(pHG->GBVtx[0])) ||
+         !DeleteGainBucket(&(pHG->GBVtx[1])) ) {
+        fprintf(stderr, "HKLFM(): Could not initialize gain buckets!\n");
+        return FALSE;
+    }
+#endif
+    
     return TRUE;
 } /* end HKLFM */
 
diff --git a/src/Heap.c b/src/Heap.c
new file mode 100644
index 0000000000000000000000000000000000000000..e9172be42692e5984aec253e9ca914b3bc03cf27
--- /dev/null
+++ b/src/Heap.c
@@ -0,0 +1,291 @@
+
+#include <string.h>
+#include "Heap.h"
+
+/* This is our own implementation of a heap, with some ideas inspired by
+ *  -> https://gist.github.com/martinkunev/1365481
+ *  -> https://github.com/robin-thomas/max-heap/blob/master/maxHeap.c
+ */
+
+#define LCHILD(x) (2*(x) + 1)
+#define RCHILD(x) (2*(x) + 2)
+#define PARENT(x) (((x) - 1)/2)
+
+
+/**
+ * Initialize a heap struct
+ * A heap consists of items, and offers the ability to retrieve
+ * the maximum/minimum item efficiently, depending on the comparison
+ * function given.
+ * 
+ * Input:
+ * itemSize         : The number of bytes one item consists of
+ * compare          : Comparison function to compare items
+ * 
+ * Input/Output:
+ * pHeap            : The heap struct
+ */
+void HeapInit(struct heap *pHeap, long itemSize, int (*compare)(const void *, const void*)) {
+    pHeap->numItems = 0;
+    pHeap->size = 0;
+    pHeap->itemSize = itemSize;
+    pHeap->items = NULL;
+    pHeap->compare = compare;
+}
+
+/**
+ * Free a heap struct
+ * 
+ * Input/Output:
+ * pHeap            : The heap struct
+ */
+void HeapDestroy(struct heap *pHeap) {
+    pHeap->numItems = 0;
+    pHeap->size = 0;
+    if(pHeap->items != NULL) {
+        free(pHeap->items);
+    }
+    pHeap->items = NULL;
+}
+
+/**
+ * Check the size of a heap. If the number of items in it
+ * is equal to the maximum size, extend the maximum size.
+ * 
+ * Input/Output:
+ * pHeap            : The heap struct
+ */
+void HeapCheckSize(struct heap *pHeap) {
+    if(pHeap->items == NULL) {
+        pHeap->size = 8;
+        pHeap->items = malloc(pHeap->size * pHeap->itemSize);
+        if(pHeap->items == NULL) {
+            fprintf(stderr, "HeapPush(): Out of memory!\n");
+            exit(1);
+        }
+    }
+    else if(pHeap->numItems >= pHeap->size) {
+        pHeap->size *= 2;
+        char *newAlloc = realloc(pHeap->items, pHeap->size * pHeap->itemSize);
+        if(newAlloc == NULL) {
+            fprintf(stderr, "HeapPush(): Out of memory!\n");
+            exit(1);
+        }
+        pHeap->items = newAlloc;
+    }
+}
+
+/**
+ * Copy the top element to item.
+ * 
+ * Input:
+ * pHeap            : The heap struct
+ * 
+ * Output:
+ * item             : Memory space to copy the top item to
+ * 
+ * Returns FALSE on error, TRUE otherwise
+ */
+int HeapPeek(struct heap *pHeap, void * const item) {
+    if(pHeap->numItems <= 0 || item == NULL)
+        return FALSE;
+    
+    memcpy(item, &pHeap->items[0], pHeap->itemSize);
+    return TRUE;
+}
+
+/**
+ * Copy the top element to item, and remove it from the heap.
+ * 
+ * Input/Output:
+ * pHeap            : The heap struct
+ * 
+ * Output:
+ * item             : Memory space to copy the top item to
+ * 
+ * Returns FALSE on error, TRUE otherwise
+ */
+int HeapPop(struct heap *pHeap, void * const item) {
+    if(pHeap->numItems <= 0)
+        return FALSE;
+    
+    /* Special case: the heap becomes empty */
+    if(pHeap->numItems == 1) {
+        --pHeap->numItems;
+        
+        if(item != NULL) {
+            memcpy(item, &pHeap->items[0], pHeap->itemSize);
+        }
+        return TRUE;
+    }
+    
+    /* The heap consists of at least two items; we can safely call HeapReplace */
+    return HeapReplace(pHeap, &pHeap->items[(--pHeap->numItems) * pHeap->itemSize], item);
+}
+
+/**
+ * Insert a new item into the heap
+ * 
+ * Input/Output:
+ * pHeap            : The heap struct
+ * 
+ * Input:
+ * new              : The item to insert into the heap
+ */
+void HeapPush(struct heap *pHeap, void * const item) {
+    HeapCheckSize(pHeap);
+    
+    memcpy(&pHeap->items[pHeap->numItems * pHeap->itemSize], item, pHeap->itemSize);
+    HeapSiftUp(pHeap, pHeap->numItems);
+    
+    ++pHeap->numItems;
+}
+
+/**
+ * Copy the top element to item, and replace it by another item.
+ * 
+ * Input/Output:
+ * pHeap            : The heap struct
+ * 
+ * Input:
+ * new              : The item to replace the top item with
+ * 
+ * Output:
+ * old              : Memory space to copy the old top item to
+ * 
+ * Returns FALSE on error, TRUE otherwise
+ */
+int HeapReplace(struct heap *pHeap, void * const new, void * const old) {
+    if(pHeap->numItems <= 0)
+        return FALSE;
+    
+    if(old != NULL) {
+        memcpy(old, &pHeap->items[0], pHeap->itemSize);
+    }
+    
+    if(&pHeap->items[0] != new) {
+        memcpy(&pHeap->items[0], new, pHeap->itemSize);
+    
+        HeapSiftDown(pHeap, 0);
+    }
+    
+    return TRUE;
+}
+
+/**
+ * Sift up an item to its right position. This is used when an element at a leaf is modified.
+ * 
+ * Input:
+ * item             : The item to sift up
+ * 
+ * Input/Output:
+ * pHeap            : The heap struct
+ */
+void HeapSiftUp(struct heap *pHeap, long item) {
+    
+    char *base_ptr = pHeap->items;
+    size_t itemSize = pHeap->itemSize;
+    
+    long index,     /* The item we are currently bubbling at */
+         swap;      /* The item to swap the bubble with */
+    
+    char value[itemSize];
+    
+    memcpy(value, &base_ptr[itemSize * item], itemSize);
+    
+    for(index = item; TRUE; index = swap) {
+        if(index == 0)
+            break;
+        
+        swap = PARENT(index);
+        
+        if(pHeap->compare(value, &base_ptr[itemSize * swap]) <= 0) {
+            break;
+        }
+        
+        memcpy(&base_ptr[itemSize * index], &base_ptr[itemSize * swap], itemSize);
+    }
+    
+    if(index != item) {
+        memcpy(&base_ptr[itemSize * index], value, itemSize);
+    }
+    
+}
+
+/**
+ * Sift down an item to its right position. This is used when the element at the root is modified.
+ * 
+ * Input:
+ * item             : The item to sift down
+ * 
+ * Input/Output:
+ * pHeap            : The heap struct
+ */
+void HeapSiftDown(struct heap *pHeap, long item) {
+    
+    char *base_ptr = pHeap->items;
+    size_t itemSize = pHeap->itemSize;
+    
+    long index,     /* The item we are currently bubbling at */
+         swap,      /* The item to swap the bubble with */
+         childL,    /* Children of the bubble */
+         childR;
+    
+    char value[itemSize];
+    
+    memcpy(value, &base_ptr[itemSize * item], itemSize);
+    
+    for(index = item; TRUE; index = swap) {
+        
+        childL = LCHILD(index);
+        if(childL >= pHeap->numItems)
+            break; /* This node has no children */
+        childR = RCHILD(index);
+        
+        swap = childL;
+        if(childR < pHeap->numItems && pHeap->compare(&base_ptr[itemSize * childR], &base_ptr[itemSize * childL]) >= 0) {
+            swap = childR;
+        }
+        
+        if(pHeap->compare(value, &base_ptr[itemSize * swap]) >= 0) {
+            break;
+        }
+        
+        memcpy(&base_ptr[itemSize * index], &base_ptr[itemSize * swap], itemSize);
+        
+    }
+    
+    if(index != item) {
+        memcpy(&base_ptr[itemSize * index], value, itemSize);
+    }
+    
+    
+}
+
+/**
+ * Turn an array into a heap
+ * 
+ * Input:
+ * numItems         : The number of items in the array
+ * 
+ * Input/Output:
+ * items            : The array (will likely be permutated)
+ * pHeap            : The heap struct
+ */
+void Heapify(struct heap *pHeap, void * const items, long numItems) {
+    pHeap->items = (char *)items;
+    pHeap->numItems = numItems;
+    pHeap->size = numItems;
+    
+    long item; /* The item we want to move into place */
+    
+    for(item = PARENT(pHeap->numItems-1); item >= 0; --item) {
+        HeapSiftDown(pHeap, item);
+    }
+    
+}
+
+
+#undef LCHILD
+#undef RCHILD
+#undef PARENT
diff --git a/src/Heap.h b/src/Heap.h
new file mode 100644
index 0000000000000000000000000000000000000000..769fead6ea4c2e9edf583f1542acf470d1a5ae2c
--- /dev/null
+++ b/src/Heap.h
@@ -0,0 +1,28 @@
+#ifndef __Heap_h__
+#define __Heap_h__
+
+#include "Sort.h"
+
+struct heap {
+    long numItems;
+    long size;
+    
+    char *items;
+    size_t itemSize;
+    int (*compare)(const void *, const void*);
+};
+
+/* Function declarations for Heap.c */
+void HeapInit(struct heap *pHeap, long itemSize, int (*compare)(const void *, const void*));
+void HeapDestroy(struct heap *pHeap);
+void HeapCheckSize(struct heap *pHeap);
+int HeapPeek(struct heap *pHeap, void * const item);
+int HeapPop(struct heap *pHeap, void * const item);
+void HeapPush(struct heap *pHeap, void * const item);
+int HeapReplace(struct heap *pHeap, void * const new, void * const old);
+void HeapSiftUp(struct heap *pHeap, long item);
+void HeapSiftDown(struct heap *pHeap, long item);
+void Heapify(struct heap *pHeap, void * const items, long numItems);
+
+#endif /* __Heap_h__ */
+
diff --git a/src/Makefile b/src/Makefile
index 06723bfe36e854075ef187d652c9c686016f8ad6..902902de9f1c78f02a7923c5b6c3e00c3f783af8 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -19,7 +19,7 @@ MATCHINPRODUCT=MatchInproduct ${OPTIONS} ${MATCH} ${GRAPH}
 MATCHSTAIRWAY=MatchStairway ${OPTIONS} ${MATCH} ${GRAPH} ${SORT}
 MATCH=Match ${OPTIONS} ${GRAPH}
 HKLFM=HKLFM ${OPTIONS} ${GRAPH} ${GAINBUCKET} ${MATCH} ${MATCHMATCHERS} ${MATCHSTAIRWAY} ${MATCHINPRODUCT}
-DISTRIBUTEMAT=DistributeMat ${GRAPH} ${HKLFM} ${PERMUTE} ${DISTR_DEPS}
+DISTRIBUTEMAT=DistributeMat SplitMatrixUpperBound ${GRAPH} ${HKLFM} ${PERMUTE} ${DISTR_DEPS}
 DISTRIBUTEVECLIB=DistributeVecLib ${OPTIONS} ${SPARSEMATRIX}
 DISTRIBUTEVECGREEDY=DistributeVecGreedy ${OPTIONS} ${SORT} ${DISTRIBUTEVECLIB}
 MATALLOC=Matalloc ${OPTIONS}
@@ -30,9 +30,13 @@ DISTRIBUTEVEC=DistributeVec ${DISTR_DEPS} ${DISTRIBUTEVECGREEDY} \
     ${DISTRIBUTEVECLOCAL} ${DISTRIBUTEVECOPT2} ${DISTRIBUTEVECORIG} \
     ${DISTRIBUTEVECLIB}
 DISTRIBUTEVECORIGEQ=DistributeVecOrigEq ${DISTRIBUTEVECORIG}
-DISTRIBUTE=${DISTRIBUTEMAT} ${DISTRIBUTEVEC} ${DISTRIBUTEVECORIGEQ}
+HEAP=Heap
+SUBSETSUM=SubsetSum ${HEAP}
+ZEROVOLUMESEARCH=ZeroVolumeSearch ${SUBSETSUM}
+DISTRIBUTE=${DISTRIBUTEMAT} ${DISTRIBUTEVEC} ${DISTRIBUTEVECORIGEQ} ${ZEROVOLUMESEARCH}
 CARTESIAN=Cartesian ${DISTRIBUTEVECLIB} ${SPARSEMATRIX}
-MONDRIAANLIBRARY=${DISTRIBUTE} ${CARTESIAN}
+FREENONZEROS=FreeNonzeros
+MONDRIAANLIBRARY=${DISTRIBUTE} ${CARTESIAN} ${FREENONZEROS}
 
 #targets
 lib/libMondriaan${MONDRIAANMAJORVERSION}.a: ${MONDRIAANLIBRARY:%=%.c} ${MONDRIAANLIBRARY:%=%.o} Mondriaan.h
diff --git a/src/Match.c b/src/Match.c
index 98827418ad14bf8b77bc343899498ff0473412f8..0b47478b8979155f39c1017855274543667137b3 100644
--- a/src/Match.c
+++ b/src/Match.c
@@ -50,6 +50,55 @@ int MoveVtxInNetAdjncy(struct biparthypergraph *pHG, const long v) {
     return TRUE;
 } /* end MoveVtxInNetAdjncy */
 
+int MoveVtxBackInNetAdjncy(struct biparthypergraph *pHG, const long v) {
+
+    /* This function moves the vertex v to the start of the adjacency list
+       in all its nets, so that it is eligible again for matching.
+       This undoes what MoveVtxInNetAdjncy() does.
+       
+       This function does not exactly revert the modifications done by
+       MoveVtxInNetAdjncy(). I.e., applying both functions directly after
+       each other will likely lead to the adjacency lists being permuted.
+
+       On input, v must be stored somewhere in the range iStartP1..iEnd-1
+       for all its nets. On output, iStartP1 has been increased by 1
+       for all the nets of v, and the index of v equals the old iStartP1.
+        
+       @see MoveVtxInNetAdjncy()
+    */
+  
+    long t, n, v2, iv, iv2;
+    
+    if (!pHG) {
+        fprintf(stderr, "MoveVtxBackInNetAdjncy(): Null argument!\n");
+        return FALSE;
+    }
+ 
+    for (t = pHG->V[v].iStart; t < pHG->V[v].iEnd; t++) {
+        n = pHG->VtxAdjncy[t];
+ 
+        /* Find vertex v in adjacency list of net n */
+        iv = pHG->N[n].iEnd-1; /* index of v */
+        
+        while (pHG->NetAdjncy[iv] != v && iv >= pHG->N[n].iStartP1)
+            iv--;
+        
+        if (iv < pHG->N[n].iStartP1) {
+            fprintf(stderr, "MoveVtxBackInNetAdjncy(): vertex v (%ld) not found in range!\n", v);
+            /*fprintf(stderr, "Vtx2Index: %ld, Net2Index: %ld\n", pHG->Vtx2MatIndex[v], pHG->Net2MatIndex[n] );*/
+            return FALSE;
+        }
+  
+        /* Swap vertex:  v <-> Net[n].iStartP1  */
+        iv2 = pHG->N[n].iStartP1;
+        v2 = pHG->NetAdjncy[iv2];
+        pHG->NetAdjncy[iv2] = v;
+        pHG->NetAdjncy[iv] = v2;
+        pHG->N[n].iStartP1++;
+    }
+
+    return TRUE;
+} /* end MoveVtxBackInNetAdjncy */
 
 int FindMatchArbitrary(struct biparthypergraph *pHG, struct contraction *pC, const long v, int *Matched) {
     /* This function matches the vertex v repeatedly with an arbitrary
diff --git a/src/Match.h b/src/Match.h
index d48628cc893393748a953207fad3a83555be764e..f33d22744094804f3fc71cf16dc4ef272dd71eae 100644
--- a/src/Match.h
+++ b/src/Match.h
@@ -19,6 +19,7 @@ struct contraction {
 
 /*** Function declarations for Match.c ***/
 int MoveVtxInNetAdjncy(struct biparthypergraph *pHG, const long v);
+int MoveVtxBackInNetAdjncy(struct biparthypergraph *pHG, const long v);
 int FindMatchArbitrary(struct biparthypergraph *pHG, struct contraction *pC,
                         const long v, int *Matched);
 int FindMatchInprod(struct biparthypergraph *pHG, struct contraction *pC,
diff --git a/src/MatchMatchers.c b/src/MatchMatchers.c
index fc55e6b8b0d30f24bc89e40c606cb9ecdd65bb26..fc3287d07027d5f8c7a6a802c0e38631a9268851 100644
--- a/src/MatchMatchers.c
+++ b/src/MatchMatchers.c
@@ -100,94 +100,108 @@ int MatchUsingGreedy(struct biparthypergraph *pHG, struct contraction *pC,
     return TRUE;
 } /* end MatchUsingGreedy */
 
-
-long FindOptimalPathMatching(long *matchings[3], const double *PathWeights, const long PathLength)
+long FindOptimalPathMatching(char **Build, double *Weight[2], char *MatchingOpt,
+                             const double *PathWeights, const long NrVtxInPath, const long MaxNrVtxInMatch)
 {
-    /* This function uses dynamic programming to create an optimal matching on the given path and adds this matching to the given list of contractions. */
-    /* Based in Fig. 7 from Maue and Sanders (2007). */
-    double weights[2];
-    long sizes[2];
-    long t;
-    int cur = 1;
-    long offset = 0;
-    
-    /* M[0] = empty. */
-    weights[0] = 0.0;
-    sizes[0] = 0;
-    
-    /* M[1] = first edge. */
-    weights[1] = PathWeights[0];
-    sizes[1] = 1;
-    matchings[1][0] = 0;
-    
-    for (t = 1; t < PathLength - 1; t++) {
-        if (PathWeights[t] + weights[1 - cur] > weights[cur]) {
-            weights[1 - cur] += PathWeights[t];
-            matchings[1 - cur][sizes[1 - cur]++] = t;
-        } else {
-            weights[1 - cur] = weights[cur];
-            sizes[1 - cur] = sizes[cur];
-            
-            for ( ; offset < sizes[cur]; offset++) {
-                matchings[2][offset] = matchings[cur][offset];
-            }
-        }
-        
-        cur = 1 - cur;
-    }
+    /* This function uses dynamic programming to create an optimal (generalized) matching on the given path.
+     * Build           : Working array for constructing the maximum matching.
+     * Weight          : Working array for registering accumulated weights during the process.
+     * MatchingOpt     : Working array to return the maximum matching in (output).
+     * PathWeights     : Weights of the edges
+     * NrVtxInPath     : Number of vertices in the given path (= number of edges/weights plus 1)
+     * MaxNrVtxInMatch : Maximum number of vertices in a generalized match (2 for the standard maximum matching problem)
+     */
     
-    for ( ; offset < sizes[cur]; offset++) {
-        matchings[2][offset] = matchings[cur][offset];
+    long t, k, prev, curr;
+    
+    /* Change from number of vertices to number of edges */
+    const long PathLength = NrVtxInPath-1;
+    const long MaxCnsqEdges = MaxNrVtxInMatch-1;
+    
+    /* Initialize */
+    Weight[0][0] = 0;
+    for(k=1; k<=MaxCnsqEdges; ++k) {
+        Weight[0][k] = PathWeights[0];
+        Build[0][k] = 1;
     }
-
-#ifdef INFO
-    /* Sanity check. */
-    if (TRUE) {
-        double oddeven[2] = {0.0, 0.0};
-        double opt = 0.0;
+    
+    /* Walk through path */
+    for(t=1; t<PathLength; ++t) {
+        curr = t%2; prev = (curr+1)%2;
         
-        for (t = 0; t < PathLength - 1; t++) {
-            oddeven[t & 1] += PathWeights[t];
-        }
+        Weight[curr][0] = Weight[prev][MaxCnsqEdges];
         
-        for (t = 0; t < offset; t++) {
-            opt += PathWeights[matchings[2][t]];
+        for(k=1; k<=MaxCnsqEdges; ++k) {
+            if(Weight[prev][k-1]+PathWeights[t] > Weight[curr][0]) {
+                Weight[curr][k] = Weight[prev][k-1]+PathWeights[t];
+                Build[t][k] = 1;
+            }
+            else {
+                Weight[curr][k] = Weight[curr][0];
+                Build[t][k] = 0;
+            }
         }
-        
-        /* Matching should be at least as good as all even or all odd edges. */
-        if (oddeven[0] > opt || oddeven[1] > opt || weights[1 - cur] > opt || weights[cur] > opt) {
-            fprintf(stderr, "FindOptimalPathMatching(): Weight sanity check failed!\n");
-            return -1;
+    }
+    
+    /* Build[PathLength-1][MaxCnsqEdges] now contains the maximum matching,
+     * with total value equal to Weight[(PathLength-1)%2][MaxCnsqEdges].
+     * Backtrack from there.
+     */
+    
+    long nrMatchedEdges = 0;
+    k = MaxCnsqEdges;
+    for(t=PathLength-1; t>=0; --t) {
+        if(k > 0 && Build[t][k]) {
+            /* We chose to include edge t, and include at most k-1 previous edges */
+            ++nrMatchedEdges;
+            MatchingOpt[t] = 1;
+            --k;
+        }
+        else {
+            /* We chose to not include edge t, so we may include MaxCnsqEdges previous edges */
+            MatchingOpt[t] = 0;
+            k = MaxCnsqEdges;
         }
     }
-#endif
     
-    return offset;
+    return nrMatchedEdges;
 }
 
-int ApplyPathMatching(struct biparthypergraph *pHG, struct contraction *pC, int *Matched,
-                      const long *PathMatching, const long *PathIndices, const long PathMatchingLength)
+int ApplyPathMatching(struct contraction *pC, int *Matched,
+                      const char *PathMatching, const long *PathIndices, const long _PathLength)
 {
-    /* Applies a path matching generated by FindOptimalPathMatching. */
+    /* Applies a (generalized) path matching generated by FindOptimalPathMatching, and adds this matching to the given list of contractions. */
     
-    /* Create groups of two vertices based on this matching. */
     long t;
     
-    for (t = 0; t < PathMatchingLength; t++) {
-        const long v = PathIndices[PathMatching[t]];
-        const long w = PathIndices[PathMatching[t] + 1];
+    /* Change from number of vertices to number of edges */
+    const long PathLength = _PathLength-1;
+    
+    for (t = 0; t < PathLength; ++t) {
+        if(!PathMatching[t]) {
+            continue;
+        }
+        
         const long k = pC->NrMatches;
         
         pC->NrMatches++;
-        pC->Start[k + 1] = pC->Start[k];
-        pC->Match[pC->Start[k + 1]++] = v;
-        pC->Match[pC->Start[k + 1]++] = w;
+        pC->Start[k+1] = pC->Start[k];
+        
+        /* For each included edge, add the first indicent vertex to the (generalized) match */
+        while(t < PathLength && PathMatching[t]) {
+            const long v = PathIndices[t];
+            pC->Match[pC->Start[k+1]++] = v;
+            Matched[v] = TRUE;
+            ++t;
+        }
+        
+        /* The current edge is not included, so add first indicent vertex to
+         * the (generalized) match, and continue to the next match */
+        const long v = PathIndices[t];
+        pC->Match[pC->Start[k+1]++] = v;
         Matched[v] = TRUE;
-        Matched[w] = TRUE;
+        /* Do not increase t, this is done by the for loop */
         
-        /* Remove v and w from the hypergraph. */
-        if (!MoveVtxInNetAdjncy(pHG, v)) return FALSE;
-        if (!MoveVtxInNetAdjncy(pHG, w)) return FALSE;
     }
     
     return TRUE;
@@ -221,7 +235,8 @@ int MatchUsingPGA(struct biparthypergraph *pHG, struct contraction *pC,
     long *Visited = (long *)malloc(pHG->NrVertices*sizeof(long));
     
     /* Working arrays for creating optimal path matchings. */
-    long *PathMatchings[3];
+    char *_PathMatchings = NULL, **PathMatchings = NULL, *PathMatchingOpt = NULL;
+    double *_PathMatchingWeights = NULL, *PathMatchingWeights[2];
     /* Array containing the matching weights along the constructed path. */
     double *PathWeights = (double *)malloc(pHG->NrVertices*sizeof(double));
     /* Array containing the indices of the vertices along this path. */
@@ -232,26 +247,71 @@ int MatchUsingPGA(struct biparthypergraph *pHG, struct contraction *pC,
     void *pData = NULL;
     
     long t, tt;
+
+    /* Check arguments */
+    if (!pHG || !pC || !iv || !Matched || !pOptions) {
+        fprintf(stderr, "MatchUsingPGA(): Null arguments!\n");
+        return FALSE;
+    }
     
-    PathMatchings[0] = (long *)malloc(pHG->NrVertices*sizeof(long));
-    PathMatchings[1] = (long *)malloc(pHG->NrVertices*sizeof(long));
-    PathMatchings[2] = (long *)malloc(pHG->NrVertices*sizeof(long));
+    if (!Ip || !ScIp || !Visited || !PathWeights || !PathIndices) {
+        fprintf(stderr, "MatchUsingPGA(): Not enough memory!\n");
+        return FALSE;
+    }
     
-    if (!pHG || !pC || !iv || !Matched || !pOptions ||
-        !Ip || !ScIp || !Visited ||
-        !PathMatchings[0] || !PathMatchings[1] || !PathMatchings[2] ||
-        !PathWeights || !PathIndices) {
-        fprintf(stderr, "MatchUsingPGA(): Null arguments or not enough memory!\n");
+    long MaxNrVtxInMatch = pOptions->Coarsening_MaxNrVtxInMatch;
+    if(MaxNrVtxInMatch == -1) {
+        MaxNrVtxInMatch = log2(pHG->NrVertices / pOptions->Coarsening_NrVertices);
+        if(MaxNrVtxInMatch < 2) {
+            MaxNrVtxInMatch = 2;
+        }
+        if(MaxNrVtxInMatch > 4) {
+            MaxNrVtxInMatch = 4;
+        }
+    }
+    
+    if (MaxNrVtxInMatch < 2) {
+        fprintf(stderr, "MatchUsingPGA(): Invalid number of vertices per match!\n");
         return FALSE;
     }
     
-    if (pOptions->Coarsening_MaxNrVtxInMatch != 2) {
-        fprintf(stderr, "MatchUsingPGA(): This only works when matching groups of two vertices!\n");
+    /* Allocation for FindOptimalPathMatching()
+     * PathMatchings[t][0] for 'no edge selected at end'
+     * PathMatchings[t][k] for 'max k edges selected at end', where k=1..MaxCnsqEdges
+     * PathMatchingWeights[0/1] for weights in previous and current iteration
+     * PathMatchingOpt for maximum matching
+     */
+    const long MaxCnsqEdges = MaxNrVtxInMatch - 1;
+    
+    /* Set up PathMatchings */
+    _PathMatchings = (char *)malloc((MaxCnsqEdges+1) * pHG->NrVertices * sizeof(char));
+    PathMatchings = (char **)malloc(pHG->NrVertices * sizeof(char *));
+    if (!_PathMatchings || !PathMatchings) {
+        fprintf(stderr, "MatchUsingPGA(): Not enough memory!\n");
+        return FALSE;
+    }
+    for(t=0; t<pHG->NrVertices; ++t) {
+        PathMatchings[t] = &(_PathMatchings[t*(MaxCnsqEdges+1)]);
+    }
+    
+    /* Set up PathMatchingWeights */
+    _PathMatchingWeights = (double *)malloc(2*(MaxCnsqEdges+1) * sizeof(double));
+    if (!_PathMatchingWeights) {
+        fprintf(stderr, "MatchUsingPGA(): Not enough memory!\n");
+        return FALSE;
+    }
+    PathMatchingWeights[0] = &(_PathMatchingWeights[0]);
+    PathMatchingWeights[1] = &(_PathMatchingWeights[MaxCnsqEdges+1]);
+    
+    /* Set up PathMatchingOpt */
+    PathMatchingOpt = (char *)malloc(pHG->NrVertices * sizeof(char));
+    if (!PathMatchingOpt) {
+        fprintf(stderr, "MatchUsingPGA(): Not enough memory!\n");
         return FALSE;
     }
     
     if (!SetupData(&pData, pHG, pOptions)) {
-        fprintf(stderr, "MatchUsingGreedy(): Unable to create neighbor finding data!\n");
+        fprintf(stderr, "MatchUsingPGA(): Unable to create neighbor finding data!\n");
         return FALSE;
     }
     
@@ -279,6 +339,8 @@ int MatchUsingPGA(struct biparthypergraph *pHG, struct contraction *pC,
         PathIndices[0] = CurrentVertex;
         PathLength = 1;
         
+        if (!MoveVtxInNetAdjncy(pHG, CurrentVertex)) return FALSE;
+
         /* Extend the path along the unmatched neighbor that has the highest inner product. */
         while (FindNeighbor(&NextVertex, &NextWeight,
                             pHG, pC, pOptions,
@@ -300,6 +362,8 @@ int MatchUsingPGA(struct biparthypergraph *pHG, struct contraction *pC,
             PathIndices[PathLength] = NextVertex;
             PathLength++;
             
+            if (!MoveVtxInNetAdjncy(pHG, NextVertex)) return FALSE;
+
             CurrentVertex = NextVertex;
             NextVertex = -1;
         }
@@ -311,21 +375,31 @@ int MatchUsingPGA(struct biparthypergraph *pHG, struct contraction *pC,
         
         /* Only match if the path contains at least two vertices. */
         if (PathLength <= 1) {
+            for (tt = 0; tt < PathLength; tt++) {
+                if (!MoveVtxBackInNetAdjncy(pHG, PathIndices[tt])) return FALSE;
+            }
             continue;
         }
         
-        /* Find optimal weight matching along the path using dynamic programming. */
-        PathMatchingLength = FindOptimalPathMatching(PathMatchings, PathWeights, PathLength);
+        /* Find optimal weight (generalized) matching along the path using dynamic programming. */
+        PathMatchingLength = FindOptimalPathMatching(PathMatchings, PathMatchingWeights, PathMatchingOpt,
+                                                     PathWeights, PathLength, MaxNrVtxInMatch);
         
         if (PathMatchingLength < 0) {
             fprintf(stderr, "MatchUsingPGA(): Unable to find a matching along the path!\n");
             return FALSE;
         }
         
-        if (!ApplyPathMatching(pHG, pC, Matched, PathMatchings[2], PathIndices, PathMatchingLength)) {
+        if (!ApplyPathMatching(pC, Matched, PathMatchingOpt, PathIndices, PathLength)) {
             fprintf(stderr, "MatchUsingPGA(): Unable to apply path matching!\n");
             return FALSE;
         }
+        
+        for (tt = 0; tt < PathLength; tt++) {
+            if(!Matched[PathIndices[tt]]) {
+                if (!MoveVtxBackInNetAdjncy(pHG, PathIndices[tt])) return FALSE;
+            }
+        }
     }
     
     /* Greedily extend matching to a maximal matching. */
@@ -376,9 +450,10 @@ int MatchUsingPGA(struct biparthypergraph *pHG, struct contraction *pC,
     free(Visited);
     free(PathWeights);
     free(PathIndices);
-    free(PathMatchings[0]);
-    free(PathMatchings[1]);
-    free(PathMatchings[2]);
+    free(_PathMatchings);
+    free(PathMatchings);
+    free(_PathMatchingWeights);
+    free(PathMatchingOpt);
     
     FreeData(pData);
     
diff --git a/src/MatchMatchers.h b/src/MatchMatchers.h
index 8ceb72a967eed1754bc77b646ea386f29274177d..233a0c1025f8e4514c2c77bc59125439419ff7be 100644
--- a/src/MatchMatchers.h
+++ b/src/MatchMatchers.h
@@ -17,7 +17,8 @@ int MatchUsingGreedy(struct biparthypergraph *pHG, struct contraction *pC,
                      int (*FreeData)(void *));
 
 
-long FindOptimalPathMatching(long *matchings[3], const double *PathWeights, const long PathLength);
+long FindOptimalPathMatching(char **Build, double *Weight[2], char *MatchingOpt,
+                             const double *PathWeights, const long NrVtxInPath, const long MaxNrVtxInMatch);
 
 int MatchUsingPGA(struct biparthypergraph *pHG, struct contraction *pC,
                   const long *iv, int *Matched,
diff --git a/src/Options.c b/src/Options.c
index c90c64616fa314a92f8b625fae10a573f856aa92..ad3548fb1f2fe56f308a26a75e08f4c674fa9dde 100644
--- a/src/Options.c
+++ b/src/Options.c
@@ -130,6 +130,9 @@ char* GetDefaultOptionText() {
 "Partitioner                                    mondriaan \n"
 "Metric                                         lambda1 \n"
 "Discard_Free_Nets                              yes \n"
+"ZeroVolumeSearch                               yes \n"
+"ImproveFreeNonzeros                            yes \n"
+"CheckUpperBound                                yes \n"
 "SquareMatrix_DistributeVectorsEqual            no \n"
 "SquareMatrix_DistributeVectorsEqual_AddDummies yes \n"
 "SymmetricMatrix_UseSingleEntry                 no \n"
@@ -245,6 +248,24 @@ int ExportOptions(FILE *Out, const struct opts *Opts) {
     else return FALSE;
     fprintf(Out, "\n");
     
+    fprintf(Out, "ZeroVolumeSearch ");
+    if (Opts->ZeroVolumeSearch == ZeroVolYes) fprintf(Out, "yes");
+    else if (Opts->ZeroVolumeSearch == ZeroVolNo) fprintf(Out, "no");
+    else return FALSE;
+    fprintf(Out, "\n");
+    
+    fprintf(Out, "ImproveFreeNonzeros ");
+    if (Opts->ImproveFreeNonzeros == FreeNonzerosYes) fprintf(Out, "yes");
+    else if (Opts->ImproveFreeNonzeros == FreeNonzerosNo) fprintf(Out, "no");
+    else return FALSE;
+    fprintf(Out, "\n");
+    
+    fprintf(Out, "CheckUpperBound ");
+    if (Opts->CheckUpperBound == CheckUpperBoundYes) fprintf(Out, "yes");
+    else if (Opts->CheckUpperBound == CheckUpperBoundNo) fprintf(Out, "no");
+    else return FALSE;
+    fprintf(Out, "\n");
+    
     fprintf(Out, "SquareMatrix_DistributeVectorsEqual ");
     if (Opts->SquareMatrix_DistributeVectorsEqual == EqVecNo) fprintf(Out, "no");
     else if (Opts->SquareMatrix_DistributeVectorsEqual == EqVecYes) fprintf(Out, "yes");
@@ -272,7 +293,12 @@ int ExportOptions(FILE *Out, const struct opts *Opts) {
     fprintf(Out, "Coarsening_NrVertices %ld \n", Opts->Coarsening_NrVertices);
     fprintf(Out, "Coarsening_MaxCoarsenings %ld \n", Opts->Coarsening_MaxCoarsenings);
     fprintf(Out, "Coarsening_NrMatchArbitrary %ld \n", Opts->Coarsening_NrMatchArbitrary);
-    fprintf(Out, "Coarsening_MaxNrVtxInMatch %ld \n", Opts->Coarsening_MaxNrVtxInMatch);
+    
+    if(Opts->Coarsening_MaxNrVtxInMatch == -1)
+        fprintf(Out, "Coarsening_MaxNrVtxInMatch log \n");
+    else
+        fprintf(Out, "Coarsening_MaxNrVtxInMatch %ld \n", Opts->Coarsening_MaxNrVtxInMatch);
+    
     fprintf(Out, "Coarsening_StopRatio %f \n", Opts->Coarsening_StopRatio);
     fprintf(Out, "Coarsening_VtxMaxFractionOfWeight %f \n", Opts->Coarsening_VtxMaxFractionOfWeight);
     
@@ -458,6 +484,24 @@ int ExportOptionsToLaTeX(FILE *Out, const struct opts *Opts) {
     else fprintf(Out, "?");
     fprintf(Out, " \\\\\n");
     
+    fprintf(Out, "ZeroVolumeSearch & ");
+    if (Opts->ZeroVolumeSearch == ZeroVolYes) fprintf(Out, "yes");
+    else if (Opts->ZeroVolumeSearch == ZeroVolNo) fprintf(Out, "no");
+    else fprintf(Out, "?");
+    fprintf(Out, " \\\\\n");
+    
+    fprintf(Out, "ImproveFreeNonzeros & ");
+    if (Opts->ImproveFreeNonzeros == FreeNonzerosYes) fprintf(Out, "yes");
+    else if (Opts->ImproveFreeNonzeros == FreeNonzerosNo) fprintf(Out, "no");
+    else fprintf(Out, "?");
+    fprintf(Out, " \\\\\n");
+    
+    fprintf(Out, "CheckUpperBound & ");
+    if (Opts->CheckUpperBound == CheckUpperBoundYes) fprintf(Out, "yes");
+    else if (Opts->CheckUpperBound == CheckUpperBoundNo) fprintf(Out, "no");
+    else fprintf(Out, "?");
+    fprintf(Out, " \\\\\n");
+    
     fprintf(Out, "DistributeVectorsEqual & ");
     if (Opts->SquareMatrix_DistributeVectorsEqual == EqVecNo) fprintf(Out, "no");
     else if (Opts->SquareMatrix_DistributeVectorsEqual == EqVecYes) fprintf(Out, "yes");
@@ -485,7 +529,12 @@ int ExportOptionsToLaTeX(FILE *Out, const struct opts *Opts) {
     fprintf(Out, "Coarsening-NrVertices & %ld \\\\\n", Opts->Coarsening_NrVertices);
     fprintf(Out, "Coarsening-MaxCoarsenings & %ld \\\\\n", Opts->Coarsening_MaxCoarsenings);
     fprintf(Out, "Coarsening-MaxNrMatchArbitrary & %ld \\\\\n", Opts->Coarsening_NrMatchArbitrary);
-    fprintf(Out, "Coarsening-MaxNrVtxInMatch & %ld \\\\\n", Opts->Coarsening_MaxNrVtxInMatch);
+    
+    if(Opts->Coarsening_MaxNrVtxInMatch == -1)
+        fprintf(Out, "Coarsening-MaxNrVtxInMatch & log \\\\\n");
+    else
+        fprintf(Out, "Coarsening-MaxNrVtxInMatch & %ld \\\\\n", Opts->Coarsening_MaxNrVtxInMatch);
+    
     fprintf(Out, "Coarsening-StopRatio & %f \\\\\n", Opts->Coarsening_StopRatio);
     fprintf(Out, "Coarsening-VtxMaxFractionOfWeight & %f \\\\\n", Opts->Coarsening_VtxMaxFractionOfWeight);
     
@@ -646,8 +695,10 @@ int ApplyOptions(const struct opts *pOptions) {
     }
   
     if (pOptions->Coarsening_MaxNrVtxInMatch < 2) {
-        fprintf(stderr, "ApplyOptions(): Coarsening_MaxNrVtxInMatch out of range!\n");
-        return FALSE;
+        if(!(pOptions->Coarsening_MatchingATAMatcher == MatchMatcherPGA && pOptions->Coarsening_MaxNrVtxInMatch == -1)) {
+            fprintf(stderr, "ApplyOptions(): Coarsening_MaxNrVtxInMatch out of range!\n");
+            return FALSE;
+        }
     }
   
     if (pOptions->Coarsening_StopRatio < 0 || 
@@ -715,8 +766,8 @@ int ApplyOptions(const struct opts *pOptions) {
         return FALSE;
     }
     
-    if (pOptions->Coarsening_MatchingStrategy == MatchATA && pOptions->Coarsening_MaxNrVtxInMatch != 2) {
-        fprintf(stderr, "ApplyOptions(): Hybrid matching is only supported for matching groups of two vertices!\n");
+    if (pOptions->Coarsening_MatchingStrategy == MatchATA && pOptions->Coarsening_MaxNrVtxInMatch != 2 && pOptions->Coarsening_MatchingATAMatcher != MatchMatcherPGA) {
+        fprintf(stderr, "ApplyOptions(): ATA Greedy matching is only supported for matching groups of two vertices!\n");
         return FALSE;
     }
 
@@ -833,7 +884,34 @@ int SetOption(struct opts *pOptions, const char *option, const char *value) {
         } else if (!strcmp(value, "no")) {
             pOptions->DiscardFreeNets = FreeNetNo;
         } else {
-            fprintf(stderr, "SetOptions(): unknown %s '%s'!\n", option, value);
+            fprintf(stderr, "SetOption(): unknown %s '%s'!\n", option, value);
+            return FALSE;
+        }
+    } else if (!strcmp(option, "ZeroVolumeSearch")) {
+        if (!strcmp(value, "yes")) {
+            pOptions->ZeroVolumeSearch = ZeroVolYes;
+        } else if (!strcmp(value, "no")) {
+            pOptions->ZeroVolumeSearch = ZeroVolNo;
+        } else {
+            fprintf(stderr, "SetOption(): unknown %s '%s'!\n", option, value);
+            return FALSE;
+        }
+    } else if (!strcmp(option, "ImproveFreeNonzeros")) {
+        if (!strcmp(value, "no") || !strcmp(value, "0")) {
+            pOptions->ImproveFreeNonzeros = FreeNonzerosNo;
+        } else if (!strcmp(value, "yes") || ! strcmp(value, "1")) {
+            pOptions->ImproveFreeNonzeros = FreeNonzerosYes;
+        } else {
+            fprintf(stderr, "SetOption(): unknown %s '%s'!\n", option, value);
+            return FALSE;
+        }
+    } else if (!strcmp(option, "CheckUpperBound")) {
+        if (!strcmp(value, "yes")) {
+            pOptions->CheckUpperBound = CheckUpperBoundYes;
+        } else if (!strcmp(value, "no")) {
+            pOptions->CheckUpperBound = CheckUpperBoundNo;
+        } else {
+            fprintf(stderr, "SetOption(): unknown %s '%s'!\n", option, value);
             return FALSE;
         }
     } else if (!strcmp(option, "SquareMatrix_DistributeVectorsEqual")) {
@@ -879,7 +957,10 @@ int SetOption(struct opts *pOptions, const char *option, const char *value) {
     } else if (!strcmp(option, "Coarsening_NrMatchArbitrary")) {
         pOptions->Coarsening_NrMatchArbitrary = atol(value);
     } else if (!strcmp(option, "Coarsening_MaxNrVtxInMatch")) {
-        pOptions->Coarsening_MaxNrVtxInMatch = atol(value);
+        if(!strcmp(value, "log"))
+            pOptions->Coarsening_MaxNrVtxInMatch = -1;
+        else
+            pOptions->Coarsening_MaxNrVtxInMatch = atol(value);
     } else if (!strcmp(option, "Coarsening_StopRatio")) {
         pOptions->Coarsening_StopRatio = atof(value);
     } else if (!strcmp(option, "Coarsening_VtxMaxFractionOfWeight")) {
diff --git a/src/Options.h b/src/Options.h
index cc8bac82bf4503484cf61a548604a365ab3f633f..1870562928e450d9eefd54ffeb5ee663c74aaa05 100644
--- a/src/Options.h
+++ b/src/Options.h
@@ -57,6 +57,9 @@ struct opts {
     enum {PartMondriaan, PartPaToH, FullPaToH} Partitioner;
     enum {MetricLambda, MetricCut, MetricLambdaLambdaMinusOne} Metric;
     enum {FreeNetYes, FreeNetNo} DiscardFreeNets;
+    enum {ZeroVolNo, ZeroVolYes} ZeroVolumeSearch;
+    enum {FreeNonzerosNo, FreeNonzerosYes} ImproveFreeNonzeros;
+    enum {CheckUpperBoundNo, CheckUpperBoundYes} CheckUpperBound;
   
     /* Matrix options */
     enum {EqVecNo, EqVecYes} SquareMatrix_DistributeVectorsEqual;
diff --git a/src/Sort.c b/src/Sort.c
index 55a70a152d6e2c6754e41c25cb5f7793dde81ec1..dbbc4b421863789c8109c071f557c93b594f1c78 100644
--- a/src/Sort.c
+++ b/src/Sort.c
@@ -110,7 +110,7 @@ long *QSort(long *X, long LengthX) {
     for (t = 0; t < LengthX; t++)
         I[t] = t;
   
-    quicksort(X, I, 0, LengthX - 1);
+    quicksort3way(X, I, LengthX);
   
     return I;
 }   /* end Qsort */
@@ -287,3 +287,286 @@ void RandomPermute(long *X, long *Y, double *D, double *E, long lo, long hi) {
 } /* end RandomPermute */
 
 
+
+
+/**
+ * Below, an implementation of 3-way quicksort is included.
+ * This implementation combines the code from
+ * https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/qsort.c;h=12a5a7506a3337ecbebc6b1778425208ef8439c3;hb=HEAD
+ * with knowledge taken from
+ *     Engineering a sort function; Jon Bentley and M. Douglas McIlroy;
+ *     Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993.
+ *
+ * In essence, the implementation below is an altered version of the glibc implementation, specialized for long values,
+ * with additional logic to provide 3-way sorting functionality. Furthermore, pivots are chosen at random instead of
+ * the median of 3 values at fixed positions, to provide better randomization of equal elements.
+ * Random numbers are generated directly useing rand() instead of Random1(), to reduce the number of function calls.
+ */
+
+
+#define LT_GT >
+
+/* Swap two items. */
+#define SWAP(A, B)		{const long C = *(A); *(A) = *(B); *(B) = C; const long D = indices[A-list]; indices[A-list] = indices[B-list]; indices[B-list] = D;}
+
+/* Stack node declarations used to store unfulfilled partition obligations. */
+typedef struct
+{
+	long *lo;
+	long *hi;
+} stack_node;
+
+/* The next 4 #defines implement a very fast in-line stack abstraction. */
+/* The stack needs log (total_elements) entries.
+ * Since total_elements has type size_t, we get as
+ * upper bound for log (total_elements):
+ * bits per byte (CHAR_BIT) * sizeof(size_t).
+ */
+#define STACK_SIZE	(CHAR_BIT * sizeof(size_t))
+#define PUSH(low, high)	((void) ((top->lo = (low)), (top->hi = (high)), ++top))
+#define	POP(low, high)	((void) (--top, (low = top->lo), (high = top->hi)))
+#define	STACK_NOT_EMPTY	(stack < top)
+
+/**
+ * In each iteration, we execute a part of a recursion of the quicksort algorithm.
+ * The list in each iteration can be viewed as follows:
+ * +-----+-----+-----+-----+-----+
+ * [  =  [  <  [  ?  ]  >  ]  =  ]
+ * +-----+-----+-----+-----+-----+
+ * a     b     c     d     e     f
+ * 
+ * After processing [c,d], we have:
+ * +-----+-----++-----+-----+
+ * [  =  [  <  ][  >  ]  =  ]
+ * +-----+-----++-----+-----+
+ * a     b     dc     e     f
+ * 
+ * After moving the equal elements, we have:
+ * +-----+-----+-----+
+ * [  <  ]  =  [  >  ]
+ * +-----+-----+-----+
+ * b=a   d     c   f=e
+ * 
+ * Note that this (and all comments below) rely on LT_GT == < . In practice, Mondriaan
+ * chooses LT_GT to be >, so 'higher' and 'lower' should be swapped in all comments below.
+ * 
+ * Here:
+ *  - [a,b) denotes all elements equal to the pivot, as found by c,
+ *  - [b,c) denotes all elements lower than the pivot,
+ *  - [c,d] denotes all elements to be processed,
+ *  - (d,e] denotes all elements higher than the pivot,
+ *  - (e,f] denotes all elements equal to the pivot, as found by d.
+ * 
+ * In each iteration, three random elements are selected from the then to be sorted set [c,d], of which the
+ * median is determined. This element serves as pivot. Any elements equal to the pivot are sorted into
+ * [a,b[ or ]e,f], while elements lower than or higher than the pivot go into respectively [b,c[ and ]d,e].
+ * After [c,d] is empty (d < c), [a,b[ and ]e,f] are moved in between [b,d] and [c,e].
+ * 
+ */
+
+void quicksort3way (long *list, long *indices, size_t total_elems)
+{
+	if (total_elems <= 1)
+		return;
+
+	long *a, *b, *c, *d, *e, *f;
+	long n, el1, el2, el3, pivot;
+	
+	/* Initialise stack */
+	stack_node stack[STACK_SIZE];
+	stack_node *top = stack;
+	PUSH (NULL, NULL);
+	
+	/* Initialise first iteration */
+	c = list;
+	d = &list[total_elems - 1];
+
+	while (STACK_NOT_EMPTY)
+	{
+		/* Process [c,d] */
+		a = b = c;
+		f = e = d;
+		n = d-c+1;
+		
+		if(n == 2) {
+			/* Two elements are easily sorted */
+			if(*d LT_GT *c) {
+				SWAP(c, d);
+			}
+			++c;
+			--d;
+		}
+		else if(n > 2) {
+			
+			/*** DETERMINE PIVOT ***/
+			
+			el1 = b[(long)((rand() / (RAND_MAX+1.0))*n)];
+			el2 = b[(long)((rand() / (RAND_MAX+1.0))*n)];
+			el3 = b[(long)((rand() / (RAND_MAX+1.0))*n)];
+			if(el1 < el2) {
+				if(el2 < el3)
+					pivot = el2;
+				else if(el1 < el3)
+					pivot = el3;
+				else
+					pivot = el1;
+			}
+			else {
+				if(el1 < el3)
+					pivot = el1;
+				else if(el2 < el3)
+					pivot = el3;
+				else
+					pivot = el2;
+			}
+			
+			/*** PARTITION ***/
+			
+			/* Check whether the start/end contains elements equal to the pivot */
+			while(b <= e && *b == pivot) {
+				++b;
+				++c;
+			}
+			while(b <= e && *e == pivot) {
+				--d;
+				--e;
+			}
+			
+			/* The `collapse the walls' section of quicksort. */
+			while (c <= d)
+			{
+				/* Check for equals */
+				while(c <= d && pivot == *c) {
+					SWAP(b, c)
+					++b;
+					++c;
+				}
+				
+				while(c <= d && pivot == *d) {
+					SWAP(e, d)
+					--e;
+					--d;
+				}
+				
+				/* Move c and d until they have to be swapped */
+				while (c <= d && *c LT_GT pivot)
+					++c;
+				
+				while (c <= d && pivot LT_GT *d)
+					--d;
+				
+				/* With LT_GT == <, we now have *d <= pivot <= *c */
+				
+				if (c < d)
+				{
+					/* Now, *d <= pivot <= *c and c<d, hence swap. */
+					SWAP (c, d);
+					
+					/* Move elements equal to pivot */
+					if(pivot == *c) {
+						SWAP(b, c)
+						++b;
+					}
+					if(pivot == *d) {
+						SWAP(e, d)
+						--e;
+					}
+					
+					/* Mark c and d as processed */
+					++c;
+					--d;
+				}
+				else if (c == d)
+				{
+					/* By the while-loops, we have *d <= pivot <= *c.
+					 * Hence, if c == d, we have *d == *c == pivot.
+					 * As the next step will be swapping back the elements equal to the pivot,
+					 * we do not swap this element to an equal area.
+					 * We move both c and d, effectively marking this element as equal to the pivot.
+					 */
+					++c;
+					--d;
+					break;
+				}
+				/* else, c > d and the loop will break */
+			}
+			/* Now, c-d is either 1 (most of times) or 2 (if (c==d) occurred above).
+			 * Anyhow, d<c and the set of elements to be processed [c,d] is empty.
+			 */
+			
+			/* If there exists a left/right partition (i.e., [b,c) or (d,e] is non-empty),
+			 * move the left/right equal-partition (i.e., [a,b) or (e,f]) between c and d. */
+			if(b <= d) {
+				while(b > a) {
+					--b;
+					SWAP(d, b);
+					--d;
+				}
+			} /* else, there exists no left partition, so moving equal elements is unnecessary. */
+			
+			if(e >= c) {
+				while(e < f) {
+					++e;
+					SWAP(c, e);
+					++c;
+				}
+			} /* else, there exists no right partition, so moving equal elements is unnecessary. */
+		
+		}
+		else {
+			fprintf(stderr, "quicksort3way(): Single element in set\n");
+			exit(-1);
+		}
+
+		/*** RECURSION ***/
+		
+		/* Set up pointers for next iteration. First determine whether one or
+		 * both partitions are completely sorted. If so, ignore one or both.
+		 * Otherwise, push the larger partition's bounds on the stack and
+		 * continue sorting the smaller one.
+		 * The next set to be partitioned is assigned to [c,d].
+		 */
+
+		if (d <= b)
+		{
+			if (c >= e)
+				/* 'Greater than' [c,e] and 'smaller than' [b,d] partition both contain less than two elements.
+				 * These are sorted, so ignore both.
+				 */
+				POP (c, d);
+			else
+				/* 'Smaller than' partition [b,d] contains less than two elements.
+				 * It is sorted, so ignore it, and process [c,e].
+				 */
+				d = e;
+		}
+		else if (c >= e)
+			/* 'Greater than' partition [c,e] contains less than two elements.
+			 * It is sorted, so ignore it, and process [b,d].
+			 */
+			c = b;
+		else if ((d - b) > (e - c))
+		{
+			/* Push larger 'Smaller than' partition [b,d] and process [c,e]. */
+			PUSH (b, d);
+			d = e;
+		}
+		else
+		{
+			/* Push larger 'Greater than' partition [c,e] and process [b,d]. */
+			PUSH (c, e);
+			c = b;
+		}
+	}
+
+}
+
+
+#undef LT_GT
+#undef SWAP
+#undef STACK_SIZE
+#undef PUSH
+#undef POP
+#undef STACK_NOT_EMPTY
+
+
diff --git a/src/Sort.h b/src/Sort.h
index 710137bc707289776e46b3ddeb1b1dd731b9ad3c..7f80c9accf6fc3de9602f45ebe0c1ac3be9d1757 100644
--- a/src/Sort.h
+++ b/src/Sort.h
@@ -31,6 +31,7 @@ void SwapDouble(double *x, long, long);
 
 long *QSort(long *X, long LengthX);
 void quicksort(long *item, long *Index, long lo, long hi);
+void quicksort3way (long *list, long *indices, size_t total_elems);
 int CSort(long *J, long *val, long maxval, long lo, long hi);
 
 void RandomPermute(long *X, long *Y, double *D, double *E, long lo, long hi);
diff --git a/src/SparseMatrix.c b/src/SparseMatrix.c
index 4fcd3a6e3f495f921f5c79771285d00ef5cd74b9..c35b6c330e3a0d23dc87d402188e36a70f8b1194 100644
--- a/src/SparseMatrix.c
+++ b/src/SparseMatrix.c
@@ -567,7 +567,7 @@ int MMSparseMatrixGetTypeCode(struct sparsematrix *pM, char *line, const char *n
              return FALSE;
     } else return FALSE;
 
-    sprintf(line, "%s\n", line);
+    strcat(line, "\n");
     return TRUE;
 } /* end MMSparseMatrixGetTypeCode */
   
@@ -791,6 +791,11 @@ int SpMatValuesToProcessorIndices(struct sparsematrix *pM) {
     long * i_orig = pM->i;
     long * j_orig = pM->j;
     
+    if (pM->ReValue == NULL) {
+        fprintf(stderr, "SpMatValuesToProcessorIndices(): Matrix does not contain ReValue values!\n");
+        return FALSE;
+    }
+    
     pM->i = (long *) malloc((pM->NrNzElts+1) * sizeof(long));
     pM->j = (long *) malloc((pM->NrNzElts+1) * sizeof(long));
     
@@ -802,6 +807,10 @@ int SpMatValuesToProcessorIndices(struct sparsematrix *pM) {
     /* Determine number of processors */
     pM->NrProcs = 0;
     for (t = 0; t < pM->NrNzElts; t++) {
+        if(pM->ReValue[t] < 0.0) {
+            fprintf(stderr, "SpMatValuesToProcessorIndices(): Negative values cannot be used as processor indices!\n");
+            return FALSE;
+        }
         if(((long)pM->ReValue[t]) > pM->NrProcs) {
             pM->NrProcs = (long)pM->ReValue[t];
         }
@@ -856,6 +865,8 @@ int SpMatValuesToProcessorIndices(struct sparsematrix *pM) {
     free(i_orig);
     free(j_orig);
     free(Pindex);
+
+    pM->MMTypeCode[0] = 'D';
     
     return TRUE;
 }
@@ -995,14 +1006,19 @@ int SpMatReadIndexAndValueMatrixFiles(const char *fnA, const char *fnI, struct s
         return FALSE;
     }
     
+    if(pI->MMTypeCode[0] != 'M' || pI->MMTypeCode[1] != 'C') {
+        fprintf(stderr, "SpMatReadIndexAndValueMatrixFiles(): Unsupported index matrix format!\n");
+        return FALSE;
+    }
+    
     /* Use values as processor numbers */
     if (!SpMatValuesToProcessorIndices(pI)) {
         fprintf(stderr, "SpMatReadIndexAndValueMatrixFiles(): Error while reading processor indices!\n");
         return FALSE;
     }
     
-    if(pI->MMTypeCode[0] != 'M' || pI->MMTypeCode[1] != 'C') {
-        fprintf(stderr, "SpMatReadIndexAndValueMatrixFiles(): Unsupported index matrix format!\n");
+    if(pI->MMTypeCode[0] != 'D') {
+        fprintf(stderr, "SpMatReadIndexAndValueMatrixFiles(): Processor number matrix not imported correctly!\n");
         return FALSE;
     }
     
@@ -1546,13 +1562,14 @@ int MMSparseMatrixPrintPstart(struct sparsematrix *pM, FILE *stream, const struc
         return FALSE;
     }
   
-    if (pM->MMTypeCode[0] == 'D') 
+    if (pM->MMTypeCode[0] == 'D') {
         for (t = 0; t <= pM->NrProcs; t++ )
             if( pOptions->OutputFormat==OutputDMM )
                  fprintf(stream, "%ld\n", pM->Pstart[t]);
             else /* default to 1-based, except if explicitly in old Mondriaan mode */
                  fprintf(stream, "%ld\n", pM->Pstart[t]+1);
-
+    }
+    
     return TRUE;
 } /* end MMSparseMatrixPrintPstart */
 
@@ -2080,6 +2097,135 @@ int SparseMatrixSymmetric2Full(struct sparsematrix *pM) {
    
     return TRUE;
 } /* end SparseMatrixSymmetric2Full */
+
+
+int SparseMatrixFull2Symmetric(struct sparsematrix *pM, char MMTypeCode) {
+
+    /* This function converts a full sparse matrix to the
+       symmetric, skew-symmetric, or hermitian representation.
+
+       This function is the 'inverse' of SparseMatrixSymmetric2Full().
+       
+       See SparseMatrixSymmetric2Full() for more information.
+    */
+  
+    long t, tt, Ndiag;
+    long start, NrNzEltsNew;
+    int q;
+    
+    if (!pM) {
+        fprintf(stderr, "SparseMatrixFull2Symmetric(): Null parameter!\n");
+        return FALSE;
+    }
+  
+    /* Check if matrix is general square */
+    if (pM->m != pM->n || pM->MMTypeCode[3] != 'G') {
+        fprintf(stderr, "SparseMatrixFull2Symmetric(): matrix is not general square!\n");
+        return FALSE;
+    }
+    if (MMTypeCode != 'S' && MMTypeCode != 'K' && MMTypeCode != 'H') {
+        fprintf(stderr, "SparseMatrixFull2Symmetric(): type code must equal S, K or H!\n");
+        return FALSE;
+    }
+  
+    /* Count the number of diagonal elements */
+    Ndiag = 0;
+    for (t = 0; t < pM->NrNzElts; t++)
+        if (pM->i[t] == pM->j[t])
+            Ndiag++;
+    NrNzEltsNew = (pM->NrNzElts + Ndiag)/2;
+
+    if (MMTypeCode == 'K' && Ndiag > 0) {
+        fprintf(stderr, "SparseMatrixFull2Symmetric(): matrix is not skew-symmetric!\n");
+        return FALSE;
+    }
+
+    /* Update Pstart */
+    if (pM->NrProcs >= 1 && pM->Pstart != NULL) {
+        tt = 0;
+        start = pM->Pstart[0];
+        for (q = 0; q < pM->NrProcs; q++) {
+            for (t = start; t < pM->Pstart[q+1]; t++) {
+                if (pM->i[t] >= pM->j[t])
+                    tt++;
+            }
+            
+            start = pM->Pstart[q+1];
+            pM->Pstart[q+1] = tt;
+        }
+    
+        if(tt != NrNzEltsNew) {
+            fprintf(stderr, "SparseMatrixFull2Symmetric(): Processor weights not rearranged correctly!\n");
+            return FALSE;
+        }
+    }
+    
+    /* Copy the entries */
+    tt = 0;
+    for (t = 0; t < pM->NrNzElts; t++) {
+        /* Copy entry t into tt */
+        if (pM->i[t] < pM->j[t]) {
+            continue;
+        }
+
+        pM->i[tt] = pM->i[t];
+        pM->j[tt] = pM->j[t];
+        if (pM->MMTypeCode[2] != 'P')
+            pM->ReValue[tt] = pM->ReValue[t];
+        if (pM->MMTypeCode[2] == 'C')
+            pM->ImValue[tt] = pM->ImValue[t];
+        tt++;
+    }
+    
+    if(tt != NrNzEltsNew) {
+        fprintf(stderr, "SparseMatrixFull2Symmetric(): Nonzeros not rearranged correctly!\n");
+        return FALSE;
+    }
+   
+    /* Allocate memory for the entries */
+    pM->i = (long *) realloc(pM->i, (NrNzEltsNew+1) * sizeof(long));
+    pM->j = (long *) realloc(pM->j, (NrNzEltsNew+1) * sizeof(long));
+    
+    if (pM->i == NULL || pM->j == NULL) {
+        fprintf(stderr, "SparseMatrixFull2Symmetric(): Unable to reallocate!\n");
+        return FALSE;
+    }
+    
+    if (pM->MMTypeCode[2] != 'P') {
+        pM->ReValue = (double *) realloc(pM->ReValue, (NrNzEltsNew+1)*sizeof(double));
+        
+        if (pM->ReValue == NULL) {
+            fprintf(stderr, "SparseMatrixFull2Symmetric(): Unable to reallocate!\n");
+            return FALSE;
+        }
+    }
+    
+    if (pM->MMTypeCode[2] == 'C') {
+        pM->ImValue = (double *) realloc(pM->ImValue, (NrNzEltsNew+1)*sizeof(double));
+        
+        if (pM->ImValue == NULL) {
+            fprintf(stderr, "SparseMatrixFull2Symmetric(): Unable to reallocate!\n");
+            return FALSE;
+        }
+    }
+  
+    /* Update the number of nonzero entries and the matrix type code */
+    pM->NrNzElts = NrNzEltsNew;
+    pM->MMTypeCode[3] = MMTypeCode;
+    switch(MMTypeCode) {
+        case 'S':
+            strcpy(pM->Symmetry, "symmetric");
+            break;
+        case 'K':
+            strcpy(pM->Symmetry, "skew-symmetric");
+            break;
+        case 'H':
+            strcpy(pM->Symmetry, "hermitian");
+            break;
+    }
+   
+    return TRUE;
+} /* end SparseMatrixFull2Symmetric */
   
 
 int SparseMatrixSymmetricLower2Random(struct sparsematrix *pM) {
@@ -2674,3 +2820,158 @@ int CreateInitialMediumGrainDistribution(struct sparsematrix *pM, long *mid){
 
 } /* end CreateInitialMediumGrainDistribution */
 
+
+
+/* Comparison function for qsort().
+ * This functions compares the values a and b, and
+ * returns -1, 0 or 1 if a is respectively lower than, equal to or larger than b.
+ */
+int compareLong (const void *a, const void *b) {
+
+    long diff = *(long*)a - *(long*)b;
+    if(diff == 0)
+        return 0;
+    return (diff < 0) ? -1 : 1;
+
+} /* end compareLong */
+
+/**
+ * Convert a sparse matrix to CRS and CCS forms.
+ * This assumes that duplicate entries in pM have already been removed with SparseMatrixRemoveDuplicates().
+ * 
+ * Input:
+ * pM                : The matrix (m-by-n)
+ * 
+ * Output:
+ * pCCS              : The matrix in Compressed Column Storage format (Only if return value == TRUE)
+ * pCRS              : The matrix in Compressed Row Storage format (Only if return value == TRUE)
+ * 
+ * Returned memory allocations:
+ *     pCCS:    indirect  (Only if return value == TRUE)
+ *     pCRS:    indirect  (Only if return value == TRUE)
+ * 
+ * Return value: FALSE if an error occurs, TRUE otherwise.
+ */
+int SparseMatrixToCRS_CCS(struct sparsematrix *pM, struct CRCS *pCCS, struct CRCS *pCRS){
+    
+    long i;
+
+    /* Position of a matrix nonzero, 0 <= x < m and 0 <= y < n,
+       where pM is an m by n matrix */
+    long x, y;
+    
+    if(!initCRCS(pCRS, 0, pM->m, pM->n, pM->NrNzElts) || !initCRCS(pCCS, 1, pM->m, pM->n, pM->NrNzElts)) {
+        return FALSE;
+    }
+    /* Fill starts array */
+    for(i=0;i<=pM->m;i++)
+        pCRS->starts[i] = 0;
+    for(i=0;i<=pM->n;i++)
+        pCCS->starts[i] = 0;
+
+    for(i=0;i<pM->NrNzElts;i++){
+        x = pM->i[i];
+        y = pM->j[i];
+        pCRS->starts[x+1]++;
+        pCCS->starts[y+1]++;
+    }
+
+    for(i=2;i<=pM->m;i++)
+        pCRS->starts[i] += pCRS->starts[i-1];
+    for(i=2;i<=pM->n;i++)
+        pCCS->starts[i] += pCCS->starts[i-1];
+
+    /* Fill index arrays */
+    long *nInRow = (long*)malloc(sizeof(long)*pM->m);
+    long *nInCol = (long*)malloc(sizeof(long)*pM->n);
+    if (nInRow == NULL || nInCol == NULL){
+        if(nInRow != NULL)
+            free(nInRow);
+        if(nInCol != NULL)
+            free(nInCol);
+        freeCRCS(pCRS);
+        freeCRCS(pCCS);
+        fprintf(stderr, "SparseMatrixToCRS_CCS(): Not enough memory!\n");
+        return FALSE;
+    }
+
+    for(i=0;i<pM->m;i++)
+        nInRow[i] = 0;
+    for(i=0;i<pM->n;i++)
+        nInCol[i] = 0;
+
+    for(i=0;i<pM->NrNzElts;i++){
+        x = pM->i[i];
+        y = pM->j[i];
+        /* Fill first free position in row x with column index y */
+        pCRS->indices[pCRS->starts[x]+nInRow[x]]=y;
+        /* Fill first free position in column y with row index x */
+        pCCS->indices[pCCS->starts[y]+nInCol[y]]=x;
+        nInRow[x]++;
+        nInCol[y]++;
+    }
+
+    /* Sort the indices within the rows and columns */
+    for(i=0;i<pM->m;i++){
+        qsort(pCRS->indices+pCRS->starts[i], pCRS->starts[i+1]-pCRS->starts[i], sizeof(long), compareLong);
+    }
+    for(i=0;i<pM->n;i++){
+        qsort(pCCS->indices+pCCS->starts[i], pCCS->starts[i+1]-pCCS->starts[i], sizeof(long), compareLong);
+    }
+
+    free(nInRow);
+    free(nInCol);
+
+    return TRUE;
+    
+} /* end SparseMatrixToCRS_CCS */
+
+/**
+ * Initialize a CRCS struct.
+ * This assumes that the struct is not already initialized.
+ * 
+ * Input:
+ * crcs              : The CRCS struct
+ * dir               : 0 for row-direction, 1 for column-direction
+ * m, n, nnz         : Dimensions and number of nonzeros
+ * 
+ * Returned memory allocations:
+ *     crcs.indices:    O(nnz)
+ *     crcs.starts:     O(m+1) if dir=0, O(n+1) if dir=1
+ * 
+ * Return value: FALSE if an error occurs, TRUE otherwise.
+ */
+int initCRCS(struct CRCS *pCRCS, int dir, long m, long n, long nnz) {
+    pCRCS->dir = dir?1:0;
+    pCRCS->m = m;
+    pCRCS->n = n;
+    pCRCS->nnz = nnz;
+    
+    long numStarts = dir?(n+1):(m+1);
+    pCRCS->indices = (long*)malloc(sizeof(long)*nnz);
+    pCRCS->starts = (long*)malloc(sizeof(long)*numStarts);
+    
+    if(pCRCS->indices == NULL || pCRCS->starts == NULL) {
+        if(pCRCS->indices != NULL)
+            free(pCRCS->indices);
+        if(pCRCS->starts != NULL)
+            free(pCRCS->starts);
+        fprintf(stderr, "initCRCS(): Not enough memory!\n");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/**
+ * Free a CRCS struct.
+ * 
+ * Input:
+ * crcs              : The CRCS struct
+ */
+void freeCRCS(struct CRCS *pCRCS) {
+    free(pCRCS->indices);
+    free(pCRCS->starts);
+    pCRCS->indices = NULL;
+    pCRCS->starts = NULL;
+}
+
diff --git a/src/SparseMatrix.h b/src/SparseMatrix.h
index 5fb87e3f5a0bd9c4d16706d55b75cbfc0057cdab..372530212c50ff6d4736c78ae45a7eaa750f2632 100644
--- a/src/SparseMatrix.h
+++ b/src/SparseMatrix.h
@@ -70,6 +70,15 @@ struct sparsematrix {
   struct remembrance *rowBoundaries, *colBoundaries;
 };
 
+struct CRCS {
+    int dir;
+    long m;
+    long n;
+    long nnz;
+    long *indices;
+    long *starts;
+};
+
 /* Function declarations for Matrix Market input/output functions */
 int MMSparseMatrixInit(struct sparsematrix *pM);
 int MMWeightsInit(struct sparsematrix *pM);
@@ -121,6 +130,7 @@ int AddDummiesToSparseMatrix(struct sparsematrix *pM);
 int RemoveDummiesFromSparseMatrix(struct sparsematrix *pM);
 
 int SparseMatrixSymmetric2Full(struct sparsematrix *pM);
+int SparseMatrixFull2Symmetric(struct sparsematrix *pM, char MMTypeCode);
 int SparseMatrixSymmetricLower2Random(struct sparsematrix *pM);
 int SparseMatrixSymmetricRandom2Lower(struct sparsematrix *pM);
 
@@ -136,5 +146,9 @@ int SparseMatrixLocal2Vector(struct sparsematrix *pM, long int **local2glob, lon
                              const char dir );
 int CreateInitialMediumGrainDistribution(struct sparsematrix *pM, long *mid);
 
+int SparseMatrixToCRS_CCS(struct sparsematrix *pM, struct CRCS *pCCS, struct CRCS *pCRS);
+int initCRCS(struct CRCS *pCRCS, int dir, long m, long n, long nnz);
+void freeCRCS(struct CRCS *pCRCS);
+
 #endif /* __SparseMatrix_h__ */
 
diff --git a/src/SplitMatrixUpperBound.c b/src/SplitMatrixUpperBound.c
new file mode 100644
index 0000000000000000000000000000000000000000..b08a3c8c46f392a31dc876bb4e9804b2417f16ff
--- /dev/null
+++ b/src/SplitMatrixUpperBound.c
@@ -0,0 +1,469 @@
+#include "SplitMatrixUpperBound.h"
+
+struct PartWeight {
+    long part;
+    long weight;
+};
+
+/**
+ * Comparison functions.
+ */
+int comparePartWeightsASC (const void *a, const void *b) {
+    long diff = ((struct PartWeight*)a)->weight - ((struct PartWeight*)b)->weight;
+    if(diff == 0)
+        return 0;
+    return (diff < 0) ? -1 : 1;
+} /* end comparePartWeightsASC */
+
+int comparePartWeightsDESC (const void *a, const void *b) {
+    long diff = ((struct PartWeight*)a)->weight - ((struct PartWeight*)b)->weight;
+    if(diff == 0)
+        return 0;
+    return (diff > 0) ? -1 : 1;
+} /* end comparePartWeightsDESC */
+
+
+int SplitMatrixUpperBound(struct sparsematrix *pT, int P, const struct opts *pOptions) {
+    /* This function splits the sparse matrix T into P (nearly-)equal parts.
+       The split is performed by distributing entire columns (or rows) over the parts,
+       possibly cutting some columns (or rows) to maintain feasibility. Nonzeros may be moved.
+       This function does not minimize communication volume, but does keep it below the
+       upper bound (min(m,n)+1)(P-1).
+       
+       Input: T sparse matrix,
+              P number of parts to split into,
+              options the options.
+       
+       Output: T sparse matrix
+               The nonzeros of part i are in positions pT->Pstart[i], pT->Pstart[i+1]-1,
+               where 0<=i<P.
+       
+       NB: This function works only if the weights are based on the number of nonzeros.
+       It does not work when:
+        - the matrix is stored as a symmetric matrix,
+        - dummy nonzeros with weight 0 are present,
+        - or weights are based on matrix columns.
+    */
+       
+    long s, t,          /* Nonzero iterators */
+         j, n,          /* Column (row) index and number of columns (rows) */
+         *nzInd,        /* Pointer to matrix column (row) indices */
+         *w,            /* w[j] = number of nonzeros in column (row) j */
+         *lastCol,      /* Last column added to a part */
+         *partWeights,  /* Weight of a part */
+         *partPointer,  /* Nonzero iterator per part */
+         delta, end, targetWeight;
+    
+    int *p,      /* p[j] = part that column (row) j is assigned to */
+        k,       /* General iterator over all parts */
+        l,       /* First dimension iterator in ColWghtDist[] */
+        q,       /* General processor number; also second dimension iterator in ColWghtDist[] */
+        numMinHeap, numMaxHeap;
+    
+    struct PartWeight maxWeight, minWeight, *MaxHeapItems, *MinHeapItems;
+    struct heap MinHeap, MaxHeap;
+
+    if (!pT || !pOptions) {
+        fprintf(stderr, "SplitMatrixUpperBound(): Null arguments!\n");
+        return FALSE;
+    }
+    
+    if ((pT->MMTypeCode[3]=='S' || pT->MMTypeCode[3]=='K' || pT->MMTypeCode[3]=='H') &&
+             pOptions->SymmetricMatrix_UseSingleEntry == SingleEntYes) {
+        fprintf(stderr, "SplitMatrixUpperBound(): Cannot be used with symmetric matrices!\n");
+        return FALSE;
+    }
+    if (pT->NrDummies > 0) {
+        fprintf(stderr, "SplitMatrixUpperBound(): Cannot be used with dummy nonzeros!\n");
+        return FALSE;
+    }
+    if (pT->MMTypeCode[0] == 'W' && pT->NrColWeights > 0) {
+        fprintf(stderr, "SplitMatrixUpperBound(): Cannot be used with column-weighted matrix!\n");
+        return FALSE;
+    }
+    
+    if(pT->NrProcs < P) {
+        pT->NrProcs = P;
+        pT->Pstart = (long *)realloc(pT->Pstart, (pT->NrProcs+1)*sizeof(long));
+        if(pT->Pstart == NULL) {
+            fprintf( stderr, "SplitMatrixUpperBound(): Not enough memory.\n" );
+            return FALSE;
+        }
+    }
+    
+    /****
+     * Determine direction
+     ****/
+    if(pT->m < pT->n) {
+        n = pT->m;
+        nzInd = pT->i;
+    }
+    else {
+        n = pT->n;
+        nzInd = pT->j;
+    }
+    
+    /* Compute number of nonzeros in a column (row) */
+    w = (long *)calloc(n, sizeof(long));
+    if(w == NULL) {
+        fprintf( stderr, "SplitMatrixUpperBound(): Not enough memory.\n" );
+        return FALSE;
+    }
+    for(t=0; t<pT->NrNzElts; ++t) {
+        ++w[nzInd[t]];
+    }
+    
+    /****
+     * Create min heap of part weights
+     ****/
+    MinHeapItems = (struct PartWeight*)malloc(P*sizeof(struct PartWeight));
+    if(MinHeapItems == NULL) {
+        fprintf( stderr, "SplitMatrixUpperBound(): Not enough memory.\n" );
+        return FALSE;
+    }
+    
+    HeapInit(&MinHeap, sizeof(struct PartWeight), comparePartWeightsDESC);
+    
+    for(k=0; k<P; ++k) {
+        MinHeapItems[k].part = k;
+        MinHeapItems[k].weight = 0;
+    }
+    Heapify(&MinHeap, MinHeapItems, P);
+    
+    /****
+     * Assign columns (rows) to parts
+     ****/
+    p = (int *)malloc(n * sizeof(int));
+    lastCol = (long *)malloc(P * sizeof(long));
+    if(p == NULL || lastCol == NULL) {
+        fprintf( stderr, "SplitMatrixUpperBound(): Not enough memory.\n" );
+        return FALSE;
+    }
+    
+    for(k=0; k<P; ++k) {
+        lastCol[k] = -1;
+    }
+    
+    for(j=0; j<n; ++j) {
+        /* Assign column (row) j to part minWeight.part with currently least weight. */
+        HeapPeek(&MinHeap, &minWeight);
+        minWeight.weight += w[j];
+        p[j] = minWeight.part;
+        lastCol[minWeight.part] = j;
+        HeapReplace(&MinHeap, &minWeight, NULL);
+    }
+    
+    /****
+     * Compute surpluses and shortages per part and store them in heaps, to be able to determine where we should cut columns (rows).
+     * Also register weights of the columns (rows) that will be cut, to be able to keep track of how we want to cut them.
+     * We reuse the variable MinHeap, which now will contain shortages, while MaxHeap will contain surpluses.
+     ****/
+    
+    HeapInit(&MaxHeap, sizeof(struct PartWeight), comparePartWeightsASC);
+    MaxHeapItems = (struct PartWeight*)malloc(P*sizeof(struct PartWeight));
+    
+    long **ColWghtDist = (long **)malloc(P * sizeof(long*));
+    long *_ColWghtDist = (long *)calloc(P * P, sizeof(long));
+    if(MaxHeapItems == NULL || ColWghtDist == NULL || _ColWghtDist == NULL) {
+        fprintf( stderr, "SplitMatrixUpperBound(): Not enough memory.\n" );
+        return FALSE;
+    }
+    
+    for(l=0; l<P; ++l) {
+        ColWghtDist[l] = &(_ColWghtDist[l*P]);
+    }
+    
+    targetWeight = ceil(pT->NrNzElts / (double)P);
+    numMinHeap = 0;
+    numMaxHeap = 0;
+    l = 0;
+    for(k=0; k<P; ++k) {
+        /* Here we use the fact that the Heap data structure
+         * just permutes all entries, hence we can just iterate over
+         * these entries as if it were an array (which it 
+         * (MinHeapItems) actually still is). We could also iteratively
+         * call HeapPop(), but this would be O(P log(P)) while just
+         * iterating over the array is O(P).
+         */
+        if(MinHeapItems[k].weight > targetWeight) {
+            /* This part has a surplus */
+            MaxHeapItems[numMaxHeap].part = MinHeapItems[k].part;
+            MaxHeapItems[numMaxHeap].weight = MinHeapItems[k].weight - targetWeight;
+            ++numMaxHeap;
+            
+            /* Mark last column as cut and link it to ColWghtDist[l] */
+            q = MinHeapItems[k].part;
+            j = lastCol[q];
+            p[j] = -l-1;
+            ColWghtDist[l][q] = w[j];
+            
+            ++l;
+        }
+        else if(MinHeapItems[k].weight < targetWeight) {
+            /* This part has a shortage */
+            MinHeapItems[numMinHeap].part = MinHeapItems[k].part;
+            MinHeapItems[numMinHeap].weight = MinHeapItems[k].weight - targetWeight;
+            ++numMinHeap;
+        }
+    }
+    MinHeapItems = (struct PartWeight*)realloc(MinHeapItems, numMinHeap*sizeof(struct PartWeight));
+    MaxHeapItems = (struct PartWeight*)realloc(MaxHeapItems, numMaxHeap*sizeof(struct PartWeight));
+    Heapify(&MinHeap, MinHeapItems, numMinHeap);
+    Heapify(&MaxHeap, MaxHeapItems, numMaxHeap);
+    
+    free(w); w = NULL;
+    
+    /****
+     * Cut columns (rows) where necessary, until perfect balance is achieved.
+     * By cutting a column, we can move weight from a surplus part to a shortage part.
+     ****/
+    while(MaxHeap.numItems > 0) {
+        if(MinHeap.numItems < 1) {
+            fprintf( stderr, "SplitMatrixUpperBound(): Corrupt data.\n" );
+            return FALSE;
+        }
+        HeapPeek(&MinHeap, &minWeight); /* minWeight.weight < 0 */
+        HeapPeek(&MaxHeap, &maxWeight); /* maxWeight.weight > 0 */
+        
+        /* Determine the weight (delta) to move */
+        if(maxWeight.weight < -minWeight.weight) {
+            delta = maxWeight.weight;
+            
+            /* Remove maxWeight, reduce minWeight */
+            HeapPop(&MaxHeap, NULL);
+            minWeight.weight += maxWeight.weight;
+            HeapReplace(&MinHeap, &minWeight, NULL);
+        }
+        else if(maxWeight.weight > -minWeight.weight) {
+            delta = -minWeight.weight;
+            
+            /* Remove minWeight, reduce maxWeight */
+            HeapPop(&MinHeap, NULL);
+            maxWeight.weight += minWeight.weight;
+            HeapReplace(&MaxHeap, &maxWeight, NULL);
+        }
+        else { /* max == min */
+            delta = maxWeight.weight;
+            
+            HeapPop(&MaxHeap, NULL);
+            HeapPop(&MinHeap, NULL);
+        }
+        
+        /* Keep track of the cut */
+        l = -p[lastCol[maxWeight.part]]-1;
+        ColWghtDist[l][maxWeight.part] -= delta;
+        ColWghtDist[l][minWeight.part] = delta;
+    }
+    
+    free(lastCol); lastCol = NULL;
+    HeapDestroy(&MaxHeap); MaxHeapItems = NULL; /* Also free()s MaxHeapItems */
+    
+    /* cutColPrtPtr[l] := the first (lowest) index q for which ColWghtDist[l][q] is nonzero. */
+    int *cutColPrtPtr = (int *)malloc(numMaxHeap * sizeof(int));
+    if(cutColPrtPtr == NULL) {
+        fprintf( stderr, "SplitMatrixUpperBound(): Not enough memory.\n" );
+        return FALSE;
+    }
+    for(l=0; l<numMaxHeap; ++l) {
+        cutColPrtPtr[l] = 0;
+        
+        for(q=0; q<P; ++q) {
+            if(ColWghtDist[l][q] > 0) {
+                break;
+            }
+            ++cutColPrtPtr[l];
+        }
+    }
+    
+    /* At this point:
+     * p[j] =  q>0    if column j is completely assigned to part q
+     *         q<0    if column j cut. The distribution data is available in ColWghtDist[-q-1]
+     */
+    
+    /****
+     * Compute total part weights and build Pstart variable
+     ****/
+    partWeights = (long *)malloc(P * sizeof(long));
+    partPointer = (long *)malloc(P * sizeof(long));
+    if(partWeights == NULL || partPointer == NULL) {
+        fprintf( stderr, "SplitMatrixUpperBound(): Not enough memory.\n" );
+        return FALSE;
+    }
+    
+    for(k=0; k<P; ++k) {
+        partWeights[k] = targetWeight;
+    }
+    for(k=0; k<MinHeap.numItems; ++k) {
+        /* Here again we use the fact that MinHeapItems is just
+         * a valid (permuted) array to iterate over
+         */
+        partWeights[MinHeapItems[k].part] += MinHeapItems[k].weight;
+    }
+    
+    HeapDestroy(&MinHeap); MinHeapItems = NULL; /* Also free()s MinHeapItems */
+    
+    /* Build Pstart */
+    pT->Pstart[k] = partPointer[0] = 0;
+    for(k=1; k<P; ++k) {
+        pT->Pstart[k] = partPointer[k] = partPointer[k-1]+partWeights[k-1];
+    }
+    pT->Pstart[P] = partPointer[P-1]+partWeights[P-1];
+    
+    /* Sanity check */
+    if(pT->Pstart[P] != pT->NrNzElts) {
+        fprintf( stderr, "SplitMatrixUpperBound(): Corrupt sum.\n" );
+        return FALSE;
+    }
+    
+    free(partWeights); partWeights = NULL;
+    
+    
+    /****
+     * Sort nonzeros by part number in order to apply the computed partitioning
+     ****/
+    for(k=0; k<P; ++k) {
+        end = pT->Pstart[k+1];
+        
+        while(partPointer[k]<end) {
+            /* We move the nonzero at position s to part q */
+            s = partPointer[k];
+            q = p[nzInd[s]];
+            
+            if(q < 0) {
+                /* We have a cut column (row), so determine q from the cut data */
+                l = -q-1;
+                
+                if(ColWghtDist[l][k] > 0) {
+                    /* We may still add this nonzero to the current part.
+                     * Doing this, we prevent 1 swap.
+                     */
+                    q = k;
+                }
+                else {
+                    /* We should add this nonzero to an other part. */
+                    q = cutColPrtPtr[l];
+                    
+                    if(ColWghtDist[l][q] == 0) {
+                        fprintf( stderr, "SplitMatrixUpperBound(): Unexpected empty column part. %d %d\n", l, q );
+                        return FALSE;
+                    }
+                }
+                
+                --ColWghtDist[l][q];
+                
+                /* Update cutColPrtPtr to point to a part we may still assign nonzeros to */
+                if(q == cutColPrtPtr[l]) {
+                    while(cutColPrtPtr[l] < P && ColWghtDist[l][cutColPrtPtr[l]] == 0) {
+                        ++cutColPrtPtr[l];
+                    }
+                }
+                
+            }
+            /* else, column (row) nzInd[s] is completely assigned to part q */
+            
+            /* If nonzero is already in the right part, we do not need to swap */
+            if(k == q) {
+                ++partPointer[k];
+                continue;
+            }
+            
+            /* ('Else',) The nonzero needs to be swapped to the target part */
+            t = partPointer[q];
+            
+#ifdef INFO2
+            if(t > pT->Pstart[q+1]) {
+                fprintf( stderr, "SplitMatrixUpperBound(): Corrupt target pointer.\n" );
+                return FALSE;
+            }
+#endif
+            
+            SwapLong(pT->i, s, t);
+            SwapLong(pT->j, s, t);
+            if (pT->MMTypeCode[2] != 'P')
+                SwapDouble(pT->ReValue, s, t);
+            if (pT->MMTypeCode[2] == 'C')
+                SwapDouble(pT->ImValue, s, t);
+            
+            /* Update partPointer to point to the next nonzero not belonging to q */
+            do {
+                ++partPointer[q];
+            }
+            while(partPointer[q] < pT->Pstart[q+1] && p[nzInd[partPointer[q]]] == q);
+        }
+    }
+    
+#ifdef INFO2
+    /* Check part integrity */
+    for(k=0; k<P; ++k) {
+        if(partPointer[k] != pT->Pstart[k+1]) {
+            fprintf( stderr, "SplitMatrixUpperBound(): Corrupt result.\n" );
+            return FALSE;
+        }
+    }
+#endif
+    
+    free(p);
+    free(cutColPrtPtr);
+    free(ColWghtDist);
+    free(_ColWghtDist);
+    free(partPointer);
+
+#ifdef INFO2
+    if(!CheckUpperBoundSolution(pT)) {
+        return FALSE;
+    }
+#endif
+    
+    /* Compute new lambdas */
+    if (!InitNprocs(pT, COL, pT->RowLambda) || !InitNprocs(pT, ROW, pT->ColLambda)) {
+        fprintf(stderr, "SplitMatrixUpperBound(): Unable to compute lambdas!\n");
+        return FALSE;
+    }
+    
+    return TRUE;
+} /* end SplitMatrixUpperBound */
+
+
+int CheckUpperBoundSolution(struct sparsematrix *pT) {
+    /* This function checks whether the distributed matrix computed by
+       SplitMatrixUpperBound() is valid. I.e., it checks whether
+       the computed solution has a fairly distributed work load and
+       communication volume not higher than the upper bound.
+       
+       Input: T sparse matrix
+       Output: TRUE if the solution is valid, FALSE otherwise
+    */
+    
+    int P = pT->NrProcs;
+    
+    /* Calculate weights */
+    long Wmax = 0, weight;
+    for(int q=0; q<P; ++q) {
+        weight = pT->Pstart[q+1] - pT->Pstart[q];
+        if(weight > Wmax) {
+            Wmax = weight;
+        }
+    }
+    
+    if(Wmax > ceil(pT->NrNzElts/(double)P)) {
+#ifdef INFO2
+        fprintf( stderr, "CheckUpperBoundSolution(): Invalid imbalance result.\n" );
+#endif
+        return FALSE;
+    }
+    
+    /* Calculate communication volume */
+    long ComVol1, ComVol2, tmp;
+    CalcCom(pT, NULL, (pT->m < pT->n)?ROW:COL, &ComVol1, &tmp, &tmp, &tmp, &tmp);
+    CalcCom(pT, NULL, (pT->m < pT->n)?COL:ROW, &ComVol2, &tmp, &tmp, &tmp, &tmp);
+    
+    long n = (pT->m < pT->n)?pT->m:pT->n;
+    if(ComVol1 > n*(P-1) || ComVol2 > P-1) {
+#ifdef INFO2
+        fprintf( stderr, "CheckUpperBoundSolution(): Invalid communication result.\n" );
+#endif
+        return FALSE;
+    }
+    
+    return TRUE;
+} /* end CheckUpperBoundSolution */
diff --git a/src/SplitMatrixUpperBound.h b/src/SplitMatrixUpperBound.h
new file mode 100644
index 0000000000000000000000000000000000000000..a1a41c0392fcf2120bcdee3561b0e83ca595f42b
--- /dev/null
+++ b/src/SplitMatrixUpperBound.h
@@ -0,0 +1,10 @@
+#ifndef __SplitMatrixUpperBound_h__
+#define __SplitMatrixUpperBound_h__
+
+#include "Heap.h"
+#include "DistributeVecLib.h"
+
+int SplitMatrixUpperBound(struct sparsematrix *pT, int P, const struct opts *pOptions);
+int CheckUpperBoundSolution(struct sparsematrix *pT);
+
+#endif /* __SplitMatrixUpperBound_h__ */
diff --git a/src/SubsetSum.c b/src/SubsetSum.c
new file mode 100644
index 0000000000000000000000000000000000000000..d54ccfb726fa26dbc58b2b09af6967c82ada7adb
--- /dev/null
+++ b/src/SubsetSum.c
@@ -0,0 +1,443 @@
+
+#include "SubsetSum.h"
+#include "Heap.h"
+
+int _SubsetSumExp(const long * const weights, long * const select, const long N, const long pos, const long weightSelected, const long weightNotSelected, const long totWeight, const long minWeight, const long maxWeight);
+
+/**
+ * Perform a subset sum calculation.
+ * This is a variant of subset sum where the desired subset should have sum between minWeight and maxWeight,
+ * as opposed to more common variants where the sum should be equal to a fixed weight, or where it should be
+ * simply maximised (in non-decision form).
+ * 
+ * Input:
+ * N                 : Number of items/weights
+ * weightlo          : The weight the smaller subset should at most have
+ * weighthi          : The weight the larger subset should at most have
+ * 
+ * Input/Output:
+ * weights           : The weights of the items (may be permuted)
+ * 
+ * Output:
+ * permutation       : The permutation of the items used by the algorithm (Only if return value == TRUE)
+ * select            : Array defining which items are included in the small subset (1) or in the large subset (0) (Only if return value == TRUE)
+ * 
+ * Returned memory allocations:
+ *     permutation:    O(N) (Only if return value == TRUE)
+ *     select:         O(N) (Only if return value == TRUE)
+ * 
+ * Return value: TRUE if a feasible subset has been found, FALSE if this has not been found or an error occurred.
+ */
+int SubsetSum(long * const weights, const long N, const long weightlo, const long weighthi, long **permutation, long **select) {
+    
+    if(N > 16) {
+        return KarmarkarKarp(weights, N, weightlo, weighthi, permutation, select);
+    }
+    
+    return SubsetSumExp(weights, N, weightlo, weighthi, permutation, select);
+    
+} /* end SubsetSum */
+
+/**
+ * Perform a subset sum calculation, using an exponential time algorithm. This should not be used for large N!
+ * 
+ * See SubsetSum()
+ */
+int SubsetSumExp(long * const weights, const long N, const long weightlo, const long weighthi, long **permutation, long **select) {
+    
+    long totalWeight = 0, i;
+    for(i=0; i<N; i++) {
+        totalWeight += weights[i];
+    }
+    
+    /* Compute min and max weight of smallest desired partition */
+    long maxWeight = weightlo;
+    long minWeight = totalWeight - weighthi;
+    
+    /* Sort the weights in ascending order */
+    *permutation = QSort(weights, N);
+    
+    if(*permutation == 0) {
+        return FALSE;
+    }
+    
+    for (long t = 0; t < N/2; ++t) {
+        SwapLong(weights, t, N-1-t);
+        SwapLong(*permutation, t, N-1-t);
+    }
+    
+    /* Allocate and zero-initialize the decision array */
+    *select = (long*)calloc(N, sizeof(long));
+    
+    if( *select == NULL ) {
+        free(*permutation);
+        fprintf( stderr, "SubsetSumExp(): Not enough memory.\n" );
+        return FALSE;
+    }
+    
+    /* Run subset sum */
+    int result = _SubsetSumExp(weights, *select, N, 0, 0, 0, totalWeight, minWeight, maxWeight);
+    if(!result) {
+        free(*permutation);
+        free(*select);
+        return FALSE;
+    }
+    
+    return TRUE;
+    
+} /* end SubsetSumExp */
+
+/**
+ * Perform a subset sum recursion. See also SubsetSumExp().
+ * 
+ * Input:
+ * weights           : The weights of the items
+ * N                 : Number of items/weights
+ * pos               : Current item under consideration
+ * weightSelected    : Weight selected from elements [0,pos-1]
+ * weightNotSelected : Weight not selected from elements [0,pos-1]
+ * totWeight         : Total weight of all N items
+ * minWeight         : The weight the desired subset should at least have
+ * maxWeight         : The weight the desired subset should at most have
+ * 
+ * Input/Output:
+ * select            : Array defining which items are included in the subset (1) or not (0)
+ * 
+ * Return value: TRUE if a feasible subset has been found in this recursion or further down
+ *               in the current recursion tree, FALSE otherwise.
+ */
+int _SubsetSumExp(const long * const weights, long * const select, const long N, const long pos, const long weightSelected, const long weightNotSelected, const long totWeight, const long minWeight, const long maxWeight) {
+    
+    /* (A)
+     * By (C), we know that weightSelected <= maxWeight. If also
+     * minWeight <= weightSelected, we have found a feasible subset.
+     */
+    if(weightSelected >= minWeight) {
+        return TRUE;
+    }
+    
+    /* (B)
+     * End of tree
+     */
+    if(pos >= N) {
+        return FALSE;
+    }
+    
+    /* (C)
+     * All items are sorted by increasing weight. Hence, if adding
+     * the new weight will exceed the maximum weight, any subsequent
+     * weight will exceed the maximum weight too. So we may return FALSE.
+     */
+    if(weightSelected+weights[pos] > maxWeight) {
+        return FALSE;
+    }
+    
+    /* (D)
+     * Check whether there is enough weight left to reach minWeight.
+     */
+    if(minWeight > totWeight-weightNotSelected) {
+        return FALSE;
+    }
+    
+    /* Branch: Select the current item */
+    select[pos] = 1;
+    if(_SubsetSumExp(weights, select, N, pos+1, weightSelected+weights[pos], weightNotSelected, totWeight, minWeight, maxWeight)) {
+        return TRUE;
+    }
+    
+    /* Branch: Don't select the current item */
+    select[pos] = 0;
+    if(_SubsetSumExp(weights, select, N, pos+1, weightSelected, weightNotSelected+weights[pos], totWeight, minWeight, maxWeight)) {
+        return TRUE;
+    }
+    
+    /* If in both branches no feasible subset is found, return FALSE */
+    return FALSE;
+    
+} /* end _SubsetSumExp */
+
+
+/**
+ * Comparison function, taking two KKWeights, and comparing the values
+ * a->weight and b->weight in an ascending fashion.
+ * Used in KarmarkarKarp().
+ * 
+ * Input:
+ * a           : The left value
+ * b           : The right value
+ * 
+ * Return value:
+ *      -1 if a->weight < b->weight
+ *       0 if a->weight = b->weight
+ *       1 if a->weight > b->weight
+ */
+int compareKKWeights (const void *a, const void *b) {
+    long diff = ((struct KKWeight*)a)->weight - ((struct KKWeight*)b)->weight;
+    if(diff == 0)
+        return 0;
+    return (diff < 0) ? -1 : 1;
+} /* end compareKKWeights */
+
+/**
+ * Perform a subset sum using the Karmarkar-Karp differencing algorithm.
+ * This is a variant of the Karmarkar Karp algorithm that can be applied to the variant on Subset Sum as
+ * explained in SubsetSum(). This heuristic is faster than SubsetSum(), but may not find a feasible subset
+ * even if one exists.
+ * A dummy weight is used to convert the subset sum problem to the number partitioning problem, on which
+ * Karmarkar-Karp can be applied.
+ * 
+ * See SubsetSum()
+ */
+int KarmarkarKarp(const long * const weights, const long N, const long weightlo, const long weighthi, long **permutation, long **select) {
+    
+    long totalWeight = 0, i;
+    for(i=0; i<N; i++) {
+        totalWeight += weights[i];
+    }
+    
+    /* Compute min and max weight of smallest desired partition. Note that
+     * these values only make sense when considering the subset-sum problem.
+     * When considering the partition problem (with the dummy weight), their
+     * values have no meaning, apart from their difference which gives a bound
+     * on the maximal difference in the two partition weights.
+     */
+    long maxWeight = weightlo;
+    long minWeight = totalWeight - weighthi;
+    
+    /* If the two desired subsets have unequal size, we should add a dummy weight to make them equal */
+    int useDummyWeight = (weightlo != weighthi)?1:0;
+    
+    /* Initialization */
+    struct KKWeight *heapItems = (struct KKWeight*)malloc((N+useDummyWeight)*sizeof(struct KKWeight));
+    if(heapItems == NULL) {
+        fprintf( stderr, "KarmarkarKarp(): Not enough memory.\n" );
+        return FALSE;
+    }
+    
+    struct Graph Tree;
+    GraphInit(&Tree, N+useDummyWeight);
+    
+    /* Register all weights */
+    for(i=0; i<N; ++i) {
+        heapItems[i].key = i;
+        heapItems[i].weight = weights[i];
+    }
+    /* If necessary, also register the dummy weight */
+    if(useDummyWeight) {
+        heapItems[N].key = N;
+        heapItems[N].weight = totalWeight - (minWeight + maxWeight);
+    }
+    
+    /* From here, we are solving the Partition problem, which is what Karmarkar-Karp is actually designed for. */
+    
+    /* Create the heap */
+    struct heap Heap;
+    HeapInit(&Heap, sizeof(struct KKWeight), compareKKWeights);
+    Heapify(&Heap, heapItems, N+useDummyWeight);
+    
+    /* A heap item exists of two long values, the first holding an identification number (key),
+     * the second holding the corresponding weight.
+     */
+    struct KKWeight max1, max2; /* Key and weight of items with highest and second highest weight */
+    
+    /* The Karmarkar Karp differencing loop.
+     * In each iteration, the two largest weights are taken, of which the smaller weight is
+     * subtracted from both, leaving the larger weight in the heap (with reduced weight),
+     * and removing the smaller weight from the heap.
+     * Then between the two selected weights an edge is created, to signify that we put them in
+     * different sets.
+     */
+    while(Heap.numItems > 1) {
+        /* Highest weight */
+        HeapPop(&Heap, &max1);
+        
+        /* Second highest weight */
+        HeapPeek(&Heap, &max2);
+        
+        /* Compute difference and replace max2 with it */
+        max1.weight -= max2.weight;
+        HeapReplace(&Heap, &max1, NULL);
+        
+        /* Connect the two weights */
+        GraphAddEdge(&Tree, max1.key, max2.key);
+    }
+    
+    /* We are left with one differencing value. The weight of it
+     * equals the difference in total weight of the two subsets.
+     * We check that it is sufficiently small.
+     */
+    HeapPeek(&Heap, &max1);
+    
+    if(max1.weight > maxWeight-minWeight) {
+        HeapDestroy(&Heap); /* Also free()s heapItems */
+        GraphDestroy(&Tree);
+        return FALSE;
+    }
+    
+    HeapDestroy(&Heap); /* Also free()s heapItems */
+    
+    /* Assign colours (0/1) to the tree such that no two neighbours have equal colours */
+    TwoColorTree(&Tree);
+    
+    /* select[i] should be 1 if weight[i] belongs to the small subset, and 0 if it belongs
+     * to the large subset. Register the colour of the small subset.
+     */
+    long smallSubsetColour = (useDummyWeight) ? Tree.nodes[N].val : 1;
+    
+    /* Generate the output */
+    *permutation = (long*)malloc(N*sizeof(long));
+    *select = (long*)malloc(N*sizeof(long));
+    
+    for(i=0; i<N; ++i) {
+        (*permutation)[i] = i;
+        (*select)[i] = (smallSubsetColour==0) ? 1 - Tree.nodes[i].val : Tree.nodes[i].val;
+    }
+    
+    GraphDestroy(&Tree);
+    
+    return TRUE;
+} /* end KarmarkarKarp */
+
+
+/**
+ * Initialize a graph struct
+ * This assumes that the struct is not already initialized.
+ * 
+ * Input:
+ * numNodes          : The number of nodes to allocate memory for
+ * 
+ * Output:
+ * pGraph            : The graph struct
+ * 
+ * Returned memory allocations:
+ *     pGraph:       O(numNodes)    (multiple mallocs)
+ */
+void GraphInit(struct Graph* pGraph, long numNodes) {
+    pGraph->numNodes = numNodes;
+    pGraph->numEdges = 0;
+    pGraph->nodes = (struct Node*)malloc(numNodes*sizeof(struct Node));
+    
+    long i;
+    for(i=0; i<pGraph->numNodes; ++i) {
+        pGraph->nodes[i].key = i;
+        pGraph->nodes[i].val = 0;
+        pGraph->nodes[i].deg = 0;
+        pGraph->nodes[i].adjSize = 4;
+        pGraph->nodes[i].adjList = (long*)malloc(pGraph->nodes[i].adjSize*sizeof(long));
+    }
+}
+
+/**
+ * Free a graph struct
+ * 
+ * Input/Output:
+ * pGraph            : The graph struct
+ */
+void GraphDestroy(struct Graph* pGraph) {
+    long i;
+    for(i=0; i<pGraph->numNodes; ++i) {
+        free(pGraph->nodes[i].adjList);
+    }
+    free(pGraph->nodes);
+    pGraph->numNodes = 0;
+}
+
+/**
+ * Check the size of the adjacency list of a node. If the adjacency
+ * list is full, resize it.
+ * 
+ * Input:
+ * i                 : The node number
+ * 
+ * Input/Output:
+ * pGraph            : The graph struct
+ */
+void GraphCheckAdjSize(struct Graph* pGraph, long i) {
+    if(pGraph->nodes[i].deg == pGraph->nodes[i].adjSize) {
+        pGraph->nodes[i].adjSize *= 2;
+        pGraph->nodes[i].adjList = (long*)realloc(pGraph->nodes[i].adjList, pGraph->nodes[i].adjSize*sizeof(long));
+        if(pGraph->nodes[i].adjList == NULL) {
+            fprintf( stderr, "GraphCheckAdjSize(): Not enough memory.\n" );
+            exit(1);
+        }
+    }
+}
+
+/**
+ * Add an edge to the graph, by adding the nodes of the
+ * edge to both their adjacency lists.
+ * 
+ * Input:
+ * u, v              : The node numbers of the nodes in the edge
+ * 
+ * Input/Output:
+ * pGraph            : The graph struct
+ */
+void GraphAddEdge(struct Graph* pGraph, long u, long v) {
+    GraphCheckAdjSize(pGraph, u);
+    GraphCheckAdjSize(pGraph, v);
+    
+    pGraph->nodes[u].adjList[pGraph->nodes[u].deg++] = v;
+    pGraph->nodes[v].adjList[pGraph->nodes[v].deg++] = u;
+}
+
+/**
+ * Assign values 0 and 1 to nodes, in such a way that every two
+ * adjacent nodes have different values (colours). Node 0 is
+ * assigned colour 0, with which the entire colouring is
+ * unambiguously defined.
+ * This function assumes the inputted graph is a tree. Else,
+ * the obtained colouring may not be valid.
+ * 
+ * Input/Output:
+ * pGraph            : The graph struct
+ */
+void TwoColorTree(struct Graph* pGraph) {
+    long color = 0;
+    long *nodeList = (long*)malloc(pGraph->numNodes * sizeof(long));
+    long *currListStart, *currListEnd, *nextListEnd;
+    nodeList[0] = 0;
+    currListStart = nodeList;
+    currListEnd = &nodeList[1];
+    nextListEnd = &nodeList[1];
+    long nodeKey, i;
+    
+    for(i=0; i<pGraph->numNodes; ++i) {
+        pGraph->nodes[i].val = -1;
+    }
+    
+    /* nodeList is of size such that each node may appear exactly once in it.
+     * It is constructed as follows:
+     * +------------------+--------------+-------------+------------+
+     * |     Visited      |   Visiting   |  To visit   | Free space |
+     * +------------------+--------------+-------------+------------+
+     * ^                  ^              ^             ^
+     * nodeList           currListStart  currListEnd   nextListEnd
+     * 
+     * `Visiting` contains all nodes that we are currently applying `color` to.
+     * `To visit` contains all nodes that we will be applying the other color to in the next iteration.
+     */
+    
+    while(currListStart < currListEnd) {
+        
+        /* Visit all nodes we should be `Visiting` */
+        while(currListStart < currListEnd) {
+            nodeKey = *(currListStart++);
+            pGraph->nodes[nodeKey].val = color;
+            
+            /* We are required `To visit` the adjacent nodes in the next iteration */
+            for(i=0; i<pGraph->nodes[nodeKey].deg; ++i) {
+                if(pGraph->nodes[pGraph->nodes[nodeKey].adjList[i]].val == -1) {
+                    *(nextListEnd++) = pGraph->nodes[nodeKey].adjList[i];
+                }
+            }
+            
+        }
+        
+        /* Next iteration: change `To visit` to `Visiting` */
+        color = (color+1)%2;
+        currListEnd = nextListEnd;
+    }
+    
+    free(nodeList);
+}
diff --git a/src/SubsetSum.h b/src/SubsetSum.h
new file mode 100644
index 0000000000000000000000000000000000000000..58bdd39683b28c8a18ebb8da05fec5a75a053bbb
--- /dev/null
+++ b/src/SubsetSum.h
@@ -0,0 +1,40 @@
+#ifndef __SubsetSum_h__
+#define __SubsetSum_h__
+
+#include "Sort.h"
+
+/*** Graph data structures ***/
+struct Node {
+    long key;
+    long val;
+    long deg;
+    long adjSize;
+    long *adjList;
+};
+
+struct Graph {
+    long numNodes;
+    long numEdges;
+    struct Node *nodes;
+};
+
+/* Weight used in KarmarkarKarp() */
+struct KKWeight {
+    long key;
+    long weight;
+};
+
+/* Function declarations for SubsetSum.c */
+int SubsetSum(long * const weights, const long N, const long weightlo, const long weighthi, long **permutation, long **select);
+int SubsetSumExp(long * const weights, const long N, const long weightlo, const long weighthi, long **permutation, long **select);
+int KarmarkarKarp(const long * const weights, const long N, const long weightlo, const long weighthi, long **permutation, long **select);
+
+/* Function declarations for Graph, used in KarmarkarKarp() */
+void GraphInit(struct Graph* pGraph, long numNodes);
+void GraphDestroy(struct Graph* pGraph);
+void GraphCheckAdjSize(struct Graph* pGraph, long i);
+void GraphAddEdge(struct Graph* pGraph, long u, long v);
+void TwoColorTree(struct Graph* pGraph);
+
+#endif /* __SubsetSum_h__ */
+
diff --git a/src/ZeroVolumeSearch.c b/src/ZeroVolumeSearch.c
new file mode 100644
index 0000000000000000000000000000000000000000..4dc1f79e602ada9d3be377cb3ddea7fc77169254
--- /dev/null
+++ b/src/ZeroVolumeSearch.c
@@ -0,0 +1,392 @@
+
+#include "ZeroVolumeSearch.h"
+#include "SubsetSum.h"
+
+/**
+ * Search for a zero-volume split of a (sub)matrix.
+ * If such a split is found, the array values in pM->i, pM->j,
+ * pM->ReValue and pM->ImValue are rearranged such that the
+ * nonzeros in [0,mid-1] form the smaller partition, and the
+ * nonzeros in [mid,nnz-1] form the larger partition.
+ * 
+ * Input:
+ * weightlo          : The maximum weight of the smallest partition
+ * weighthi          : The maximum weight of the largest partition
+ * pOptions          : Options struct
+ * 
+ * Input/Output:
+ * pM                : The matrix (m-by-n)
+ * 
+ * Output:
+ * mid               : The index of the first nonzero in the second partition. This value 
+ *                     is only set if return value == TRUE.
+ * 
+ * Return value: TRUE if a feasible zero-volume split has been found, FALSE otherwise.
+ *               If TRUE, the found zero-volume split has already been applied to pM, i.e.
+ *               the nonzeros are reordered such that nonzeros [0,mid-1] belong to partition 0,
+ *               and nonzeros [mid,nnz-1] belong to partition 1.
+ */
+int ZeroVolumeSearch(struct sparsematrix *pM, long weightlo, long weighthi, long *mid, const struct opts *pOptions) {
+    
+    long numComponents = 0;
+    long *componentWeights = NULL;
+    long *rowToComponent = NULL;
+    
+    /* Search for connected components */
+    if(!DetectConnectedComponents(pM, weighthi, &numComponents, &componentWeights, &rowToComponent, pOptions)) {
+        return FALSE;
+    }
+    
+    if(numComponents < 2) {
+        free(componentWeights);
+        free(rowToComponent);
+        if(numComponents == 1) {
+            /* As DetectConnectedComponents() returned TRUE, the single component the matrix consists of has
+             * a weight lower than weighthi, which implies that we may put all nonzeros in one single partition.
+             */
+            *mid = 0;
+            return TRUE;
+        }
+        return FALSE;
+    }
+    
+    long *componentIds = NULL;
+    long *componentsSelected = NULL;
+    
+    /* Determine whether the components can be distributed over the partitions */
+    int found = SubsetSum(componentWeights, numComponents, weightlo, weighthi, &componentIds, &componentsSelected);
+    free(componentWeights);
+    
+    if(found) {
+        
+        /* Form a component-to-partition mapping */
+        long *componentToPartition = (long*)malloc(numComponents*sizeof(long));
+        if( componentToPartition == NULL ) {
+            fprintf( stderr, "ZeroVolumeSearch(): Not enough memory.\n" );
+            free(rowToComponent);
+            free(componentIds);
+            free(componentsSelected);
+            return FALSE;
+        }
+        
+        long i;
+        for(i=0; i<numComponents; ++i) {
+            /* In componentsSelected, those entries with value 1 belong to the small partition.
+             * For conceptual ease, we place the small partition before the large partition,
+             * and make 0 the small partition and 1 the large partition.
+             */
+            componentToPartition[componentIds[i]] = 1-componentsSelected[i];
+        }
+        
+        /* 'Collapse the walls'; a single iteration of quicksort
+         * Swap all zeros to the left, and all ones to the right.
+         */
+        long lo = 0, hi = pM->NrNzElts-1;
+        do {
+            
+            while(componentToPartition[rowToComponent[pM->i[lo]]] == 0 && lo <= hi) {
+                ++lo;
+            }
+            while(componentToPartition[rowToComponent[pM->i[hi]]] == 1 && lo <= hi) {
+                --hi;
+            }
+            
+            if(lo < hi) {
+                /* Swap entries */
+                SwapLong(pM->i, lo, hi);
+                SwapLong(pM->j, lo, hi);
+                if(pM->ReValue != NULL)
+                    SwapDouble(pM->ReValue, lo, hi);
+                if(pM->ImValue != NULL)
+                    SwapDouble(pM->ImValue, lo, hi);
+                ++lo;
+                --hi;
+            }
+            
+        }
+        while(lo <= hi);
+        /* At this point, hi+1 = lo; and part0 = [0,hi], part1 = [lo,nnz-1] */
+        
+        /* Register where new partition starts */
+        *mid = lo;
+        
+        free(componentToPartition);
+        free(componentIds);
+        free(componentsSelected);
+    }
+    
+    free(rowToComponent);
+    
+    return (found == TRUE);
+    
+} /* end ZeroVolumeSearch */
+
+/**
+ * Detect connected components in a (sub)matrix.
+ * When viewing the (sub)matrix as a (bipartite) graph, with the columns and rows as vertices
+ * and the nonzeros as edges, this amounts to finding connected components in this graph.
+ * This is done using a breadth-first search, starting at a column and then switching between
+ * columns and rows in a breadth-first fashion.
+ * In case pM contains colWeights, this function assumes these are all positive.
+ * 
+ * Input:
+ * pM                : The matrix (m-by-n)
+ * maxWeight         : Maximum weight of a component. If a component is detected of weight
+ *                     larger than this value, it will stop and return FALSE.
+ * pOptions          : Options struct
+ * 
+ * Output:
+ * numComponents     : The number of components found (Only if return value == TRUE)
+ * componentWeights  : The number of nonzeros in each found component (Only if return value == TRUE)
+ * rowAssignments    : The components to which each row is assigned (Only if return value == TRUE)
+ * 
+ * Returned memory allocations:
+ *     componentWeights:  O(numComponents) (Only if return value == TRUE)
+ *     rowAssignments:    O(m)             (Only if return value == TRUE)
+ * 
+ * Return value: FALSE if an error occurs, TRUE otherwise
+ */
+
+int DetectConnectedComponents(struct sparsematrix *pM, long maxWeight, long *numComponents, long **componentWeights, long **rowAssignments, const struct opts *pOptions) {
+    
+    struct CRCS CRS, CCS;
+    
+    if(!SparseMatrixToCRS_CCS(pM, &CCS, &CRS)) {
+        return FALSE;
+    }
+    
+    /* Array of numbers of nonzeros in each component */
+    long compArrLen = 8;
+    long *component_weights = (long*)malloc(sizeof(long)*compArrLen);
+    long component = -1; /* Current component */
+    
+    /* Arrays containing the components the rows/columns are assigned to. */
+    long *rowAssign = (long*)malloc(sizeof(long)*pM->m);
+    long *colAssign = (long*)malloc(sizeof(long)*pM->n);
+    
+    /* Arrays containing the rows/columns we still have to process. */
+    long *rowStack = (long*)malloc(sizeof(long)*pM->m);
+    long *colStack = (long*)malloc(sizeof(long)*pM->n);
+    long *rowStackHead = rowStack;
+    long *colStackHead = colStack;
+    
+    /* Check memory */
+    if( component_weights == NULL || rowAssign == NULL || colAssign == NULL ||
+        rowStack == NULL || colStack == NULL ) {
+        if(component_weights != NULL)
+            free(component_weights);
+        if(rowAssign != NULL)
+            free(rowAssign);
+        if(colAssign != NULL)
+            free(colAssign);
+        if(rowStack != NULL)
+            free(rowStack);
+        if(colStack != NULL)
+            free(colStack);
+        fprintf( stderr, "DetectConnectedComponents(): Not enough memory.\n" );
+        return FALSE;
+    }
+    
+    long i, k, col, row, start, end; /* Indices and iterators */
+    
+    /* Boolean variables */
+    int symmetric = (pM->m == pM->n && (pM->MMTypeCode[3] == 'S' || pM->MMTypeCode[3] == 'K' || pM->MMTypeCode[3] == 'H') && pOptions->SymmetricMatrix_UseSingleEntry == SingleEntYes);
+    int dummies = (pM->m == pM->n && pM->NrDummies > 0);
+    int colWeights = (pM->MMTypeCode[0] == 'W' && pM->NrColWeights > 0);
+    /* If colWeights is false, we count the nonzeros as the weight of a submatrix.
+     * Every nonzero is counted exactly twice (once in each direction), hence we divide by 2 in the end.
+     * If colWeights is true, the weight of a submatrix is based on the column weights.
+     */
+    
+    long numAssignedRows = 0;
+    int broken = FALSE;
+    
+    /* Initialize */
+    for(i=0; i<pM->m; ++i)
+        rowAssign[i] = -1;
+    for(i=0; i<pM->n; ++i)
+        colAssign[i] = -1;
+    
+    /* Walk through all rows. As soon as all rows have been processed,
+     * all nonzeros have been assigned to a component and we are finished.
+     */
+    for(i=0; i<pM->m; ) {
+        /* If we have no new rows to process, we have finished a component.
+         * In that case, find the next unassigned row to assign it to the next component.
+         */
+        if(rowStackHead-rowStack == 0) {
+            if(rowAssign[i] != -1) {
+                /* Row i is already assigned to a component */
+                ++i;
+                continue;
+            }
+            
+            /* Found an unassigned row */
+            if(component == -1 || component_weights[component] > 0) {
+                /* We have an unassigned row unrelated to any previous components, and
+                 * the last component is non-empty, hence create a new one.
+                 * (Or we have no other components at all; in which case we als should create one.)
+                 */
+                ++component;
+                
+                /* Check component array length */
+                if(component >= compArrLen) {
+                    /* Array is too small; enlarge it */
+                    compArrLen *= 2;
+                    long *newArr = (long*)realloc(component_weights, sizeof(long)*compArrLen);
+                    if (newArr == NULL) {
+                        fprintf(stderr, "DetectConnectedComponents(): Not enough memory.\n");
+                        broken = TRUE;
+                        break;
+                    }
+                    else {
+                        component_weights = newArr;
+                    }
+                }
+                
+                component_weights[component] = 0;
+                numAssignedRows = 0;
+            }
+            
+            *(rowStackHead++) = i;
+            rowAssign[i] = component;
+        }
+        
+        /* Process all found rows, and register new columns */
+        while(rowStackHead-rowStack>0) {
+            row = *(--rowStackHead);
+            start = CRS.starts[row];
+            end = CRS.starts[row+1];
+            if(start == end) {
+                continue;
+            }
+            if(!colWeights) {
+                component_weights[component] += end-start;
+            }
+            ++numAssignedRows;
+            
+            if(symmetric) {
+                /* When a row is added, the column should also be added */
+                if(!colWeights) {
+                    component_weights[component] += end-start;
+                }
+                if(colAssign[row] == -1) {
+                    *(colStackHead++) = row;
+                    colAssign[row] = component;
+                }
+            }
+            
+            /* Walk through all nonzeros in this row */
+            for(k=start; k<end; ++k) {
+                col = CRS.indices[k];
+                if(colAssign[col] == -1) {
+                    *(colStackHead++) = col;
+                    colAssign[col] = component;
+                }
+                if(col == row && !colWeights) {
+                    if(symmetric) {
+                        /* We only count diagonal elements once */
+                        --component_weights[component];
+                    }
+                    if(dummies && pM->dummy[row]) {
+                        /* Dummy element */
+                        --component_weights[component];
+                    }
+                }
+            }
+            
+        } /* end while */
+        
+        /* Process all found columns, and register new rows */
+        while(colStackHead-colStack>0) {
+            col = *(--colStackHead);
+            start = CCS.starts[col];
+            end = CCS.starts[col+1];
+            if(start == end) {
+                continue;
+            }
+            if(!colWeights) {
+                component_weights[component] += end-start;
+            }
+            else {
+                component_weights[component] += pM->ColWeights[col];
+            }
+            
+            if(symmetric) {
+                /* When a column is added, the row should also be added */
+                if(!colWeights) {
+                    component_weights[component] += end-start;
+                }
+                if(rowAssign[col] == -1) {
+                    *(rowStackHead++) = col;
+                    rowAssign[col] = component;
+                }
+            }
+            
+            /* Walk through all nonzeros in this column */
+            for(k=start; k<end; ++k) {
+                row = CCS.indices[k];
+                if(rowAssign[row] == -1) {
+                    *(rowStackHead++) = row;
+                    rowAssign[row] = component;
+                }
+                if(row == col && !colWeights) {
+                    if(symmetric) {
+                        /* We only count diagonal elements once */
+                        --component_weights[component];
+                    }
+                    if(dummies && pM->dummy[row]) {
+                        /* Dummy element */
+                        --component_weights[component];
+                    }
+                }
+            }
+        } /* end while */
+        
+        if(component_weights[component] > maxWeight*(colWeights?1:2)) {
+            broken = TRUE;
+            break;
+        }
+        
+    } /* end for */
+    
+    free(rowStack);
+    free(colStack);
+    free(colAssign);
+    freeCRCS(&CCS);
+    freeCRCS(&CRS);
+    
+    if(broken == TRUE) {
+        free(component_weights);
+        free(rowAssign);
+        return FALSE;
+    }
+    
+    /* If the last component is empty, discard it. */
+    if(component_weights[component] == 0) {
+        --component;
+        if(numAssignedRows > 0) {
+            /* Rows were added, but the weight is 0. As we assume all weights are positive, we may assign the rows to another component. */
+            for(i=0; i<pM->m; ++i) {
+                if(rowAssign[i] > component) {
+                    rowAssign[i] = 0;
+                }
+            }
+        }
+    }
+    
+    if(!colWeights) {
+        /* Every nonzero is counted twice, so divide by two */
+        for(i=0; i<component+1; ++i) {
+            component_weights[i] /= 2;
+        }
+    }
+    
+    /* Assign values to return variables */
+    *componentWeights = component_weights;
+    *numComponents = component + 1;
+    *rowAssignments = rowAssign;
+    
+    return TRUE;
+    
+} /* end DetectConnectedComponents */
diff --git a/src/ZeroVolumeSearch.h b/src/ZeroVolumeSearch.h
new file mode 100644
index 0000000000000000000000000000000000000000..cfdf833ebc20b8a7ae7cc338f5c840eff8c1b0cf
--- /dev/null
+++ b/src/ZeroVolumeSearch.h
@@ -0,0 +1,15 @@
+#ifndef __ZeroVolumeSearch_h__
+#define __ZeroVolumeSearch_h__
+
+#include "Sort.h"
+#include "SparseMatrix.h"
+
+/* Function declarations for ZeroVolumeSearch.c */
+int ZeroVolumeSearch(struct sparsematrix *pM, long weightlo, long weighthi, long *mid, const struct opts *pOptions);
+int SubsetSum(long * const weights, const long N, const long weightlo, const long weighthi, long **permutation, long **select);
+int SubsetSumExp(long * const weights, const long N, const long weightlo, const long weighthi, long **permutation, long **select);
+int KarmarkarKarp(const long * const weights, const long N, const long weightlo, const long weighthi, long **permutation, long **select);
+int DetectConnectedComponents(struct sparsematrix *pM, long maxWeight, long *numComponents, long **componentWeights, long **rowAssignments, const struct opts *pOptions);
+
+#endif /* __ZeroVolumeSearch_h__ */
+
diff --git a/tests/Makefile b/tests/Makefile
index 438ed6d3089924505c1a7f7841a304abe48ddaff..df8e903d34b3bea7996fa49c6fae5a4f94cbbe4e 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -21,13 +21,13 @@ test_DistributeVecGreedyImprove test_InitSums test_GenerateHistogram \
 test_DistributeVecOrig test_DistributeVecOrigEq test_DistributeVec \
 test_BalanceParts test_DetermineSplit test_logb2 \
 test_ComputeWeight test_SplitMatrixSimple test_SplitMatrixKLFM \
-test_DistributeMatrixMondriaan \
+test_SplitMatrixUpperBound test_DistributeMatrixMondriaan \
 test_CreateNewBiPartHyperGraph test_DeleteBiPartHyperGraph \
 test_SparseMatrix2BiPartHyperGraph test_BiPartHyperGraph2SparseMatrix \
-test_SparseMatrixSymmetric2Full test_SparseMatrixSymmetricLower2Random \
-test_SparseMatrixSymmetricRandom2Lower test_AddDummiesToSparseMatrix \
-test_RemoveDummiesFromSparseMatrix test_MMSparseMatrixInit \
-test_MMDeleteSparseMatrix test_MMSparseMatrixAllocateMemory \
+test_SparseMatrixSymmetric2Full test_SparseMatrixFull2Symmetric \
+test_SparseMatrixSymmetricLower2Random test_SparseMatrixSymmetricRandom2Lower \
+test_AddDummiesToSparseMatrix test_RemoveDummiesFromSparseMatrix \
+test_MMSparseMatrixInit test_MMDeleteSparseMatrix test_MMSparseMatrixAllocateMemory \
 test_MMSparseMatrixFreeMemory test_MMSparseMatrixReadEntries \
 test_MMSparseMatrixReadPstart test_MMSparseMatrixReadWeights \
 test_MMSparseMatrixReadTail test_MMSparseMatrixReadHeader \
@@ -47,7 +47,10 @@ test_ClearMoveLog test_MoveVertex test_UpdateGains test_HKLFM test_RunHKLFM \
 test_LambdaLambdaMinusOneMetric \
 test_FindOptimalPathMatching \
 test_MatchATA \
-test_CoarsenGraph test_RunMLGraphPart
+test_CoarsenGraph test_RunMLGraphPart \
+test_SparseMatrixToCRS_CCS test_DetectConnectedComponents \
+test_Heap test_TwoColorTree test_SubsetSum test_ZeroVolumeSearch \
+test_FreeNonzeros
 
 .SECONDARY: ${TESTTARGETS:%=%.o}
 
@@ -243,6 +246,9 @@ test_SplitMatrixSimple: test_SplitMatrixSimple.o ${DISTRIBUTEMAT:%=%.o}
 test_SplitMatrixKLFM: test_SplitMatrixKLFM.o ${DISTRIBUTEMAT:%=%.o} ${DISTRIBUTEVECLIB:%=%.o}
 	make -r OBJDEPS='$^' $@.target
 
+test_SplitMatrixUpperBound: test_SplitMatrixUpperBound.o testHelper_DisconnectedMatrix.o ${DISTRIBUTEMAT:%=%.o}
+	make -r OBJDEPS='$^' $@.target
+
 test_DistributeMatrixMondriaan: test_DistributeMatrixMondriaan.o ${DISTRIBUTEMAT:%=%.o}
 	make -r OBJDEPS='$^' $@.target
 
@@ -270,6 +276,9 @@ test_BiPartHyperGraph2SparseMatrix: test_BiPartHyperGraph2SparseMatrix.o ${GRAPH
 test_SparseMatrixSymmetric2Full: test_SparseMatrixSymmetric2Full.o ${SPARSEMATRIX:%=%.o} ${MATALLOC:%=%.o}
 	make -r OBJDEPS='$^' $@.target
 
+test_SparseMatrixFull2Symmetric: test_SparseMatrixFull2Symmetric.o ${SPARSEMATRIX:%=%.o}
+	make -r OBJDEPS='$^' $@.target
+
 test_SparseMatrixSymmetricLower2Random: test_SparseMatrixSymmetricLower2Random.o ${SPARSEMATRIX:%=%.o} ${MATALLOC:%=%.o}
 	make -r OBJDEPS='$^' $@.target
 
@@ -357,6 +366,27 @@ test_CRS2CCS: test_CRS2CCS.o ${CARTESIAN:%=%.o}
 test_MMWriteCartesianSubmatrices: test_MMWriteCartesianSubmatrices.o ${CARTESIAN:%=%.o}
 	make -r OBJDEPS='$^' $@.target
 
+test_SparseMatrixToCRS_CCS: test_SparseMatrixToCRS_CCS.o ${SPARSEMATRIX:%=%.o}
+	make -r OBJDEPS='$^' $@.target
+
+test_DetectConnectedComponents: testHelper_DisconnectedMatrix.o test_DetectConnectedComponents.o ${SPARSEMATRIX:%=%.o} ${ZEROVOLUMESEARCH:%=%.o}
+	make -r OBJDEPS='$^' $@.target
+
+test_Heap: test_Heap.o ${HEAP:%=%.o}
+	make -r OBJDEPS='$^' $@.target
+
+test_TwoColorTree: test_TwoColorTree.o ${SUBSETSUM:%=%.o}
+	make -r OBJDEPS='$^' $@.target
+
+test_SubsetSum: test_SubsetSum.o ${SUBSETSUM:%=%.o}
+	make -r OBJDEPS='$^' $@.target
+
+test_ZeroVolumeSearch: testHelper_DisconnectedMatrix.o test_ZeroVolumeSearch.o ${SPARSEMATRIX:%=%.o} ${DISTRIBUTEVECLIB:%=%.o} ${ZEROVOLUMESEARCH:%=%.o}
+	make -r OBJDEPS='$^' $@.target
+
+test_FreeNonzeros: test_FreeNonzeros.o ${SPARSEMATRIX:%=%.o} ${FREENONZEROS:%=%.o}
+	make -r OBJDEPS='$^' $@.target
+
 test_GetParameters: test_GetParameters.o ${OPTIONS:%=%.o}
 	make -r OBJDEPS='$^' $@.target
 
diff --git a/tests/Mondriaan.defaults b/tests/Mondriaan.defaults
index 30f3ea1bf69197cc614050c09c7e4093845dc919..41e0f6ec75de6c01a60dc82cdf542d69d5bcc5f6 100644
--- a/tests/Mondriaan.defaults
+++ b/tests/Mondriaan.defaults
@@ -7,16 +7,22 @@ SplitMethod                                    KLFM
 Partitioner                                    mondriaan
 Metric                                         lambda1
 Discard_Free_Nets                              yes
+ZeroVolumeSearch                               no
+ImproveFreeNonzeros                            no
+CheckUpperBound                                no
 SquareMatrix_DistributeVectorsEqual            no 
 SquareMatrix_DistributeVectorsEqual_AddDummies yes 
 SymmetricMatrix_UseSingleEntry                 no
 SymmetricMatrix_SingleEntryType                lower 
 Coarsening_NrVertices                          200 
 Coarsening_MaxCoarsenings                      128
+Coarsening_NrMatchArbitrary                    0 
 Coarsening_MaxNrVtxInMatch                     2 
 Coarsening_StopRatio                           0.05 
 Coarsening_VtxMaxFractionOfWeight              0.2 
 Coarsening_MatchingStrategy                    inproduct 
+Coarsening_MatchingATAMatcher                  pga 
+Coarsening_MatchingATAFinder                   inproduct 
 Coarsening_InprodMatchingOrder                 decrwgt 
 Coarsening_FineSwitchLevel                     2 
 Coarsening_NetScaling                          linear
@@ -27,6 +33,7 @@ KLFM_InitPart_MaxNrLoops                       25
 KLFM_InitPart_MaxNrNoGainMoves                 200 
 KLFM_Refine_MaxNrLoops                         25 
 KLFM_Refine_MaxNrNoGainMoves                   200 
+Iterative_Refinement                           never
 VectorPartition_Step3                          random 
 VectorPartition_MaxNrLoops                     10 
 VectorPartition_MaxNrGreedyImproves            10
@@ -35,4 +42,3 @@ OutputMode                                     original
 Seed                                           99 
 Permute                                        none
 EnforceSymmetricPermutation                    no
-Iterative_Refinement                           never
diff --git a/tests/m300_arc130.mtx-C8 b/tests/m300_arc130.mtx-C8
index 36f879f627686ff4f5ce34b6ff9a9b21447f9d7c..cdea01d7c5dde9a7337b7275e92ebc096e98a074 100644
--- a/tests/m300_arc130.mtx-C8
+++ b/tests/m300_arc130.mtx-C8
@@ -1,623 +1,645 @@
 130 130 8
-1 32 35
-5
-6
-7
-13
-17
-18
-19
-25
-30
-35
-44
-51
-55
-60
-61
-63
-65
-68
-71
-76
-79
-80
-85
-87
-105
-110
-114
-115
-116
-119
-120
-125
-2
+1 17 60
 3
-6
-7
-9
-13
-14
-18
-19
-28
-29
-30
-34
-40
-42
-45
-49
-54
-55
-56
-60
-64
-65
-70
-74
-79
-84
-85
-90
-99
-110
-114
-115
-124
-130
-2 34 63
-12
-20
-23
-39
-40
-41
-42
-43
-45
-46
-50
-52
-56
-62
-70
-72
-73
-82
+8
+22
+32
+47
+57
+77
 83
-86
-88
-89
-90
-91
+87
 92
-93
-95
-99
-100
-106
-108
-111
+103
+112
+113
+117
 118
-130
+123
+127
+1
 2
 3
+4
+5
 6
 7
+8
 9
-13
+10
+11
 14
+15
+16
+17
 18
-26
+20
+22
+27
 28
-29
-30
-31
 32
-34
-36
+33
 37
-40
+38
 42
-45
+43
+47
 48
-49
 52
 53
-54
-55
-56
-60
-64
-65
+57
+58
+62
+63
 67
-70
+68
 72
 73
-74
 77
 78
-79
-81
+82
 83
-84
-85
-90
-91
+87
+88
 92
 93
-96
 97
-99
-101
-106
+98
+102
+103
+107
 108
-110
+112
 113
-114
-115
 117
 118
-121
-124
+122
+123
 127
 128
-130
-3 34 62
-17
-19
-20
-23
-43
-50
+2 18 53
+1
+11
+15
+16
+26
+33
+36
+46
 51
-52
 56
-60
-61
-62
-63
-65
-68
-70
-71
-72
 73
 76
-79
-80
-82
-83
 86
-87
-88
-90
 91
-93
-95
 111
 116
-119
+126
+128
 1
+2
+3
 4
 5
+6
+7
 8
+9
 10
 11
 12
+13
+14
 15
 16
 17
+18
 20
-23
-27
+26
+28
+29
+31
 33
-35
-38
-39
+34
+36
 41
 43
 44
 46
-47
-50
+48
 51
-57
-58
-59
+55
+56
 61
-62
 63
 66
 68
-69
 71
+73
 75
 76
-80
-82
+81
 86
-87
-88
-89
-94
-95
-98
-100
-102
-103
-104
-105
-107
-109
+91
+96
+101
+106
 111
-112
 116
-119
-120
-122
-123
-125
+121
 126
-129
-4 32 37
+128
+3 31 42
+4
 5
-6
-7
 12
 13
-18
-25
-30
+23
+24
+28
 35
 39
-40
-41
-42
 44
 45
-46
-55
+54
+60
+63
+68
+79
+80
+84
 85
-89
-92
+93
+94
+95
+98
 99
-100
 105
-106
-108
+109
 110
-114
 115
-118
 120
 125
-130
+129
 1
-4
+2
 5
-8
-10
+9
 11
 12
+13
+14
 15
-16
-17
-20
-25
-33
-35
+18
+24
+28
+29
+38
 39
-41
+40
+43
 44
-46
+45
 50
-58
+55
 59
+60
 63
-68
-69
-75
-80
+73
+78
+79
+88
 89
-94
-95
+93
 100
+103
 104
-105
 109
+110
 119
 120
+123
+124
 125
 129
-5 32 42
-3
+130
+4 31 42
 4
-9
-14
-22
+5
+12
+13
+23
 24
-38
-49
-53
+28
+35
+39
+44
+45
 54
-58
-59
-64
-69
-74
-78
+60
+63
+68
+79
+80
 84
+85
+93
 94
-97
+95
 98
-102
-103
-104
-107
+99
+105
 109
-112
-113
-117
-123
-124
-128
+110
+115
+120
+125
 129
-2
+3
 4
+6
 7
-9
+8
 10
-12
 16
-18
-22
-24
-27
-28
+17
+20
+23
+30
 33
 34
-37
-38
-39
-43
-47
+35
 48
+49
 53
-57
+54
+58
 64
-67
+65
 68
-72
-73
+69
+70
 74
-78
-79
+75
+80
+83
 84
-93
+85
+90
+94
+95
+98
 99
-107
+105
 108
-112
 113
-117
-122
-124
+114
+115
+118
 128
-129
-6 32 42
-3
-4
-9
-14
-22
-24
-38
+5 36 62
+7
+20
+21
+27
+29
+31
+40
+42
+43
 49
+50
+52
 53
-54
 58
 59
 64
-69
-74
+65
+66
+70
+72
 78
-84
-94
+88
+96
 97
-98
+100
+101
 102
-103
 104
+106
 107
-109
-112
-113
-117
-123
+108
+114
+119
+121
+122
 124
-128
-129
-1
-3
-5
+2
+4
 6
-8
-11
-13
+7
+10
+12
 14
 15
-17
+16
 20
+21
+27
+28
 29
+30
 32
+33
+36
+39
+41
 42
 44
-49
-52
+45
+50
+51
+53
 54
+55
+57
 58
 59
+60
 62
-63
-69
+64
+71
+72
+75
 77
-82
-83
+78
+79
+80
+81
+85
+86
 87
-88
-89
+91
 92
 94
-97
-98
 102
 103
 104
-109
+105
+106
+111
 114
+115
 118
-119
+121
 123
 127
-7 32 37
-1
+128
+130
+6 28 46
 2
-8
+6
+9
 10
-11
-15
-16
-21
-26
-27
-28
-29
-31
-32
-33
+14
+17
+18
+19
+25
+30
 34
-36
 37
-47
+38
+41
 48
-57
-66
+55
+61
+62
 67
+69
+71
+74
 75
-77
 81
-96
-101
-121
-122
-126
-127
+82
+89
+90
+130
 2
 4
-5
+6
 7
-9
 10
+12
+14
 15
 16
+19
 20
-26
-29
+27
+28
+30
+32
 33
-34
 36
+41
 42
-43
-44
-47
-48
+45
+50
 51
-56
+55
 57
+58
+60
 62
-67
 71
-73
-76
+72
+75
 77
+80
 81
-82
+85
 86
 87
+91
 92
-96
 102
+105
+106
 111
-117
-8 31 37
-1
+115
+121
+127
+130
+7 33 43
 2
-8
+6
+7
+9
 10
-11
-15
-21
-26
+14
+18
+25
 27
-28
 29
+30
 31
-32
-33
-34
-36
 37
-47
-48
-57
+40
+41
+42
+50
+52
+53
+55
+58
+59
 66
 67
+71
 75
-77
+78
 81
+90
+97
+100
+121
+130
+1
+3
+5
+8
+9
+11
+13
+17
+18
+25
+26
+31
+35
+37
+40
+46
+47
+52
+56
+61
+63
+65
+66
+67
+68
+70
+76
+82
+90
+95
 96
+97
+100
 101
-121
+107
+110
+112
+116
+117
+120
 122
+125
 126
-127
+8 31 63
+17
+19
+20
+21
+34
+38
+43
+48
+49
+61
+62
+64
+65
+69
+70
+72
+74
+82
+88
+89
+96
+101
+102
+104
+106
+107
+108
+114
+119
+122
+124
 1
 3
-6
+5
 8
+9
 11
-12
 13
-14
 17
 18
-21
-27
-28
+26
 31
-32
+34
+35
 37
-41
+38
+40
+43
 46
+47
+48
+49
 52
-55
+56
 61
 63
+65
 66
+67
 68
-72
-75
-91
+69
+70
+73
+74
+76
+82
+83
+84
+88
+89
+90
+93
+95
+96
 97
+98
+99
+100
 101
-106
 107
+108
+109
+110
 112
+113
 116
-121
+117
+119
+120
 122
+124
+125
 126
-127
+129
diff --git a/tests/m300_arc130.mtx-I8 b/tests/m300_arc130.mtx-I8
index 113a44bab9a10b164e7ba51bfcede73b9a995960..27f5899c1dc83ecebdde9baa0d11fa9b55dec626 100644
--- a/tests/m300_arc130.mtx-I8
+++ b/tests/m300_arc130.mtx-I8
@@ -1,1284 +1,1284 @@
 %%MatrixMarket matrix coordinate integer general
 130 130 1282
-5 2 1
-5 3 1
-5 6 1
-5 7 1
-5 9 1
-5 18 1
-5 29 1
-5 30 1
-5 34 1
-5 40 1
-5 45 1
-5 49 1
-5 54 1
-5 55 1
-5 60 1
-5 64 1
-5 65 1
-5 70 1
-5 74 1
-5 79 1
-5 84 1
-5 85 1
-5 90 1
-5 99 1
-5 110 1
-5 114 1
-5 115 1
-5 124 1
-5 130 1
-6 2 1
-6 3 1
-6 6 1
-6 7 1
-6 9 1
-6 18 1
-6 30 1
-6 40 1
-6 45 1
-6 55 1
-6 60 1
-6 65 1
-6 70 1
-6 85 1
-6 90 1
-6 110 1
-6 115 1
-6 130 1
-7 2 1
-7 3 1
-7 6 1
-7 7 1
-7 9 1
-7 13 1
-7 18 1
-13 2 1
-13 3 1
-13 6 1
-13 7 1
-13 9 1
-13 13 1
-13 18 1
-17 2 1
-17 3 1
-17 6 1
-17 7 1
-17 9 1
-17 14 1
-17 18 1
-18 2 1
-18 3 1
-18 6 1
-18 7 1
-18 9 1
-18 13 1
-18 14 1
-18 18 1
-18 19 1
-18 28 1
-18 40 1
-18 42 1
-18 55 1
-18 56 1
-18 60 1
-19 2 1
-19 3 1
-19 6 1
-19 7 1
-19 9 1
-19 13 1
-19 14 1
-19 18 1
-19 19 1
-25 30 1
-25 40 1
-25 45 1
-25 55 1
-25 60 1
-25 65 1
-25 70 1
-25 85 1
-25 90 1
-25 110 1
-25 115 1
-25 130 1
-30 6 1
-30 18 1
-30 30 1
-35 6 1
-35 18 1
-44 18 1
-51 2 1
-51 18 1
-55 6 1
-55 18 1
-55 55 1
-60 6 1
-60 18 1
-60 60 1
-61 2 1
-61 18 1
-63 3 1
-63 18 1
-65 6 1
-65 18 1
-65 65 1
-68 3 1
-68 18 1
-71 2 1
-71 18 1
-76 2 1
-76 18 1
-79 18 1
-79 79 1
-80 6 1
-80 18 1
-85 6 1
-85 18 1
-85 85 1
+3 1 1
+3 2 1
+3 3 1
+3 4 1
+3 5 1
+3 6 1
+3 7 1
+3 8 1
+3 9 1
+3 10 1
+3 11 1
+3 15 1
+3 16 1
+3 17 1
+3 18 1
+3 20 1
+3 27 1
+3 28 1
+3 32 1
+3 33 1
+3 37 1
+3 38 1
+3 42 1
+3 43 1
+3 47 1
+3 48 1
+3 52 1
+3 53 1
+3 57 1
+3 58 1
+3 62 1
+3 63 1
+3 67 1
+3 68 1
+3 72 1
+3 73 1
+3 77 1
+3 78 1
+3 82 1
+3 83 1
+3 87 1
+3 88 1
+3 92 1
+3 93 1
+3 97 1
+3 98 1
+3 102 1
+3 103 1
+3 107 1
+3 108 1
+3 112 1
+3 113 1
+3 117 1
+3 118 1
+3 122 1
+3 123 1
+3 127 1
+3 128 1
+8 1 1
+8 2 1
+8 3 1
+8 4 1
+8 5 1
+8 6 1
+8 7 1
+8 8 1
+8 9 1
+8 10 1
+8 11 1
+8 14 1
+8 18 1
+8 20 1
+22 22 1
+22 27 1
+22 32 1
+22 37 1
+22 42 1
+22 47 1
+22 52 1
+22 57 1
+22 62 1
+22 67 1
+22 72 1
+22 77 1
+22 82 1
+22 87 1
+22 92 1
+22 97 1
+22 102 1
+22 107 1
+22 112 1
+22 117 1
+22 122 1
+22 127 1
+32 2 1
+32 3 1
+32 18 1
+32 20 1
+32 32 1
+47 2 1
+47 3 1
+47 18 1
+47 20 1
+47 47 1
+57 2 1
+57 3 1
+57 18 1
+57 20 1
+57 57 1
+77 2 1
+77 3 1
+77 18 1
+77 20 1
+77 77 1
+83 3 1
+83 4 1
+83 18 1
+83 20 1
+83 83 1
 87 2 1
 87 3 1
 87 18 1
-105 6 1
-105 18 1
-110 6 1
-110 18 1
-110 110 1
-114 18 1
-114 114 1
-115 6 1
-115 18 1
-115 115 1
-116 2 1
-116 18 1
-119 18 1
-120 6 1
-120 18 1
-125 6 1
-125 18 1
-130 130 2
-130 18 2
-130 6 2
-118 118 2
-118 18 2
-118 3 2
+87 20 1
+87 87 1
+92 2 1
+92 3 1
+92 18 1
+92 20 1
+92 92 1
+103 3 1
+103 4 1
+103 18 1
+103 20 1
+103 103 1
+112 2 1
+112 3 1
+112 18 1
+112 20 1
+112 112 1
+113 3 1
+113 4 1
+113 18 1
+113 20 1
+113 113 1
+117 2 1
+117 3 1
+117 18 1
+117 20 1
+117 117 1
+118 3 1
+118 4 1
+118 18 1
+118 20 1
+118 118 1
+123 3 1
+123 4 1
+123 18 1
+123 20 1
+123 123 1
+127 2 1
+127 3 1
+127 18 1
+127 20 1
+127 127 1
+128 128 2
+128 20 2
+128 18 2
+128 4 2
+128 3 2
+126 126 2
+126 20 2
+126 18 2
+126 2 2
+126 1 2
+116 116 2
+116 20 2
+116 18 2
+116 2 2
+116 1 2
+111 111 2
+111 20 2
 111 18 2
 111 2 2
-108 108 2
-108 18 2
-108 3 2
-106 106 2
-106 18 2
-106 2 2
-100 18 2
-100 6 2
-99 99 2
-99 18 2
-95 18 2
-95 6 2
-93 93 2
-93 18 2
-93 3 2
-92 92 2
-92 18 2
-92 3 2
-92 2 2
+111 1 2
 91 91 2
+91 20 2
 91 18 2
 91 2 2
-90 90 2
-90 18 2
-90 6 2
-89 18 2
-88 18 2
-88 3 2
+91 1 2
+86 86 2
+86 20 2
 86 18 2
 86 2 2
-83 83 2
-83 18 2
-83 3 2
-82 18 2
-82 3 2
-82 2 2
+86 1 2
+76 76 2
+76 20 2
+76 18 2
+76 2 2
+76 1 2
 73 73 2
+73 20 2
 73 18 2
+73 4 2
 73 3 2
-72 72 2
-72 18 2
-72 3 2
-72 2 2
-70 70 2
-70 18 2
-70 6 2
-62 18 2
-62 3 2
-62 2 2
 56 56 2
+56 20 2
 56 18 2
 56 2 2
-52 52 2
-52 18 2
-52 3 2
-52 2 2
-50 18 2
-50 6 2
+56 1 2
+51 51 2
+51 20 2
+51 18 2
+51 2 2
+51 1 2
+46 46 2
+46 20 2
 46 18 2
 46 2 2
-45 45 2
-45 18 2
-45 6 2
-43 18 2
-43 3 2
-42 42 2
-42 18 2
-42 3 2
-42 2 2
-41 18 2
-41 2 2
-40 40 2
-40 18 2
-40 6 2
-39 18 2
-23 128 2
-23 118 2
-23 113 2
-23 108 2
-23 93 2
-23 83 2
-23 78 2
-23 73 2
-23 53 2
-23 48 2
-23 28 2
-20 130 2
-20 128 2
-20 127 2
-20 124 2
-20 121 2
-20 118 2
-20 117 2
-20 115 2
-20 114 2
-20 113 2
-20 110 2
-20 108 2
-20 106 2
-20 101 2
-20 99 2
-20 97 2
-20 96 2
-20 93 2
-20 92 2
-20 91 2
-20 90 2
-20 85 2
-20 84 2
-20 83 2
-20 81 2
-20 79 2
-20 78 2
-20 77 2
-20 74 2
-20 73 2
-20 72 2
-20 70 2
-20 67 2
-20 65 2
-20 64 2
-20 60 2
-20 56 2
-20 55 2
-20 54 2
-20 53 2
-20 52 2
-20 49 2
-20 48 2
-20 45 2
-20 42 2
-20 40 2
-20 37 2
-20 36 2
-20 34 2
-20 32 2
-20 31 2
-20 30 2
-20 29 2
-20 28 2
-20 26 2
-20 18 2
-20 14 2
-20 13 2
-20 9 2
-20 7 2
-20 6 2
-20 3 2
-20 2 2
-12 18 2
-12 14 2
-12 13 2
-12 9 2
-12 7 2
-12 6 2
-12 3 2
-12 2 2
-17 20 3
-17 17 3
-17 11 3
-17 10 3
-17 8 3
-17 5 3
-17 4 3
-17 1 3
-19 20 3
-19 12 3
-19 11 3
-19 10 3
-19 8 3
-19 5 3
-19 4 3
-19 1 3
-20 129 3
-20 126 3
-20 125 3
-20 123 3
-20 122 3
-20 120 3
-20 119 3
-20 116 3
-20 112 3
-20 111 3
-20 109 3
-20 107 3
-20 105 3
-20 104 3
-20 103 3
-20 102 3
-20 100 3
-20 98 3
-20 95 3
-20 94 3
-20 89 3
-20 88 3
-20 87 3
-20 86 3
-20 82 3
-20 80 3
-20 76 3
-20 75 3
-20 71 3
-20 69 3
-20 68 3
-20 66 3
-20 63 3
-20 62 3
-20 61 3
-20 59 3
-20 58 3
-20 57 3
-20 51 3
-20 50 3
-20 47 3
-20 46 3
-20 44 3
-20 43 3
-20 41 3
-20 39 3
-20 38 3
-20 35 3
-20 33 3
-20 27 3
-20 20 3
-20 17 3
-20 16 3
-20 15 3
-20 12 3
-20 11 3
-20 10 3
-20 8 3
-20 5 3
-20 4 3
-20 1 3
-23 123 3
-23 103 3
-23 98 3
-23 88 3
-23 68 3
-23 63 3
-23 58 3
-23 43 3
-23 38 3
-23 33 3
-23 23 3
-43 43 3
-43 20 3
-43 4 3
-50 50 3
-50 20 3
-50 5 3
-51 51 3
-51 20 3
-51 1 3
-52 20 3
-56 20 3
-56 1 3
-60 20 3
+46 1 2
+36 36 2
+36 20 2
+36 18 2
+36 2 2
+36 1 2
+33 33 2
+33 20 2
+33 18 2
+33 4 2
+33 3 2
+26 26 2
+26 20 2
+26 18 2
+26 2 2
+26 1 2
+16 16 2
+15 20 2
+15 18 2
+15 15 2
+15 14 2
+15 13 2
+15 12 2
+15 11 2
+15 10 2
+15 9 2
+15 8 2
+15 7 2
+15 6 2
+15 5 2
+15 4 2
+15 3 2
+15 2 2
+15 1 2
+11 75 2
+11 73 2
+11 68 2
+11 66 2
+11 63 2
+11 61 2
+11 56 2
+11 55 2
+11 51 2
+11 48 2
+11 44 2
+11 43 2
+11 36 2
+11 34 2
+11 33 2
+11 31 2
+11 29 2
+11 28 2
+11 26 2
+11 20 2
+11 18 2
+11 17 2
+11 14 2
+11 13 2
+11 12 2
+11 11 2
+11 10 2
+11 8 2
+11 7 2
+11 6 2
+11 5 2
+11 4 2
+11 3 2
+11 2 2
+11 1 2
+1 126 2
+1 121 2
+1 116 2
+1 111 2
+1 106 2
+1 101 2
+1 96 2
+1 91 2
+1 86 2
+1 81 2
+1 76 2
+1 71 2
+1 66 2
+1 61 2
+1 56 2
+1 51 2
+1 46 2
+1 41 2
+1 36 2
+1 31 2
+1 26 2
+1 20 2
+1 18 2
+1 17 2
+1 16 2
+1 15 2
+1 11 2
+1 10 2
+1 9 2
+1 8 2
+1 7 2
+1 6 2
+1 5 2
+1 4 2
+1 3 2
+1 2 2
+1 1 2
+13 1 3
+12 1 3
+5 1 3
+4 1 3
+13 2 3
+12 2 3
+5 2 3
+4 2 3
+129 5 3
+125 5 3
+120 5 3
+115 5 3
+110 5 3
+109 5 3
+105 5 3
+99 5 3
+95 5 3
+94 5 3
+85 5 3
+84 5 3
+80 5 3
+79 5 3
 60 5 3
-61 61 3
-61 20 3
-61 1 3
-62 62 3
-62 20 3
+54 5 3
+45 5 3
+44 5 3
+39 5 3
+35 5 3
+13 5 3
+12 5 3
+5 5 3
+4 5 3
+13 9 3
+12 9 3
+5 9 3
+4 9 3
+12 11 3
+5 11 3
+4 11 3
+13 12 3
+12 12 3
+13 13 3
+12 13 3
+12 14 3
+5 15 3
+4 15 3
+129 18 3
+125 18 3
+120 18 3
+115 18 3
+110 18 3
+109 18 3
+105 18 3
+99 18 3
+98 18 3
+95 18 3
+94 18 3
+93 18 3
+85 18 3
+84 18 3
+80 18 3
+79 18 3
+68 18 3
+63 18 3
+60 18 3
+54 18 3
+45 18 3
+44 18 3
+39 18 3
+35 18 3
+28 18 3
+13 18 3
+12 18 3
+5 18 3
+4 18 3
+24 24 3
+28 28 3
+23 28 3
+4 28 3
+24 29 3
+5 29 3
+4 29 3
+23 38 3
+4 38 3
+39 39 3
+24 39 3
+5 39 3
+4 39 3
+5 40 3
+23 43 3
+4 43 3
+44 44 3
+24 44 3
+5 44 3
+4 44 3
+45 45 3
+5 45 3
+5 50 3
+5 55 3
+24 59 3
+5 59 3
+4 59 3
+60 60 3
+5 60 3
 63 63 3
-63 20 3
-63 4 3
-65 20 3
-65 5 3
-68 68 3
-68 20 3
-68 4 3
-70 20 3
-70 5 3
-71 71 3
-71 20 3
-71 1 3
-72 20 3
-73 20 3
-73 4 3
-76 76 3
-76 20 3
-76 1 3
-79 20 3
-79 5 3
-79 4 3
-80 80 3
-80 20 3
-80 5 3
-82 82 3
-82 20 3
-83 20 3
-83 4 3
-86 86 3
-86 20 3
-86 1 3
-87 87 3
-87 20 3
-88 88 3
-88 20 3
-88 4 3
-90 20 3
-90 5 3
-91 20 3
-91 1 3
-93 20 3
-93 4 3
-95 95 3
-95 20 3
-95 5 3
-111 111 3
-111 20 3
-111 1 3
-116 116 3
-116 20 3
-116 1 3
-119 119 3
-119 20 3
-119 5 3
-119 4 3
-130 5 4
-130 20 4
-125 5 4
-125 20 4
-125 125 4
-120 5 4
-120 20 4
-120 120 4
-118 4 4
-118 20 4
-115 5 4
-115 20 4
-114 4 4
-114 5 4
-114 20 4
-110 5 4
-110 20 4
-108 4 4
-108 20 4
-106 1 4
-106 20 4
-105 5 4
-105 20 4
+23 63 3
+4 63 3
+23 73 3
+4 73 3
+23 78 3
+4 78 3
+79 79 3
+24 79 3
+5 79 3
+4 79 3
+23 88 3
+4 88 3
+24 89 3
+5 89 3
+4 89 3
+93 93 3
+23 93 3
+4 93 3
+5 100 3
+23 103 3
+4 103 3
+24 104 3
+5 104 3
+4 104 3
+109 109 3
+24 109 3
+5 109 3
+4 109 3
+110 110 3
+5 110 3
+24 119 3
+5 119 3
+4 119 3
+120 120 3
+5 120 3
+23 123 3
+4 123 3
+24 124 3
+5 124 3
+4 124 3
+125 125 3
+5 125 3
+129 129 3
+24 129 3
+5 129 3
+4 129 3
+5 130 3
+4 128 4
+23 128 4
+4 118 4
+23 118 4
+5 115 4
+115 115 4
+4 114 4
+5 114 4
+24 114 4
+4 113 4
+23 113 4
+4 108 4
+23 108 4
+5 105 4
 105 105 4
-100 5 4
-100 20 4
-100 100 4
-99 4 4
-99 5 4
-99 20 4
-92 20 4
-89 4 4
-89 5 4
-89 20 4
-89 89 4
-85 5 4
-85 20 4
-55 5 4
-55 20 4
-46 1 4
-46 20 4
-46 46 4
-45 5 4
-45 20 4
-44 4 4
-44 5 4
-44 20 4
-44 44 4
-42 20 4
-41 1 4
-41 20 4
-41 41 4
-40 5 4
-40 20 4
-39 4 4
-39 5 4
-39 20 4
-39 39 4
-35 5 4
-35 20 4
+4 99 4
+5 99 4
+24 99 4
+99 99 4
+4 98 4
+23 98 4
+98 98 4
+5 95 4
+95 95 4
+4 94 4
+5 94 4
+24 94 4
+94 94 4
+5 90 4
+5 85 4
+85 85 4
+4 84 4
+5 84 4
+24 84 4
+84 84 4
+4 83 4
+23 83 4
+5 80 4
+80 80 4
+5 75 4
+4 74 4
+5 74 4
+24 74 4
+5 70 4
+4 69 4
+5 69 4
+24 69 4
+4 68 4
+23 68 4
+68 68 4
+5 65 4
+4 64 4
+5 64 4
+24 64 4
+4 58 4
+23 58 4
+4 54 4
+5 54 4
+24 54 4
+54 54 4
+4 53 4
+23 53 4
+4 49 4
+5 49 4
+24 49 4
+4 48 4
+23 48 4
+5 35 4
 35 35 4
-30 5 4
-30 20 4
-25 25 4
-25 35 4
-25 50 4
-25 75 4
-25 80 4
-25 95 4
-25 100 4
-25 105 4
-25 120 4
-25 125 4
-18 1 4
-18 4 4
-18 5 4
-18 8 4
-18 10 4
-18 11 4
-18 12 4
-18 15 4
-18 16 4
-18 17 4
-18 20 4
-18 33 4
-18 35 4
-18 41 4
-18 46 4
-18 58 4
-18 63 4
-18 68 4
-13 1 4
-13 4 4
-13 5 4
-13 8 4
-13 12 4
+4 34 4
+5 34 4
+24 34 4
+4 33 4
+23 33 4
+5 30 4
+23 23 4
+4 20 4
+5 20 4
+12 20 4
 13 20 4
-12 1 4
-12 4 4
-12 5 4
-12 8 4
+28 20 4
+35 20 4
+39 20 4
+44 20 4
+45 20 4
+54 20 4
+60 20 4
+63 20 4
+68 20 4
+79 20 4
+80 20 4
+84 20 4
+85 20 4
+93 20 4
+94 20 4
+95 20 4
+98 20 4
+99 20 4
+105 20 4
+109 20 4
+110 20 4
+115 20 4
+120 20 4
+125 20 4
+129 20 4
+4 17 4
+5 17 4
+4 16 4
+5 16 4
+4 10 4
+5 10 4
 12 10 4
-12 11 4
-12 12 4
-12 20 4
-7 1 4
-7 4 4
-7 5 4
-7 8 4
-7 10 4
-7 11 4
-7 12 4
-7 20 4
-6 1 4
-6 4 4
-6 5 4
-6 8 4
-6 10 4
-6 11 4
-6 15 4
-6 16 4
-6 17 4
-6 20 4
-6 35 4
-6 50 4
-6 75 4
-6 80 4
-6 95 4
-6 100 4
-6 105 4
-6 120 4
-6 125 4
-5 1 4
-5 4 4
-5 5 4
+4 8 4
 5 8 4
-5 10 4
-5 11 4
-5 15 4
-5 16 4
-5 17 4
-5 20 4
-5 35 4
-5 39 4
-5 44 4
-5 50 4
-5 59 4
-5 69 4
-5 75 4
-5 80 4
-5 89 4
-5 94 4
-5 95 4
-5 100 4
-5 104 4
-5 105 4
-5 109 4
-5 119 4
-5 120 4
-5 125 4
-5 129 4
-3 2 5
-4 2 5
-9 2 5
-14 2 5
-97 2 5
-102 2 5
-107 2 5
-112 2 5
-117 2 5
-3 4 5
-4 4 5
-9 4 5
-14 4 5
-38 4 5
+12 8 4
+13 8 4
+4 7 4
+5 7 4
+12 7 4
+13 7 4
+4 6 4
+5 6 4
+12 6 4
+13 6 4
+35 6 4
+45 6 4
+60 6 4
+80 6 4
+85 6 4
+95 6 4
+105 6 4
+110 6 4
+115 6 4
+120 6 4
+125 6 4
+4 4 4
+5 4 4
+12 4 4
+13 4 4
+28 4 4
+39 4 4
+44 4 4
+54 4 4
+63 4 4
+68 4 4
+79 4 4
+84 4 4
+93 4 4
+94 4 4
+98 4 4
+99 4 4
+109 4 4
+129 4 4
+4 3 4
+5 3 4
+12 3 4
+13 3 4
+28 3 4
+63 3 4
+68 3 4
+93 3 4
+98 3 4
+7 2 5
+7 4 5
+7 6 5
+7 7 5
+7 10 5
+7 12 5
+7 20 5
+20 2 5
+20 4 5
+20 6 5
+20 7 5
+20 10 5
+20 12 5
+20 14 5
+20 15 5
+20 16 5
+20 20 5
+20 27 5
+20 28 5
+20 29 5
+20 30 5
+20 32 5
+20 33 5
+20 36 5
+20 39 5
+20 41 5
+20 42 5
+20 44 5
+20 45 5
+20 50 5
+20 51 5
+20 53 5
+20 54 5
+20 55 5
+20 57 5
+20 58 5
+20 59 5
+20 60 5
+20 62 5
+20 64 5
+20 71 5
+20 72 5
+20 75 5
+20 77 5
+20 78 5
+20 79 5
+20 80 5
+20 81 5
+20 85 5
+20 86 5
+20 87 5
+20 91 5
+20 92 5
+20 94 5
+20 102 5
+20 103 5
+20 104 5
+20 105 5
+20 106 5
+20 111 5
+20 114 5
+20 115 5
+20 118 5
+20 121 5
+20 123 5
+20 127 5
+20 128 5
+20 130 5
+21 21 5
+21 36 5
+21 41 5
+21 51 5
+21 71 5
+21 81 5
+21 86 5
+21 91 5
+21 106 5
+21 111 5
+21 121 5
+27 2 5
+27 20 5
+27 27 5
+29 4 5
+29 20 5
+29 29 5
+31 2 5
+31 20 5
+40 6 5
+40 20 5
+42 2 5
+42 20 5
+42 42 5
+43 4 5
+43 20 5
 49 4 5
+49 20 5
+50 6 5
+50 20 5
+50 50 5
+52 2 5
+52 20 5
 53 4 5
-54 4 5
+53 20 5
+53 53 5
 58 4 5
+58 20 5
+58 58 5
 59 4 5
+59 20 5
+59 59 5
 64 4 5
-69 4 5
-74 4 5
+64 20 5
+64 64 5
+65 6 5
+65 20 5
+66 2 5
+66 20 5
+70 6 5
+70 20 5
+72 2 5
+72 20 5
+72 72 5
 78 4 5
-84 4 5
-94 4 5
-98 4 5
-103 4 5
+78 20 5
+78 78 5
+88 4 5
+88 20 5
+96 2 5
+96 20 5
+97 2 5
+97 20 5
+100 6 5
+100 20 5
+101 2 5
+101 20 5
+102 2 5
+102 20 5
+102 102 5
 104 4 5
-109 4 5
-113 4 5
-123 4 5
+104 20 5
+104 104 5
+106 2 5
+106 20 5
+106 106 5
+107 2 5
+107 20 5
+108 4 5
+108 20 5
+114 4 5
+114 20 5
+114 114 5
+119 4 5
+119 20 5
+121 2 5
+121 20 5
+121 121 5
+122 2 5
+122 20 5
 124 4 5
-128 4 5
-129 4 5
-3 7 5
-4 7 5
-9 7 5
-14 7 5
-3 9 5
-4 9 5
-9 9 5
-14 9 5
-3 10 5
-4 10 5
-14 10 5
-9 12 5
-14 12 5
-3 16 5
-4 16 5
-3 18 5
-4 18 5
-9 18 5
-14 18 5
-38 18 5
-49 18 5
-53 18 5
-54 18 5
-58 18 5
-59 18 5
-64 18 5
-69 18 5
-74 18 5
-78 18 5
-84 18 5
-94 18 5
-97 18 5
-98 18 5
-102 18 5
-103 18 5
-104 18 5
-107 18 5
-109 18 5
-112 18 5
-113 18 5
-117 18 5
-123 18 5
-124 18 5
-128 18 5
-129 18 5
-22 22 5
-24 24 5
-3 27 5
-22 27 5
-3 28 5
-4 28 5
-3 33 5
-4 33 5
-4 34 5
-24 34 5
-3 37 5
-22 37 5
-3 38 5
-4 38 5
-38 38 5
-4 39 5
-24 39 5
-3 43 5
-4 43 5
-3 47 5
-22 47 5
-3 48 5
-4 48 5
-3 53 5
-4 53 5
-53 53 5
-3 57 5
-22 57 5
-4 64 5
-24 64 5
-64 64 5
-3 67 5
-22 67 5
-3 68 5
-4 68 5
-3 72 5
-22 72 5
-3 73 5
-4 73 5
-4 74 5
-24 74 5
-74 74 5
-3 78 5
-4 78 5
-78 78 5
-4 79 5
-24 79 5
-4 84 5
-24 84 5
-84 84 5
-3 93 5
-4 93 5
-4 99 5
-24 99 5
-3 107 5
-22 107 5
-107 107 5
-3 108 5
-4 108 5
-3 112 5
-22 112 5
-112 112 5
-3 113 5
-4 113 5
-113 113 5
-3 117 5
-22 117 5
-117 117 5
-3 122 5
-22 122 5
-4 124 5
-24 124 5
-124 124 5
-3 128 5
-4 128 5
-128 128 5
-4 129 5
-24 129 5
-129 129 5
-22 127 6
-3 127 6
-123 123 6
-4 123 6
-3 123 6
-24 119 6
-4 119 6
-4 118 6
-3 118 6
-24 114 6
-4 114 6
-109 109 6
-24 109 6
-4 109 6
-104 104 6
-24 104 6
-4 104 6
-103 103 6
-4 103 6
-3 103 6
-102 102 6
-22 102 6
-3 102 6
-98 98 6
-4 98 6
-3 98 6
-97 97 6
-22 97 6
-3 97 6
-94 94 6
-24 94 6
-4 94 6
-22 92 6
-3 92 6
-24 89 6
-4 89 6
-4 88 6
-3 88 6
-22 87 6
-3 87 6
-4 83 6
-3 83 6
-22 82 6
-3 82 6
-22 77 6
-3 77 6
-69 69 6
-24 69 6
-4 69 6
-4 63 6
-3 63 6
-22 62 6
-3 62 6
-59 59 6
-24 59 6
-4 59 6
-58 58 6
-4 58 6
-3 58 6
-54 54 6
-24 54 6
-4 54 6
-22 52 6
-3 52 6
-49 49 6
-24 49 6
-4 49 6
-24 44 6
-4 44 6
-22 42 6
-3 42 6
-22 32 6
-3 32 6
-24 29 6
-4 29 6
-129 20 6
-128 20 6
-124 20 6
-123 20 6
-117 20 6
-113 20 6
-112 20 6
-109 20 6
-107 20 6
-104 20 6
-103 20 6
-102 20 6
-98 20 6
-97 20 6
-94 20 6
-84 20 6
-78 20 6
+124 20 5
+130 130 6
+130 20 6
+130 6 6
+90 20 6
+90 6 6
+89 20 6
+89 4 6
+82 20 6
+82 2 6
+81 81 6
+81 20 6
+81 2 6
+75 75 6
+75 20 6
+75 6 6
 74 20 6
+74 4 6
+71 71 6
+71 20 6
+71 2 6
 69 20 6
-64 20 6
-59 20 6
-58 20 6
-54 20 6
-53 20 6
-49 20 6
+69 4 6
+67 20 6
+67 2 6
+62 62 6
+62 20 6
+62 2 6
+61 20 6
+61 2 6
+55 55 6
+55 20 6
+55 6 6
+48 20 6
+48 4 6
+41 41 6
+41 20 6
+41 2 6
 38 20 6
+38 4 6
+37 20 6
+37 2 6
+34 20 6
+34 4 6
+30 30 6
+30 20 6
+30 6 6
+25 130 6
+25 115 6
+25 105 6
+25 85 6
+25 80 6
+25 75 6
+25 60 6
+25 55 6
+25 50 6
+25 45 6
+25 30 6
+19 20 6
+19 19 6
+19 14 6
+19 12 6
+19 10 6
+19 7 6
+19 6 6
+19 4 6
+19 2 6
+18 60 6
+18 58 6
+18 55 6
+18 42 6
+18 41 6
+18 33 6
+18 28 6
+18 20 6
+18 19 6
+18 16 6
+18 15 6
+18 14 6
+18 12 6
+18 10 6
+18 7 6
+18 6 6
+18 4 6
+18 2 6
+17 20 6
+17 14 6
+17 10 6
+17 7 6
+17 6 6
+17 4 6
+17 2 6
 14 20 6
-9 20 6
-4 20 6
-3 20 6
-4 17 6
-3 17 6
-4 15 6
-3 15 6
 14 14 6
-14 13 6
-9 13 6
-14 11 6
-4 11 6
-3 11 6
-14 8 6
-9 8 6
-4 8 6
-3 8 6
+14 12 6
+14 10 6
+14 7 6
 14 6 6
+14 4 6
+14 2 6
+10 20 6
+10 14 6
+10 10 6
+10 7 6
+10 6 6
+10 4 6
+10 2 6
+9 20 6
+9 12 6
+9 7 6
 9 6 6
-4 6 6
-3 6 6
-129 5 6
-124 5 6
-109 5 6
-104 5 6
-94 5 6
-84 5 6
-74 5 6
-69 5 6
-64 5 6
-59 5 6
-54 5 6
-49 5 6
-14 5 6
-9 5 6
-4 5 6
-3 5 6
-128 3 6
-123 3 6
-117 3 6
-113 3 6
-112 3 6
-107 3 6
-103 3 6
-102 3 6
-98 3 6
-97 3 6
-78 3 6
-58 3 6
-53 3 6
-38 3 6
-14 3 6
-9 3 6
-4 3 6
-3 3 6
-14 1 6
-9 1 6
-4 1 6
-3 1 6
-127 2 7
-126 2 7
-122 2 7
-121 2 7
-101 2 7
-96 2 7
-81 2 7
-77 2 7
-67 2 7
-66 2 7
-57 2 7
-47 2 7
-37 2 7
-36 2 7
-32 2 7
-31 2 7
-27 2 7
-26 2 7
-15 2 7
-11 2 7
-10 2 7
-8 2 7
-2 2 7
-1 2 7
-48 4 7
-34 4 7
-33 4 7
-29 4 7
-28 4 7
-15 4 7
-11 4 7
-10 4 7
-8 4 7
-2 4 7
-1 4 7
-75 5 7
-34 5 7
-29 5 7
-15 5 7
-11 5 7
-10 5 7
-8 5 7
-2 5 7
-1 5 7
-15 7 7
-11 7 7
-10 7 7
-8 7 7
-2 7 7
-1 7 7
-15 9 7
-8 9 7
-2 9 7
-1 9 7
-15 10 7
-11 10 7
-10 10 7
-8 10 7
-2 10 7
-1 10 7
-15 15 7
-2 15 7
-1 15 7
-16 16 7
-2 16 7
-1 16 7
-127 20 7
-126 20 7
-122 20 7
-121 20 7
-101 20 7
-96 20 7
-81 20 7
-77 20 7
-75 20 7
-67 20 7
-66 20 7
-57 20 7
-48 20 7
-47 20 7
-37 20 7
-36 20 7
-34 20 7
-33 20 7
-32 20 7
-31 20 7
-29 20 7
-28 20 7
-27 20 7
-26 20 7
-15 20 7
-11 20 7
-10 20 7
-8 20 7
-2 20 7
-1 20 7
-26 26 7
-21 26 7
-11 26 7
-2 26 7
-1 26 7
-29 29 7
-11 29 7
-33 33 7
-11 33 7
-34 34 7
-11 34 7
-36 36 7
-21 36 7
-11 36 7
-2 36 7
-1 36 7
-2 42 7
-11 43 7
-11 44 7
-47 47 7
-2 47 7
-48 48 7
-11 48 7
-21 51 7
-11 51 7
-2 51 7
-1 51 7
-21 56 7
-11 56 7
+9 4 6
+9 2 6
+6 130 6
+6 115 6
+6 105 6
+6 85 6
+6 80 6
+6 75 6
+6 60 6
+6 55 6
+6 50 6
+6 45 6
+6 30 6
+6 20 6
+6 16 6
+6 15 6
+6 10 6
+6 7 6
+6 6 6
+6 4 6
+6 2 6
+2 127 6
+2 121 6
+2 111 6
+2 106 6
+2 102 6
+2 92 6
+2 91 6
+2 87 6
+2 86 6
+2 81 6
+2 77 6
+2 72 6
+2 71 6
+2 62 6
+2 57 6
+2 51 6
+2 42 6
+2 41 6
+2 36 6
+2 32 6
+2 27 6
+2 20 6
+2 16 6
+2 15 6
+2 10 6
+2 7 6
+2 6 6
+2 4 6
+2 2 6
+2 126 7
+2 122 7
+2 117 7
+2 116 7
+2 112 7
+2 107 7
+2 101 7
+2 97 7
+2 96 7
+2 82 7
+2 76 7
+2 67 7
+2 66 7
+2 61 7
 2 56 7
-1 56 7
-57 57 7
-2 57 7
-2 62 7
+2 52 7
+2 47 7
+2 46 7
+2 37 7
+2 31 7
+2 26 7
+2 18 7
+2 17 7
+2 11 7
+2 9 7
+2 8 7
+2 5 7
+2 3 7
+2 1 7
+6 125 7
+6 120 7
+6 110 7
+6 100 7
+6 95 7
+6 90 7
+6 70 7
+6 65 7
+6 40 7
+6 35 7
+6 18 7
+6 17 7
+6 11 7
+6 9 7
+6 8 7
+6 5 7
+6 3 7
+6 1 7
+7 18 7
+7 13 7
+7 11 7
+7 9 7
+7 8 7
+7 5 7
+7 3 7
+7 1 7
+9 18 7
+9 13 7
+9 9 7
+9 8 7
+9 5 7
+9 3 7
+9 1 7
+10 18 7
+10 8 7
+10 5 7
+10 3 7
+10 1 7
+14 18 7
+14 13 7
+14 11 7
+14 9 7
+14 8 7
+14 5 7
+14 3 7
+14 1 7
+18 68 7
+18 63 7
+18 56 7
+18 46 7
+18 40 7
+18 35 7
+18 18 7
+18 17 7
+18 13 7
+18 11 7
+18 9 7
+18 8 7
+18 5 7
+18 3 7
+18 1 7
+25 125 7
+25 120 7
+25 110 7
+25 100 7
+25 95 7
+25 90 7
+25 70 7
+25 65 7
+25 40 7
+25 35 7
+25 25 7
+27 18 7
+27 3 7
+29 18 7
+29 5 7
+30 18 7
+30 5 7
+31 31 7
+31 18 7
+31 1 7
+37 37 7
+37 18 7
+37 3 7
+40 40 7
+40 18 7
+40 5 7
+41 18 7
+41 1 7
+42 18 7
+42 3 7
+50 18 7
+50 5 7
+52 52 7
+52 18 7
+52 3 7
+53 18 7
+53 3 7
+55 18 7
+55 5 7
+58 18 7
+58 3 7
+59 18 7
+59 5 7
+66 66 7
+66 18 7
+66 1 7
 67 67 7
-2 67 7
-21 71 7
-2 71 7
-1 71 7
-11 73 7
-21 76 7
-2 76 7
-1 76 7
-77 77 7
-2 77 7
-81 81 7
-21 81 7
-2 81 7
-1 81 7
-2 82 7
-21 86 7
-2 86 7
-1 86 7
-2 87 7
-2 92 7
-96 96 7
-21 96 7
-2 96 7
-1 96 7
-2 102 7
-21 111 7
-2 111 7
-1 111 7
-2 117 7
-2 127 8
-127 127 8
-1 126 8
-2 126 8
-21 126 8
-126 126 8
-2 122 8
+67 18 7
+67 3 7
+71 18 7
+71 1 7
+75 18 7
+75 5 7
+78 18 7
+78 3 7
+81 18 7
+81 1 7
+90 90 7
+90 18 7
+90 5 7
+97 97 7
+97 18 7
+97 3 7
+100 100 7
+100 18 7
+100 5 7
+121 18 7
+121 1 7
+130 18 7
+130 5 7
+124 5 8
+124 18 8
+124 124 8
+122 3 8
+122 18 8
 122 122 8
-1 121 8
-2 121 8
-21 121 8
-121 121 8
-1 116 8
-2 116 8
-21 116 8
-2 112 8
-2 107 8
-1 106 8
-2 106 8
-21 106 8
-1 101 8
-2 101 8
-21 101 8
+119 5 8
+119 18 8
+119 119 8
+114 5 8
+114 18 8
+108 3 8
+108 18 8
+108 108 8
+107 3 8
+107 18 8
+107 107 8
+106 1 8
+106 18 8
+104 5 8
+104 18 8
+102 3 8
+102 18 8
+101 1 8
+101 18 8
 101 101 8
-2 97 8
-1 91 8
-2 91 8
-21 91 8
-11 75 8
-75 75 8
-2 72 8
-11 68 8
-1 66 8
-2 66 8
-11 66 8
-21 66 8
-66 66 8
-11 63 8
-1 61 8
-2 61 8
-11 61 8
-21 61 8
-11 55 8
-2 52 8
-1 46 8
-2 46 8
-21 46 8
-1 41 8
-2 41 8
-21 41 8
-2 37 8
-37 37 8
-2 32 8
-32 32 8
-1 31 8
-2 31 8
-11 31 8
-21 31 8
-31 31 8
-11 28 8
-28 28 8
-2 27 8
-27 27 8
-21 21 8
-1 18 8
-2 18 8
-8 18 8
-10 18 8
-11 18 8
-15 18 8
-26 18 8
-27 18 8
-28 18 8
-29 18 8
-31 18 8
-32 18 8
-33 18 8
-34 18 8
-36 18 8
-37 18 8
-47 18 8
-48 18 8
-57 18 8
-66 18 8
-67 18 8
-75 18 8
-77 18 8
-81 18 8
+96 1 8
 96 18 8
-101 18 8
-121 18 8
-122 18 8
-126 18 8
-127 18 8
-1 17 8
-2 17 8
-11 17 8
-8 14 8
-10 14 8
-11 14 8
-15 14 8
-11 13 8
-15 13 8
-11 12 8
-15 12 8
-1 11 8
-2 11 8
-8 11 8
-11 11 8
-15 11 8
-1 8 8
-2 8 8
-8 8 8
-10 8 8
-11 8 8
-15 8 8
-1 6 8
-2 6 8
-8 6 8
-10 6 8
-11 6 8
-15 6 8
-75 6 8
-1 3 8
-2 3 8
-8 3 8
-10 3 8
-11 3 8
-15 3 8
-27 3 8
-28 3 8
-32 3 8
-33 3 8
-37 3 8
-47 3 8
+96 96 8
+89 5 8
+89 18 8
+89 89 8
+88 3 8
+88 18 8
+88 88 8
+82 3 8
+82 18 8
+82 82 8
+74 5 8
+74 18 8
+74 74 8
+72 3 8
+72 18 8
+70 5 8
+70 18 8
+70 70 8
+69 5 8
+69 18 8
+69 69 8
+65 5 8
+65 18 8
+65 65 8
+64 5 8
+64 18 8
+62 3 8
+62 18 8
+61 1 8
+61 18 8
+61 61 8
+49 5 8
+49 18 8
+49 49 8
 48 3 8
-57 3 8
-67 3 8
-77 3 8
-122 3 8
-127 3 8
-1 1 8
-2 1 8
-8 1 8
-10 1 8
-11 1 8
-15 1 8
-26 1 8
-31 1 8
-36 1 8
-66 1 8
-81 1 8
-96 1 8
-101 1 8
-121 1 8
-126 1 8
+48 18 8
+48 48 8
+43 3 8
+43 18 8
+43 43 8
+38 3 8
+38 18 8
+38 38 8
+34 5 8
+34 18 8
+34 34 8
+21 26 8
+21 31 8
+21 46 8
+21 56 8
+21 61 8
+21 66 8
+21 76 8
+21 96 8
+21 101 8
+21 116 8
+21 126 8
+20 1 8
+20 3 8
+20 5 8
+20 8 8
+20 9 8
+20 11 8
+20 13 8
+20 17 8
+20 18 8
+20 26 8
+20 31 8
+20 34 8
+20 35 8
+20 37 8
+20 38 8
+20 40 8
+20 43 8
+20 46 8
+20 47 8
+20 48 8
+20 49 8
+20 52 8
+20 56 8
+20 61 8
+20 63 8
+20 65 8
+20 66 8
+20 67 8
+20 68 8
+20 69 8
+20 70 8
+20 73 8
+20 74 8
+20 76 8
+20 82 8
+20 83 8
+20 84 8
+20 88 8
+20 89 8
+20 90 8
+20 93 8
+20 95 8
+20 96 8
+20 97 8
+20 98 8
+20 99 8
+20 100 8
+20 101 8
+20 107 8
+20 108 8
+20 109 8
+20 110 8
+20 112 8
+20 113 8
+20 116 8
+20 117 8
+20 119 8
+20 120 8
+20 122 8
+20 124 8
+20 125 8
+20 126 8
+20 129 8
+19 1 8
+19 3 8
+19 5 8
+19 8 8
+19 9 8
+19 11 8
+19 13 8
+19 18 8
+17 1 8
+17 3 8
+17 5 8
+17 8 8
+17 9 8
+17 11 8
+17 17 8
+17 18 8
diff --git a/tests/m300_arc130.mtx-P8 b/tests/m300_arc130.mtx-P8
index 1092af347c4ff9163a4b91e4fbd965b56ad37fe2..13e9cf98fa604932adcb08e9f08dda979b152603 100644
--- a/tests/m300_arc130.mtx-P8
+++ b/tests/m300_arc130.mtx-P8
@@ -1,1293 +1,1293 @@
 %%MatrixMarket distributed-matrix coordinate real general
 130 130 1282 8
 0
-158
-323
-486
-647
-805
-966
-1126
+164
+324
+476
+638
+798
+958
+1118
 1282
-5 2 -5.72528e-09
-5 3 -9.39363e-08
-5 6 -0.00104175
-5 7 -0.000113867
-5 9 0
-5 18 -0.00015244
-5 29 -0.128117
-5 30 0.0449037
-5 34 -0.205263
-5 40 0.0826973
-5 45 0.0749193
-5 49 -0.148631
-5 54 -0.0942582
-5 55 0.0397058
-5 60 0.0230579
-5 64 -0.0246476
-5 65 0.0114346
-5 70 0.00498403
-5 74 -0.00446512
-5 79 -0.0029784
-5 84 0
-5 85 0
-5 90 0
-5 99 0
-5 110 0
-5 114 0
-5 115 0
-5 124 0
-5 130 0
-6 2 -1.77376e-08
-6 3 -2.99542e-08
-6 6 1.00104
-6 7 -2.63737e-05
-6 9 0
-6 18 -4.57016e-05
-6 30 -0.0447633
-6 40 -0.0834993
-6 45 -0.0757129
-6 55 -0.038757
-6 60 -0.0226576
-6 65 -0.0115367
-6 70 -0.00513978
-6 85 -0.00140839
-6 90 0.0037198
-6 110 0
-6 115 0
-6 130 0
-7 2 5.6379e-08
-7 3 5.6379e-08
-7 6 5.63709e-08
-7 7 1.00116
-7 9 -1.2303e-06
-7 13 2.18493e-11
-7 18 -0.00242886
-13 2 -5.6379e-08
-13 3 -5.6379e-08
-13 6 -5.6379e-08
-13 7 -1.46354e-07
-13 9 1.2303e-06
-13 13 1
-13 18 0.00417594
-17 2 -5.6379e-08
-17 3 -5.6379e-08
-17 6 -5.63709e-08
-17 7 -0.00102241
-17 9 1.2303e-06
-17 14 1.34533e-07
-17 18 0.00407312
-18 2 1.43774e-07
-18 3 -1.14481e-06
-18 6 9.09531e-07
-18 7 4.04581e-06
-18 9 -2.20048e-09
-18 13 1.38886e-13
-18 14 4.66853e-10
-18 18 1.00002
-18 19 2.86321e-11
-18 28 0
-18 40 0
-18 42 6.81792e-06
-18 55 0
-18 56 0
-18 60 0
-19 2 0
-19 3 0
-19 6 0
-19 7 6.69125e-05
-19 9 0
-19 13 1.09247e-11
-19 14 6.72665e-08
-19 18 0.000822131
-19 19 1
-25 30 -48446.2
-25 40 -47682.8
-25 45 -47284.4
-25 55 -46455.6
-25 60 -46025.4
-25 65 -45585.4
-25 70 -45135.3
-25 85 -43728.7
-25 90 -43241.8
-25 110 -41210.5
-25 115 -40683
-25 130 -39056.4
-30 6 -5.38785e-14
-30 18 -1.28689e-16
-30 30 1.07003
-35 6 -1.06586e-13
-35 18 -1.68621e-16
-44 18 -5.60673e-16
-51 2 -1.74247e-14
-51 18 -4.34373e-17
-55 6 -1.09455e-13
-55 18 -2.48875e-16
-55 55 1.06392
-60 6 -7.44616e-14
-60 18 -2.32712e-16
-60 60 1.04777
-61 2 -6.78651e-15
-61 18 -5.10312e-17
-63 3 1.63597e-11
-63 18 -2.0466e-12
-65 6 -4.38977e-14
-65 18 -1.87716e-16
-65 65 1.03674
-68 3 1.0996e-13
-68 18 -1.88183e-14
-71 2 -1.63115e-15
-71 18 -4.29686e-17
-76 2 -6.682e-16
-76 18 -2.77218e-17
-79 18 -1.11357e-16
-79 79 1.02597
-80 6 -4.70965e-15
-80 18 -3.37527e-17
-85 6 -1.90612e-15
-85 18 -1.42592e-17
-85 85 1.02535
-87 2 1.55096e-18
-87 3 -1.95499e-16
-87 18 -3.03288e-17
-105 6 -2.1338e-17
-105 18 -1.63797e-19
-110 6 -5.50569e-18
-110 18 -4.22695e-20
-110 110 1.02516
-114 18 -2.6569e-20
-114 114 1.02516
-115 6 -1.29419e-18
-115 18 -9.93638e-21
-115 115 1.02516
-116 2 -1.38867e-20
-116 18 -8.54504e-22
-119 18 -5.47265e-21
-120 6 -2.77187e-19
-120 18 -2.12817e-21
-125 6 -5.41063e-20
-125 18 -4.15415e-22
-130 130 1.02516
-130 18 -7.39407e-23
-130 6 -9.63052e-21
-118 118 1.02516
-118 18 -1.06034e-20
-118 3 1.66265e-25
-111 18 -4.69178e-21
-111 2 -7.62484e-20
-108 108 1.02515
-108 18 -2.45815e-19
-108 3 1.17254e-22
-106 106 1.02516
-106 18 -2.32651e-20
-106 2 -3.78132e-19
-100 18 -5.77741e-19
-100 6 -7.53043e-17
-99 99 1.02514
-99 18 -1.71584e-18
-95 18 -1.85403e-18
-95 6 -2.42129e-16
-93 93 1.02476
-93 18 -1.33669e-17
-93 3 5.04551e-19
-92 92 1.02481
-92 18 -9.32422e-18
-92 3 -5.99676e-17
-92 2 1.32167e-19
-91 91 1.02547
-91 18 -1.51727e-18
-91 2 -2.49627e-17
-90 90 1.0252
-90 18 -5.4046e-18
-90 6 -7.1014e-16
-89 18 -1.70672e-17
-88 18 -4.26509e-17
-88 3 5.64556e-18
-86 18 -4.8409e-18
-86 2 -8.23275e-17
-83 83 1.02212
-83 18 -1.29694e-16
-83 3 5.4442e-17
-82 18 -9.24004e-17
-82 3 -5.99769e-16
-82 2 1.52358e-17
-73 73 1.01015
-73 18 -1.64547e-15
-73 3 5.00653e-15
-72 72 1.00709
-72 18 -9.24877e-16
-72 3 -6.36214e-15
-72 2 1.05988e-15
-70 70 1.03036
-70 18 -1.25924e-16
-70 6 -2.28268e-14
-62 18 -6.05744e-14
-62 3 -4.67691e-13
-62 2 1.98412e-13
-56 56 1.38522
-56 18 -4.77846e-17
-56 2 -1.15556e-14
-52 52 0.862585
-52 18 -6.18966e-10
-52 3 -5.07647e-09
-52 2 2.77975e-09
-50 18 -2.41995e-16
-50 6 -1.39097e-13
-46 18 -3.90387e-17
-46 2 -2.31468e-14
-45 45 1.10083
-45 18 -2.2267e-16
-45 6 -1.52538e-13
-43 18 -0.000996902
-43 3 0.0381683
-42 42 0.794851
-42 18 -5.19145e-07
-42 3 -4.5648e-06
-42 2 3.10792e-06
-41 18 -3.49479e-17
-41 2 -2.67359e-14
-40 40 1.10865
-40 18 -1.97671e-16
-40 6 -1.42018e-13
-39 18 -4.8124e-16
-23 128 0
-23 118 0
-23 113 0
-23 108 0
-23 93 0
-23 83 -34492.6
-23 78 -43888.4
-23 73 -49508
-23 53 -50364.7
-23 48 -50808.1
-23 28 -52460.6
-20 130 0
-20 128 0
-20 127 0
-20 124 0
-20 121 0
-20 118 0
-20 117 0
-20 115 0
-20 114 0
-20 113 0
-20 110 0
-20 108 0
-20 106 0
-20 101 0
-20 99 0
-20 97 0
-20 96 0
-20 93 0
-20 92 0
-20 91 0
-20 90 0
-20 85 0
-20 84 0
-20 83 0
-20 81 0
-20 79 0
-20 78 0
-20 77 0
-20 74 0
-20 73 0
-20 72 0
-20 70 0
-20 67 0
-20 65 0
-20 64 0
-20 60 0
-20 56 0
-20 55 0
-20 54 0
-20 53 -31.7106
-20 52 -68.5102
-20 49 0
-20 48 -25.3546
-20 45 0
-20 42 -62.6554
-20 40 0
-20 37 -43.2607
-20 36 0
-20 34 0
-20 32 -21.1696
-20 31 0
-20 30 0
-20 29 0
-20 28 0
-20 26 0
-20 18 -101.449
-20 14 -0.00445353
-20 13 -1.30285e-06
-20 9 0.020222
-20 7 -37.3426
-20 6 -8.35841
-20 3 10.5206
-20 2 -1.32126
-12 18 -0.0495021
-12 14 6.72665e-08
-12 13 4.36987e-10
-12 9 -2.4606e-05
-12 7 0.0220356
-12 6 1.12743e-06
-12 3 1.12758e-06
-12 2 1.12758e-06
-17 20 3.13539e-09
-17 17 1
-17 11 2.52205e-08
-17 10 -5.39396e-06
-17 8 5.52765e-05
-17 5 -5.63789e-08
-17 4 -5.6379e-08
-17 1 -5.6379e-08
-19 20 -1.15543e-10
-19 12 1.23862e-11
-19 11 2.52205e-08
-19 10 0
-19 8 7.26177e-06
-19 5 0
-19 4 0
-19 1 0
-20 129 0
-20 126 0
-20 125 0
-20 123 0
-20 122 0
-20 120 0
-20 119 0
-20 116 0
-20 112 0
-20 111 0
-20 109 0
-20 107 0
-20 105 0
-20 104 0
-20 103 0
-20 102 0
-20 100 0
-20 98 0
-20 95 0
-20 94 0
-20 89 0
-20 88 0
-20 87 0
-20 86 0
-20 82 0
-20 80 0
-20 76 0
-20 75 0
-20 71 0
-20 69 0
-20 68 -18.274
-20 66 0
-20 63 -25.5022
-20 62 -29.0175
-20 61 0
-20 59 0
-20 58 -31.3394
-20 57 -55.4209
-20 51 0
-20 50 0
-20 47 -71.8441
-20 46 0
-20 44 0
-20 43 -14.3108
-20 41 0
-20 39 0
-20 38 -3.64635
-20 35 0
-20 33 1.84408
-20 27 -5.77173
-20 20 0.999994
-20 17 -0.0177478
-20 16 -6.8096e-07
-20 15 -0.000139857
-20 12 -1.47715e-06
-20 11 -0.00240875
-20 10 -0.130576
-20 8 -0.44093
-20 5 -8.40741
-20 4 -29.7996
-20 1 0.0187834
-23 123 0
-23 103 0
-23 98 0
-23 88 -105156
-23 68 -48828.1
-23 63 -49443.7
-23 58 -49910.6
-23 43 -51241
-23 38 -51663.3
-23 33 -52074.4
-23 23 1
-43 43 0.97214
-43 20 2.73732e-10
-43 4 -0.0555268
-50 50 1.08333
-50 20 3.78102e-23
-50 5 2.40613e-14
-51 51 1.64291
-51 20 6.82554e-23
-51 1 5.64117e-16
-52 20 -3.69682e-17
-56 20 4.76634e-23
-56 1 3.63701e-16
-60 20 -4.80723e-24
-60 5 9.87515e-15
-61 61 1.21065
-61 20 2.36266e-23
-61 1 2.01005e-16
-62 62 0.956854
-62 20 -1.18989e-20
-63 63 0.979846
-63 20 -1.974e-19
-63 4 -3.6762e-11
-65 20 -2.42816e-23
-65 5 4.3499e-15
-68 68 0.997323
-68 20 -3.71797e-21
-68 4 -2.87613e-13
-70 20 -2.99607e-23
-70 5 1.43718e-15
-71 71 1.05976
-71 20 -1.03771e-23
-71 1 3.14761e-17
-72 20 -3.56644e-22
-73 20 -5.41364e-22
-73 4 -1.94082e-14
-76 76 1.03775
-76 20 -1.23817e-23
-76 1 7.34519e-18
-79 20 -5.03015e-23
-79 5 -3.83077e-15
-79 4 1.61233e-16
-80 80 1.02584
-80 20 -1.53213e-23
-80 5 7.01134e-17
-82 82 1.02207
-82 20 -5.67143e-23
-83 20 -7.66152e-23
+3 1 2.09667e-07
+3 2 -5.61361e-06
+3 3 1.05034
+3 4 -0.0753684
+3 5 8.87101e-07
+3 6 4.00821e-07
+3 7 -0.000315772
+3 8 -1.5896e-07
+3 9 -2.03732e-18
+3 10 -1.50865e-06
+3 11 -4.20342e-09
+3 15 -1.08828e-09
+3 16 -5.29883e-12
+3 17 -5.36574e-07
+3 18 -0.00204841
+3 20 1.79096e-10
+3 27 0.0830495
+3 28 0.0239369
+3 32 0.162961
+3 33 0.0139964
+3 37 0.216264
+3 38 -0.0182403
+3 42 0.230306
+3 43 -0.0530172
+3 47 0.207741
+3 48 -0.0741984
+3 52 0.162573
+3 53 -0.0763679
+3 57 0.111906
+3 58 -0.0638895
+3 62 0.0684081
+3 63 -0.0453119
+3 67 0.0368473
+3 68 -0.0278839
+3 72 0.0233857
+3 73 -0.0183612
+3 77 0
+3 78 0
+3 82 0
+3 83 0
+3 87 0
+3 88 0
+3 92 0
+3 93 0
+3 97 0
+3 98 0
+3 102 0
+3 103 0
+3 107 0
+3 108 0
+3 112 0
+3 113 0
+3 117 0
+3 118 0
+3 122 0
+3 123 0
+3 127 0
+3 128 0
+8 1 -5.6379e-08
+8 2 -5.6379e-08
+8 3 -5.6379e-08
+8 4 -5.6379e-08
+8 5 -5.63789e-08
+8 6 -5.63709e-08
+8 7 -0.00102241
+8 8 1.00006
+8 9 1.2303e-06
+8 10 -5.39396e-06
+8 11 2.52205e-08
+8 14 1.34533e-07
+8 18 0.00407312
+8 20 3.13539e-09
+22 22 1
+22 27 -54498.8
+22 32 -54087.2
+22 37 -53664.1
+22 42 -53229.8
+22 47 -52784.8
+22 52 -52328.9
+22 57 -51862.8
+22 62 -51386.4
+22 67 -50900.4
+22 72 -50404.8
+22 77 -49899.8
+22 82 -49385.6
+22 87 -48861.5
+22 92 -48336.6
+22 97 -47784
+22 102 -47200
+22 107 -46717.9
+22 112 -46409.7
+22 117 -48883.1
+22 122 -94803.4
+22 127 0
+32 2 2.67704e-09
+32 3 -3.19181e-09
+32 18 -3.3199e-10
+32 20 2.64015e-17
+32 32 0.862197
+47 2 3.15971e-07
+47 3 -5.16888e-07
+47 18 -6.09967e-08
+47 20 -4.95312e-16
+47 47 0.817417
+57 2 1.44913e-11
+57 3 -2.96339e-11
+57 18 -3.72001e-12
+57 20 -4.55052e-19
+57 57 0.913244
+77 2 1.30217e-16
+77 3 -1.83468e-15
+77 18 -2.77506e-16
+77 20 -1.37502e-22
+77 77 1.01728
+83 3 5.4442e-17
 83 4 -1.10598e-15
-86 86 1.02635
-86 20 -3.64541e-24
-86 1 1.23409e-19
-87 87 1.02407
+83 18 -1.29694e-16
+83 20 -7.66152e-23
+83 83 1.02212
+87 2 1.55096e-18
+87 3 -1.95499e-16
+87 18 -3.03288e-17
 87 20 -2.2486e-23
-88 88 1.024
-88 20 -3.07296e-23
-88 4 -3.4844e-16
-90 20 -3.71408e-24
-90 5 1.38872e-18
-91 20 -1.36428e-24
-91 1 1.03011e-20
-93 20 -1.14532e-23
-93 4 -1.07626e-16
-95 95 1.02516
-95 20 -1.51276e-24
-95 5 1.44754e-19
-111 111 1.02516
-111 20 -7.39022e-27
-111 1 5.60827e-26
+87 87 1.02407
+92 2 1.32167e-19
+92 3 -5.99676e-17
+92 18 -9.32422e-18
+92 20 -8.19719e-24
+92 92 1.02481
+103 3 2.31738e-21
+103 4 -8.19354e-18
+103 18 -1.02323e-18
+103 20 -1.1845e-24
+103 103 1.02512
+112 2 9.57683e-25
+112 3 -2.09945e-19
+112 18 -3.26722e-20
+112 20 -5.02173e-26
+112 112 1.02516
+113 3 4.87173e-24
+113 4 -4.29009e-19
+113 18 -5.35939e-20
+113 20 -8.0389e-26
+113 113 1.02516
+117 2 3.00265e-26
+117 3 -3.98811e-20
+117 18 -6.20642e-21
+117 20 -1.07295e-26
+117 117 1.02516
+118 3 1.66265e-25
+118 4 -8.48772e-20
+118 18 -1.06034e-20
+118 20 -1.78897e-26
+118 118 1.02516
+123 3 4.66292e-27
+123 4 -1.52403e-20
+123 18 -1.90392e-21
+123 20 -3.58919e-27
+123 123 1.02516
+127 2 1.61478e-29
+127 3 -1.0682e-21
+127 18 -1.66237e-22
+127 20 -3.56672e-28
+127 127 1.02516
+128 128 1.02516
+128 20 -6.49772e-28
+128 18 -3.10301e-22
+128 4 -2.48386e-21
+128 3 1.07494e-28
+126 126 1.02516
+126 20 -4.59537e-29
+126 18 -2.09008e-23
+126 2 -3.39662e-22
+126 1 7.17244e-31
 116 116 1.02516
 116 20 -1.51385e-27
+116 18 -8.54504e-22
+116 2 -1.38867e-20
 116 1 1.6107e-27
-119 119 1.02516
-119 20 -9.00935e-27
-119 5 -1.59427e-19
-119 4 1.41551e-25
-130 5 9.35515e-29
-130 20 -1.47453e-28
-125 5 3.38021e-27
-125 20 -7.45778e-28
-125 125 1.02516
-120 5 1.01332e-25
-120 20 -3.41921e-27
-120 120 1.02516
-118 4 -8.48772e-20
-118 20 -1.78897e-26
-115 5 2.51764e-24
-115 20 -1.41919e-26
-114 4 3.81563e-24
-114 5 -7.74007e-19
-114 20 -3.88846e-26
-110 5 5.18144e-23
-110 20 -5.32645e-26
-108 4 -1.96782e-18
-108 20 -3.25329e-25
-106 1 1.58898e-24
-106 20 -3.23367e-26
-105 5 8.82913e-22
-105 20 -1.80518e-25
-105 105 1.02516
-100 5 1.24377e-20
-100 20 -5.51212e-25
-100 100 1.02516
-99 4 2.34647e-20
-99 5 -5.00702e-17
-99 20 -1.67746e-24
-92 20 -8.19719e-24
-89 4 2.96271e-18
-89 5 -5.07976e-16
-89 20 -1.19898e-23
-89 89 1.0251
-85 5 1.09383e-17
-85 20 -8.08599e-24
-55 5 1.72311e-14
-55 20 1.9845e-23
-46 1 7.59601e-16
-46 20 7.85428e-23
+111 111 1.02516
+111 20 -7.39022e-27
+111 18 -4.69178e-21
+111 2 -7.62484e-20
+111 1 5.60827e-26
+91 91 1.02547
+91 20 -1.36428e-24
+91 18 -1.51727e-18
+91 2 -2.49627e-17
+91 1 1.03011e-20
+86 86 1.02635
+86 20 -3.64541e-24
+86 18 -4.8409e-18
+86 2 -8.23275e-17
+86 1 1.23409e-19
+76 76 1.03775
+76 20 -1.23817e-23
+76 18 -2.77218e-17
+76 2 -6.682e-16
+76 1 7.34519e-18
+73 73 1.01015
+73 20 -5.41364e-22
+73 18 -1.64547e-15
+73 4 -1.94082e-14
+73 3 5.00653e-15
+56 56 1.38522
+56 20 4.76634e-23
+56 18 -4.77846e-17
+56 2 -1.15556e-14
+56 1 3.63701e-16
+51 51 1.64291
+51 20 6.82554e-23
+51 18 -4.34373e-17
+51 2 -1.74247e-14
+51 1 5.64117e-16
 46 46 1.95582
-45 5 2.76304e-14
-45 20 4.29939e-23
-44 4 3.50603e-14
-44 5 -1.43928e-13
-44 20 1.94788e-22
-44 44 1.22519
-42 20 1.68193e-14
-41 1 8.82948e-16
-41 20 7.37233e-23
-41 41 2.23984
-40 5 2.60058e-14
-40 20 3.65434e-23
-39 4 3.25834e-14
-39 5 -1.326e-13
-39 20 1.50307e-22
-39 39 1.25201
-35 5 1.89274e-14
-35 20 2.42864e-23
-35 35 1.09935
-30 5 8.30179e-15
-30 20 1.26503e-23
-25 25 1
-25 35 -48070.2
-25 50 -46875.4
-25 75 -44675.7
-25 80 -44206.8
-25 95 -42746.3
-25 100 -42242.5
-25 105 -41730.4
-25 120 -40147.8
-25 125 -39605.7
-18 1 -2.04393e-09
-18 4 3.24268e-06
-18 5 9.14862e-07
-18 8 4.60627e-08
-18 10 1.42088e-08
-18 11 2.55451e-10
-18 12 1.57467e-13
-18 15 1.52186e-11
-18 16 7.40995e-14
-18 17 1.93124e-09
-18 20 -1.77142e-13
-18 33 -2.00701e-07
-18 35 0
-18 41 0
-18 46 0
-18 58 3.41024e-06
-18 63 2.78937e-06
-18 68 2.17843e-06
+46 20 7.85428e-23
+46 18 -3.90387e-17
+46 2 -2.31468e-14
+46 1 7.59601e-16
+36 36 2.36736
+36 20 5.48506e-23
+36 18 -3.12504e-17
+36 2 -2.62405e-14
+36 1 8.68257e-16
+33 33 1.03915
+33 20 1.19249e-16
+33 18 3.13952e-10
+33 4 -5.06218e-08
+33 3 4.2654e-08
+26 26 1.74046
+26 20 9.25889e-24
+26 18 -2.47109e-17
+26 2 -1.11448e-14
+26 1 3.62494e-16
+16 16 1
+15 20 -3.36998e-08
+15 18 -0.0247516
+15 15 1
+15 14 -3.36333e-07
+15 13 2.16309e-10
+15 12 2.45248e-10
+15 11 2.26985e-07
+15 10 7.55155e-05
+15 9 -1.32872e-05
+15 8 -0.000473389
+15 7 0.0158523
+15 6 6.0878e-07
+15 5 6.08892e-07
+15 4 6.08893e-07
+15 3 6.08893e-07
+15 2 6.08893e-07
+15 1 6.08893e-07
+11 75 0
+11 73 0
+11 68 0
+11 66 0
+11 63 0
+11 61 0
+11 56 0
+11 55 -0.0186851
+11 51 0.111418
+11 48 0
+11 44 0
+11 43 0
+11 36 0
+11 34 0.0182456
+11 33 0
+11 31 -0.172794
+11 29 0
+11 28 7.18827e-05
+11 26 0
+11 20 -1.15543e-10
+11 18 0.000822131
+11 17 0
+11 14 6.72665e-08
+11 13 1.09247e-11
+11 12 1.23862e-11
+11 11 1
+11 10 0
+11 8 7.26177e-06
+11 7 6.69125e-05
+11 6 -4.54269e-14
+11 5 0
+11 4 2.726e-15
+11 3 -1.144e-15
+11 2 8.51694e-15
+11 1 0
+1 126 0
+1 121 0
+1 116 0
+1 111 0
+1 106 0
+1 101 0
+1 96 0
+1 91 0
+1 86 0
+1 81 0
+1 76 0
+1 71 0.11683
+1 66 0.0972206
+1 61 0.191204
+1 56 0.40627
+1 51 0.668505
+1 46 0.962069
+1 41 1.18844
+1 36 1.38915
+1 31 1.03677
+1 26 0.7812
+1 20 -4.01435e-09
+1 18 -0.00421819
+1 17 -6.47378e-08
+1 16 -5.13201e-12
+1 15 -1.05402e-09
+1 11 -4.20342e-09
+1 10 -7.54326e-09
+1 9 -1.2303e-06
+1 8 -4.18657e-05
+1 7 -1.25934e-05
+1 6 1.47134e-07
+1 5 2.06717e-07
+1 4 9.67761e-07
+1 3 3.17213e-06
+1 2 -0.000142653
+1 1 1
 13 1 -5.6379e-08
-13 4 -5.6379e-08
-13 5 -5.63789e-08
-13 8 4.17067e-05
-13 12 -1.23862e-11
-13 20 4.0166e-09
 12 1 1.12758e-06
-12 4 1.12758e-06
-12 5 1.12758e-06
-12 8 -0.000808752
-12 10 0.000102485
-12 11 5.0441e-07
-12 12 1
-12 20 -6.80951e-08
-7 1 5.6379e-08
-7 4 5.6379e-08
-7 5 5.63789e-08
-7 8 -4.0753e-05
-7 10 5.39396e-06
-7 11 2.52205e-08
-7 12 2.47725e-11
-7 20 -3.36647e-09
-6 1 6.19435e-10
-6 4 -1.16327e-07
-6 5 -3.228e-06
-6 8 -1.5896e-07
-6 10 -7.54326e-08
-6 11 -4.20342e-09
-6 15 6.5756e-10
-6 16 3.20165e-12
-6 17 6.54493e-08
-6 20 -4.06592e-12
-6 35 -0.0740706
-6 50 -0.0581895
-6 75 -0.00197719
-6 80 0
-6 95 0
-6 100 0
-6 105 0
-6 120 0
-6 125 0
 5 1 3.9564e-09
-5 4 -5.1094e-06
+4 1 6.421e-08
+13 2 -5.6379e-08
+12 2 1.12758e-06
+5 2 -5.72528e-09
+4 2 4.75331e-07
+129 5 -5.09073e-21
+125 5 3.38021e-27
+120 5 1.01332e-25
+115 5 2.51764e-24
+110 5 5.18144e-23
+109 5 -3.41691e-18
+105 5 8.82913e-22
+99 5 -5.00702e-17
+95 5 1.44754e-19
+94 5 -1.66575e-16
+85 5 1.09383e-17
+84 5 -1.43666e-15
+80 5 7.01134e-17
+79 5 -3.83077e-15
+60 5 9.87515e-15
+54 5 -1.0656e-13
+45 5 2.76304e-14
+44 5 -1.43928e-13
+39 5 -1.326e-13
+35 5 1.89274e-14
+13 5 -5.63789e-08
+12 5 1.12758e-06
 5 5 1.00101
-5 8 -1.5896e-07
-5 10 -5.08732e-07
+4 5 -0.00100485
+13 9 1.2303e-06
+12 9 -2.4606e-05
+5 9 0
+4 9 0
+12 11 5.0441e-07
 5 11 -4.20342e-09
+4 11 -4.20342e-09
+13 12 -1.23862e-11
+12 12 1
+13 13 1
+12 13 4.36987e-10
+12 14 6.72665e-08
 5 15 1.69443e-09
-5 16 8.25018e-12
-5 17 2.27179e-07
-5 20 -5.82546e-11
-5 35 0.0738608
-5 39 -0.227587
-5 44 -0.20013
-5 50 0.0578207
-5 59 -0.0518787
-5 69 -0.0101891
-5 75 0
-5 80 0
-5 89 0.0189957
-5 94 0
-5 95 0
-5 100 0
-5 104 0
-5 105 0
-5 109 0
-5 119 0
-5 120 0
-5 125 0
-5 129 0
-3 2 -5.61361e-06
-4 2 4.75331e-07
-9 2 -5.6379e-08
-14 2 -5.07411e-07
-97 2 9.27036e-21
-102 2 5.32586e-22
-107 2 2.49806e-23
-112 2 9.57683e-25
-117 2 3.00265e-26
-3 4 -0.0753684
-4 4 1.07537
-9 4 -5.6379e-08
-14 4 -5.07411e-07
-38 4 -0.000510366
-49 4 3.15009e-14
-53 4 -6.74733e-05
-54 4 2.3892e-14
-58 4 -3.74534e-08
-59 4 1.51069e-14
-64 4 7.67782e-15
-69 4 2.94565e-15
-74 4 8.09583e-16
-78 4 -3.85526e-15
-84 4 2.45044e-17
-94 4 2.9115e-19
-98 4 -3.10323e-17
-103 4 -8.19354e-18
-104 4 1.555e-21
-109 4 8.48368e-23
-113 4 -4.29009e-19
-123 4 -1.52403e-20
-124 4 4.33455e-27
-128 4 -2.48386e-21
-129 4 1.09604e-28
-3 7 -0.000315772
-4 7 -0.000523881
-9 7 -1.46354e-07
-14 7 -0.0091348
-3 9 -2.03732e-18
-4 9 0
-9 9 1
-14 9 1.10727e-05
-3 10 -1.50865e-06
-4 10 -2.53927e-06
-14 10 -4.85457e-05
-9 12 -1.23862e-11
-14 12 1.23863e-11
-3 16 -5.29883e-12
-4 16 1.9806e-11
-3 18 -0.00204841
-4 18 0.000848761
-9 18 0.00417594
-14 18 0.0374802
-38 18 -3.24413e-06
-49 18 -6.3223e-16
-53 18 -2.59383e-06
-54 18 -6.73146e-16
-58 18 -1.77629e-09
-59 18 -6.51794e-16
-64 18 -5.52136e-16
-69 18 -3.94321e-16
-74 18 -2.30533e-16
-78 18 -4.05664e-16
-84 18 -4.62553e-17
-94 18 -5.68166e-18
-97 18 -2.62597e-18
-98 18 -3.8709e-18
-102 18 -6.72056e-19
-103 18 -1.02323e-18
-104 18 -4.70628e-19
-107 18 -1.55734e-19
-109 18 -1.17282e-19
-112 18 -3.26722e-20
-113 18 -5.35939e-20
-117 18 -6.20642e-21
-123 18 -1.90392e-21
-124 18 -1.0253e-21
-128 18 -3.10301e-22
+4 15 4.06779e-09
 129 18 -1.7475e-22
-22 22 1
+125 18 -4.15415e-22
+120 18 -2.12817e-21
+115 18 -9.93638e-21
+110 18 -4.22695e-20
+109 18 -1.17282e-19
+105 18 -1.63797e-19
+99 18 -1.71584e-18
+98 18 -3.8709e-18
+95 18 -1.85403e-18
+94 18 -5.68166e-18
+93 18 -1.33669e-17
+85 18 -1.42592e-17
+84 18 -4.62553e-17
+80 18 -3.37527e-17
+79 18 -1.11357e-16
+68 18 -1.88183e-14
+63 18 -2.0466e-12
+60 18 -2.32712e-16
+54 18 -6.73146e-16
+45 18 -2.2267e-16
+44 18 -5.60673e-16
+39 18 -4.8124e-16
+35 18 -1.68621e-16
+28 18 4.88866e-14
+13 18 0.00417594
+12 18 -0.0495021
+5 18 -0.00015244
+4 18 0.000848761
 24 24 1
-3 27 0.0830495
-22 27 -54498.8
-3 28 0.0239369
+28 28 1.04909
+23 28 -52460.6
 4 28 -0.0238651
-3 33 0.0139964
-4 33 -0.0139964
-4 34 0.218948
-24 34 -50070.6
-3 37 0.216264
-22 37 -53664.1
-3 38 -0.0182403
+24 29 -50458.4
+5 29 -0.128117
+4 29 0.136658
+23 38 -51663.3
 4 38 0.0182403
-38 38 1.00692
-4 39 0.233423
+39 39 1.25201
 24 39 -49671.4
-3 43 -0.0530172
-4 43 0.0530172
-3 47 0.207741
-22 47 -52784.8
-3 48 -0.0741984
-4 48 0.0741984
-3 53 -0.0763679
-4 53 0.0763679
-53 53 0.94879
-3 57 0.111906
-22 57 -51862.8
-4 64 0.0231977
-24 64 -47514.7
-64 64 1.04997
-3 67 0.0368473
-22 67 -50900.4
-3 68 -0.0278839
-4 68 0.0278839
-3 72 0.0233857
-22 72 -50404.8
-3 73 -0.0183612
+5 39 -0.227587
+4 39 0.233423
+5 40 0.0826973
+23 43 -51241
+4 43 0.0530172
+44 44 1.22519
+24 44 -49261.2
+5 44 -0.20013
+4 44 0.203522
+45 45 1.10083
+5 45 0.0749193
+5 50 0.0578207
+5 55 0.0397058
+24 59 -47966.8
+5 59 -0.0518787
+4 59 0.0538999
+60 60 1.04777
+5 60 0.0230579
+63 63 0.979846
+23 63 -49443.7
+4 63 0.0453119
+23 73 -49508
 4 73 0.0183612
-4 74 0.023814
-24 74 -46581.7
-74 74 1.02848
-3 78 0
+23 78 -43888.4
 4 78 0
-78 78 1.018
-4 79 0
+79 79 1.02597
 24 79 -46101
-4 84 0
-24 84 -45611.2
-84 84 1.02524
-3 93 0
+5 79 -0.0029784
+4 79 0
+23 88 -105156
+4 88 0
+24 89 -45112.6
+5 89 0.0189957
+4 89 0
+93 93 1.02476
+23 93 0
 4 93 0
-4 99 0
-24 99 -44090
-3 107 0
-22 107 -46717.9
-107 107 1.02515
-3 108 0
-4 108 0
-3 112 0
-22 112 -46409.7
-112 112 1.02516
-3 113 0
-4 113 0
-113 113 1.02516
-3 117 0
-22 117 -48883.1
-117 117 1.02516
-3 122 0
-22 122 -94803.4
-4 124 0
-24 124 -41396.2
-124 124 1.02516
-3 128 0
-4 128 0
-128 128 1.02516
-4 129 0
-24 129 -40835.9
-129 129 1.02516
-22 127 0
-3 127 0
-123 123 1.02516
-4 123 0
-3 123 0
+5 100 0
+23 103 0
+4 103 0
+24 104 -43566.4
+5 104 0
+4 104 0
+109 109 1.02516
+24 109 -43035
+5 109 0
+4 109 0
+110 110 1.02516
+5 110 0
 24 119 -41949.7
+5 119 0
 4 119 0
+120 120 1.02516
+5 120 0
+23 123 0
+4 123 0
+24 124 -41396.2
+5 124 0
+4 124 0
+125 125 1.02516
+5 125 0
+129 129 1.02516
+24 129 -40835.9
+5 129 0
+4 129 0
+5 130 0
+4 128 0
+23 128 0
 4 118 0
-3 118 0
-24 114 -42496.1
+23 118 0
+5 115 0
+115 115 1.02516
 4 114 0
-109 109 1.02516
-24 109 -43035
-4 109 0
-104 104 1.02515
-24 104 -43566.4
-4 104 0
-103 103 1.02512
-4 103 0
-3 103 0
-102 102 1.02513
-22 102 -47200
-3 102 0
-98 98 1.02504
+5 114 0
+24 114 -42496.1
+4 113 0
+23 113 0
+4 108 0
+23 108 0
+5 105 0
+105 105 1.02516
+4 99 0
+5 99 0
+24 99 -44090
+99 99 1.02514
 4 98 0
-3 98 0
-97 97 1.02506
-22 97 -47784
-3 97 0
-94 94 1.02512
-24 94 -44605.4
+23 98 0
+98 98 1.02504
+5 95 0
+95 95 1.02516
 4 94 0
-22 92 -48336.6
-3 92 0
-24 89 -45112.6
-4 89 0
-4 88 0
-3 88 0
-22 87 -48861.5
-3 87 0
+5 94 0
+24 94 -44605.4
+94 94 1.02512
+5 90 0
+5 85 0
+85 85 1.02535
+4 84 0
+5 84 0
+24 84 -45611.2
+84 84 1.02524
 4 83 0
-3 83 0
-22 82 -49385.6
-3 82 0
-22 77 -49899.8
-3 77 0
-69 69 1.03518
-24 69 -47053
+23 83 -34492.6
+5 80 0
+80 80 1.02584
+5 75 0
+4 74 0.023814
+5 74 -0.00446512
+24 74 -46581.7
+5 70 0.00498403
 4 69 0
-4 63 0.0453119
-3 63 -0.0453119
-22 62 -51386.4
-3 62 0.0684081
-59 59 1.07739
-24 59 -47966.8
-4 59 0.0538999
-58 58 0.961268
+5 69 -0.0101891
+24 69 -47053
+4 68 0.0278839
+23 68 -48828.1
+68 68 0.997323
+5 65 0.0114346
+4 64 0.0231977
+5 64 -0.0246476
+24 64 -47514.7
 4 58 0.0638895
-3 58 -0.0638895
-54 54 1.1199
-24 54 -48408.7
+23 58 -49910.6
 4 54 0.0887136
-22 52 -52328.9
-3 52 0.162573
-49 49 1.17371
-24 49 -48840.2
+5 54 -0.0942582
+24 54 -48408.7
+54 54 1.1199
+4 53 0.0763679
+23 53 -50364.7
 4 49 0.15693
-24 44 -49261.2
-4 44 0.203522
-22 42 -53229.8
-3 42 0.230306
-22 32 -54087.2
-3 32 0.162961
-24 29 -50458.4
-4 29 0.136658
-129 20 -3.57066e-28
-128 20 -6.49772e-28
-124 20 -1.88602e-27
-123 20 -3.58919e-27
-117 20 -1.07295e-26
-113 20 -8.0389e-26
-112 20 -5.02173e-26
-109 20 -1.51443e-25
-107 20 -2.1121e-25
-104 20 -5.31508e-25
-103 20 -1.1845e-24
-102 20 -7.97313e-25
-98 20 -3.8794e-24
-97 20 -2.69787e-24
-94 20 -4.74808e-24
-84 20 -2.66372e-23
-78 20 -1.87671e-22
-74 20 -7.47787e-23
-69 20 -7.47888e-23
-64 20 -2.73851e-23
-59 20 5.77262e-23
-58 20 -1.64087e-17
-54 20 1.44534e-22
-53 20 1.91119e-13
-49 20 1.95887e-22
-38 20 2.09731e-12
-14 20 2.81029e-08
-9 20 4.0166e-09
+5 49 -0.148631
+24 49 -48840.2
+4 48 0.0741984
+23 48 -50808.1
+5 35 0.0738608
+35 35 1.09935
+4 34 0.218948
+5 34 -0.205263
+24 34 -50070.6
+4 33 -0.0139964
+23 33 -52074.4
+5 30 0.0449037
+23 23 1
 4 20 -6.72983e-10
-3 20 1.79096e-10
+5 20 -5.82546e-11
+12 20 -6.80951e-08
+13 20 4.0166e-09
+28 20 2.05748e-22
+35 20 2.42864e-23
+39 20 1.50307e-22
+44 20 1.94788e-22
+45 20 4.29939e-23
+54 20 1.44534e-22
+60 20 -4.80723e-24
+63 20 -1.974e-19
+68 20 -3.71797e-21
+79 20 -5.03015e-23
+80 20 -1.53213e-23
+84 20 -2.66372e-23
+85 20 -8.08599e-24
+93 20 -1.14532e-23
+94 20 -4.74808e-24
+95 20 -1.51276e-24
+98 20 -3.8794e-24
+99 20 -1.67746e-24
+105 20 -1.80518e-25
+109 20 -1.51443e-25
+110 20 -5.32645e-26
+115 20 -1.41919e-26
+120 20 -3.41921e-27
+125 20 -7.45778e-28
+129 20 -3.57066e-28
 4 17 7.72345e-07
-3 17 -5.36574e-07
-4 15 4.06779e-09
-3 15 -1.08828e-09
-14 14 1
-14 13 1.09247e-11
-9 13 -1.09247e-11
-14 11 2.52205e-07
-4 11 -4.20342e-09
-3 11 -4.20342e-09
-14 8 0.000504751
-9 8 4.17067e-05
+5 17 2.27179e-07
+4 16 1.9806e-11
+5 16 8.25018e-12
+4 10 -2.53927e-06
+5 10 -5.08732e-07
+12 10 0.000102485
 4 8 -1.5896e-07
-3 8 -1.5896e-07
-14 6 -5.07338e-07
-9 6 -5.6379e-08
+5 8 -1.5896e-07
+12 8 -0.000808752
+13 8 4.17067e-05
+4 7 -0.000523881
+5 7 -0.000113867
+12 7 0.0220356
+13 7 -1.46354e-07
 4 6 1.81395e-06
-3 6 4.00821e-07
-129 5 -5.09073e-21
-124 5 -2.98685e-20
-109 5 -3.41691e-18
-104 5 -1.37158e-17
-94 5 -1.66575e-16
-84 5 -1.43666e-15
-74 5 -9.66208e-15
-69 5 -2.22072e-14
-64 5 -4.40264e-14
-59 5 -7.39662e-14
-54 5 -1.0656e-13
-49 5 -1.33059e-13
-14 5 -5.07411e-07
-9 5 -5.63789e-08
-4 5 -0.00100485
-3 5 8.87101e-07
-128 3 1.07494e-28
-123 3 4.66292e-27
-117 3 -3.98811e-20
-113 3 4.87173e-24
-112 3 -2.09945e-19
-107 3 -1.00072e-18
-103 3 2.31738e-21
-102 3 -4.3187e-18
-98 3 3.76454e-20
-97 3 -1.68776e-17
-78 3 4.8811e-16
-58 3 1.86517e-08
-53 3 3.74969e-05
-38 3 0.00038885
-14 3 -5.07411e-07
-9 3 -5.6379e-08
+5 6 -0.00104175
+12 6 1.12743e-06
+13 6 -5.6379e-08
+35 6 -1.06586e-13
+45 6 -1.52538e-13
+60 6 -7.44616e-14
+80 6 -4.70965e-15
+85 6 -1.90612e-15
+95 6 -2.42129e-16
+105 6 -2.1338e-17
+110 6 -5.50569e-18
+115 6 -1.29419e-18
+120 6 -2.77187e-19
+125 6 -5.41063e-20
+4 4 1.07537
+5 4 -5.1094e-06
+12 4 1.12758e-06
+13 4 -5.6379e-08
+28 4 -2.69016e-12
+39 4 3.25834e-14
+44 4 3.50603e-14
+54 4 2.3892e-14
+63 4 -3.6762e-11
+68 4 -2.87613e-13
+79 4 1.61233e-16
+84 4 2.45044e-17
+93 4 -1.07626e-16
+94 4 2.9115e-19
+98 4 -3.10323e-17
+99 4 2.34647e-20
+109 4 8.48368e-23
+129 4 1.09604e-28
 4 3 -0.0499177
-3 3 1.05034
-14 1 -5.07411e-07
-9 1 -5.6379e-08
-4 1 6.421e-08
-3 1 2.09667e-07
-127 2 1.61478e-29
-126 2 -3.39662e-22
-122 2 7.69576e-28
-121 2 -2.28428e-21
-101 2 -1.69307e-18
-96 2 -6.84142e-18
-81 2 -2.46198e-16
-77 2 1.30217e-16
-67 2 1.02401e-14
+5 3 -9.39363e-08
+12 3 1.12758e-06
+13 3 -5.6379e-08
+28 3 2.47367e-12
+63 3 1.63597e-11
+68 3 1.0996e-13
+93 3 5.04551e-19
+98 3 3.76454e-20
+7 2 5.6379e-08
+7 4 5.6379e-08
+7 6 5.63709e-08
+7 7 1.00116
+7 10 5.39396e-06
+7 12 2.47725e-11
+7 20 -3.36647e-09
+20 2 -1.32126
+20 4 -29.7996
+20 6 -8.35841
+20 7 -37.3426
+20 10 -0.130576
+20 12 -1.47715e-06
+20 14 -0.00445353
+20 15 -0.000139857
+20 16 -6.8096e-07
+20 20 0.999994
+20 27 -5.77173
+20 28 0
+20 29 0
+20 30 0
+20 32 -21.1696
+20 33 1.84408
+20 36 0
+20 39 0
+20 41 0
+20 42 -62.6554
+20 44 0
+20 45 0
+20 50 0
+20 51 0
+20 53 -31.7106
+20 54 0
+20 55 0
+20 57 -55.4209
+20 58 -31.3394
+20 59 0
+20 60 0
+20 62 -29.0175
+20 64 0
+20 71 0
+20 72 0
+20 75 0
+20 77 0
+20 78 0
+20 79 0
+20 80 0
+20 81 0
+20 85 0
+20 86 0
+20 87 0
+20 91 0
+20 92 0
+20 94 0
+20 102 0
+20 103 0
+20 104 0
+20 105 0
+20 106 0
+20 111 0
+20 114 0
+20 115 0
+20 118 0
+20 121 0
+20 123 0
+20 127 0
+20 128 0
+20 130 0
+21 21 1
+21 36 -55679.5
+21 41 -55232.9
+21 51 -54307.4
+21 71 -52332.7
+21 81 -51287.8
+21 86 -50752
+21 91 -50207.6
+21 106 -48525.5
+21 111 -47949.3
+21 121 -46775.5
+27 2 1.7333e-12
+27 20 1.60246e-20
+27 27 0.94207
+29 4 1.12415e-14
+29 20 3.79006e-23
+29 29 1.15183
+31 2 -2.07438e-14
+31 20 2.9675e-23
+40 6 -1.42018e-13
+40 20 3.65434e-23
+42 2 3.10792e-06
+42 20 1.68193e-14
+42 42 0.794851
+43 4 -0.0555268
+43 20 2.73732e-10
+49 4 3.15009e-14
+49 20 1.95887e-22
+50 6 -1.39097e-13
+50 20 3.78102e-23
+50 50 1.08333
+52 2 2.77975e-09
+52 20 -3.69682e-17
+53 4 -6.74733e-05
+53 20 1.91119e-13
+53 53 0.94879
+58 4 -3.74534e-08
+58 20 -1.64087e-17
+58 58 0.961268
+59 4 1.51069e-14
+59 20 5.77262e-23
+59 59 1.07739
+64 4 7.67782e-15
+64 20 -2.73851e-23
+64 64 1.04997
+65 6 -4.38977e-14
+65 20 -2.42816e-23
 66 2 -3.53565e-15
-57 2 1.44913e-11
-47 2 3.15971e-07
-37 2 6.32825e-07
-36 2 -2.62405e-14
-32 2 2.67704e-09
-31 2 -2.07438e-14
-27 2 1.7333e-12
-26 2 -1.11448e-14
-15 2 6.08893e-07
-11 2 8.51694e-15
-10 2 0
-8 2 -5.6379e-08
-2 2 1.00015
-1 2 -0.000142653
-48 4 -0.0182945
-34 4 2.39362e-14
-33 4 -5.06218e-08
-29 4 1.12415e-14
-28 4 -2.69016e-12
-15 4 6.08893e-07
-11 4 2.726e-15
-10 4 0
-8 4 -5.6379e-08
-2 4 -1.24529e-06
-1 4 9.67761e-07
-75 5 3.59641e-16
-34 5 -9.87808e-14
-29 5 -4.97786e-14
-15 5 6.08892e-07
-11 5 0
-10 5 0
-8 5 -5.63789e-08
-2 5 -3.46404e-07
-1 5 2.06717e-07
-15 7 0.0158523
-11 7 6.69125e-05
-10 7 0.00108918
-8 7 -0.00102241
-2 7 -0.000163458
-1 7 -1.25934e-05
-15 9 -1.32872e-05
-8 9 1.2303e-06
-2 9 -1.23894e-12
-1 9 -1.2303e-06
-15 10 7.55155e-05
-11 10 0
-10 10 1.00001
-8 10 -5.39396e-06
-2 10 -7.54326e-07
-1 10 -7.54326e-09
-15 15 1
-2 15 -4.27748e-09
-1 15 -1.05402e-09
-16 16 1
-2 16 -2.0827e-11
-1 16 -5.13201e-12
-127 20 -3.56672e-28
-126 20 -4.59537e-29
-122 20 -2.06143e-27
-121 20 -2.7823e-28
-101 20 -1.26586e-25
+66 20 2.50929e-24
+70 6 -2.28268e-14
+70 20 -2.99607e-23
+72 2 1.05988e-15
+72 20 -3.56644e-22
+72 72 1.00709
+78 4 -3.85526e-15
+78 20 -1.87671e-22
+78 78 1.018
+88 4 -3.4844e-16
+88 20 -3.07296e-23
+96 2 -6.84142e-18
 96 20 -4.41792e-25
+97 2 9.27036e-21
+97 20 -2.69787e-24
+100 6 -7.53043e-17
+100 20 -5.51212e-25
+101 2 -1.69307e-18
+101 20 -1.26586e-25
+102 2 5.32586e-22
+102 20 -7.97313e-25
+102 102 1.02513
+104 4 1.555e-21
+104 20 -5.31508e-25
+104 104 1.02515
+106 2 -3.78132e-19
+106 20 -3.23367e-26
+106 106 1.02516
+107 2 2.49806e-23
+107 20 -2.1121e-25
+108 4 -1.96782e-18
+108 20 -3.25329e-25
+114 4 3.81563e-24
+114 20 -3.88846e-26
+114 114 1.02516
+119 4 1.41551e-25
+119 20 -9.00935e-27
+121 2 -2.28428e-21
+121 20 -2.7823e-28
+121 121 1.02516
+122 2 7.69576e-28
+122 20 -2.06143e-27
+124 4 4.33455e-27
+124 20 -1.88602e-27
+130 130 1.02516
+130 20 -1.47453e-28
+130 6 -9.63052e-21
+90 20 -3.71408e-24
+90 6 -7.1014e-16
+89 20 -1.19898e-23
+89 4 2.96271e-18
+82 20 -5.67143e-23
+82 2 1.52358e-17
+81 81 1.02926
 81 20 -7.94892e-24
-77 20 -1.37502e-22
+81 2 -2.46198e-16
+75 75 1.02719
 75 20 -2.43151e-23
+75 6 -1.07803e-14
+74 20 -7.47787e-23
+74 4 8.09583e-16
+71 71 1.05976
+71 20 -1.03771e-23
+71 2 -1.63115e-15
+69 20 -7.47888e-23
+69 4 2.94565e-15
 67 20 -1.29458e-21
-66 20 2.50929e-24
-57 20 -4.55052e-19
+67 2 1.02401e-14
+62 62 0.956854
+62 20 -1.18989e-20
+62 2 1.98412e-13
+61 20 2.36266e-23
+61 2 -6.78651e-15
+55 55 1.06392
+55 20 1.9845e-23
+55 6 -1.09455e-13
 48 20 8.36086e-11
-47 20 -4.95312e-16
+48 4 -0.0182945
+41 41 2.23984
+41 20 7.37233e-23
+41 2 -2.67359e-14
+38 20 2.09731e-12
+38 4 -0.000510366
 37 20 5.62728e-15
-36 20 5.48506e-23
+37 2 6.32825e-07
 34 20 8.87269e-23
-33 20 1.19249e-16
-32 20 2.64015e-17
-31 20 2.9675e-23
-29 20 3.79006e-23
-28 20 2.05748e-22
-27 20 1.60246e-20
-26 20 9.25889e-24
-15 20 -3.36998e-08
-11 20 -1.15543e-10
-10 20 7.65673e-10
-8 20 3.13539e-09
-2 20 -9.6173e-11
-1 20 -4.01435e-09
-26 26 1.74046
-21 26 -56538.4
-11 26 0
-2 26 -0.720168
-1 26 0.7812
-29 29 1.15183
-11 29 0
-33 33 1.03915
-11 33 0
-34 34 1.23118
-11 34 0.0182456
-36 36 2.36736
-21 36 -55679.5
-11 36 0
-2 36 -1.33127
-1 36 1.38915
-2 42 -0.230306
-11 43 0
-11 44 0
-47 47 0.817417
-2 47 -0.207741
-48 48 0.950959
-11 48 0
-21 51 -54307.4
-11 51 0.111418
-2 51 -0.612796
-1 51 0.668505
-21 56 -53828.6
-11 56 0
-2 56 -0.361834
-1 56 0.40627
-57 57 0.913244
-2 57 -0.111914
-2 62 -0.0683113
-67 67 0.988015
-2 67 -0.0370897
-21 71 -52332.7
-2 71 -0.0292076
-1 71 0.11683
-11 73 0
-21 76 -51814.9
-2 76 -0.0115763
-1 76 0
-77 77 1.01728
-2 77 -0.00747763
-81 81 1.02926
-21 81 -51287.8
-2 81 0
-1 81 0
-2 82 -0.00434723
-21 86 -50752
-2 86 0
-1 86 0
-2 87 0
-2 92 0
-96 96 1.02523
-21 96 -49654.8
-2 96 0
-1 96 0
-2 102 0
-21 111 -47949.3
-2 111 0
-1 111 0
-2 117 0
+34 4 2.39362e-14
+30 30 1.07003
+30 20 1.26503e-23
+30 6 -5.38785e-14
+25 130 -39056.4
+25 115 -40683
+25 105 -41730.4
+25 85 -43728.7
+25 80 -44206.8
+25 75 -44675.7
+25 60 -46025.4
+25 55 -46455.6
+25 50 -46875.4
+25 45 -47284.4
+25 30 -48446.2
+19 20 -1.15543e-10
+19 19 1
+19 14 6.72665e-08
+19 12 1.23862e-11
+19 10 0
+19 7 6.69125e-05
+19 6 0
+19 4 0
+19 2 0
+18 60 0
+18 58 3.41024e-06
+18 55 0
+18 42 6.81792e-06
+18 41 0
+18 33 -2.00701e-07
+18 28 0
+18 20 -1.77142e-13
+18 19 2.86321e-11
+18 16 7.40995e-14
+18 15 1.52186e-11
+18 14 4.66853e-10
+18 12 1.57467e-13
+18 10 1.42088e-08
+18 7 4.04581e-06
+18 6 9.09531e-07
+18 4 3.24268e-06
+18 2 1.43774e-07
+17 20 3.13539e-09
+17 14 1.34533e-07
+17 10 -5.39396e-06
+17 7 -0.00102241
+17 6 -5.63709e-08
+17 4 -5.6379e-08
+17 2 -5.6379e-08
+14 20 2.81029e-08
+14 14 1
+14 12 1.23863e-11
+14 10 -4.85457e-05
+14 7 -0.0091348
+14 6 -5.07338e-07
+14 4 -5.07411e-07
+14 2 -5.07411e-07
+10 20 7.65673e-10
+10 14 -6.72665e-08
+10 10 1.00001
+10 7 0.00108918
+10 6 -8.08598e-12
+10 4 0
+10 2 0
+9 20 4.0166e-09
+9 12 -1.23862e-11
+9 7 -1.46354e-07
+9 6 -5.6379e-08
+9 4 -5.6379e-08
+9 2 -5.6379e-08
+6 130 0
+6 115 0
+6 105 0
+6 85 -0.00140839
+6 80 0
+6 75 -0.00197719
+6 60 -0.0226576
+6 55 -0.038757
+6 50 -0.0581895
+6 45 -0.0757129
+6 30 -0.0447633
+6 20 -4.06592e-12
+6 16 3.20165e-12
+6 15 6.5756e-10
+6 10 -7.54326e-08
+6 7 -2.63737e-05
+6 6 1.00104
+6 4 -1.16327e-07
+6 2 -1.77376e-08
 2 127 0
-127 127 1.02516
-1 126 0
+2 121 0
+2 111 0
+2 106 0
+2 102 0
+2 92 0
+2 91 0
+2 87 0
+2 86 0
+2 81 0
+2 77 -0.00747763
+2 72 -0.0180265
+2 71 -0.0292076
+2 62 -0.0683113
+2 57 -0.111914
+2 51 -0.612796
+2 42 -0.230306
+2 41 -1.21673
+2 36 -1.33127
+2 32 -0.162961
+2 27 -0.0830881
+2 20 -9.6173e-11
+2 16 -2.0827e-11
+2 15 -4.27748e-09
+2 10 -7.54326e-07
+2 7 -0.000163458
+2 6 -1.26628e-07
+2 4 -1.24529e-06
+2 2 1.00015
 2 126 0
-21 126 -46178.4
-126 126 1.02516
 2 122 0
-122 122 1.02516
-1 121 0
-2 121 0
-21 121 -46775.5
-121 121 1.02516
-1 116 0
+2 117 0
 2 116 0
-21 116 -47365.9
 2 112 0
 2 107 0
-1 106 0
-2 106 0
-21 106 -48525.5
-1 101 0
 2 101 0
-21 101 -49094
-101 101 1.02517
 2 97 0
-1 91 0
-2 91 0
-21 91 -50207.6
-11 75 0
-75 75 1.02719
-2 72 -0.0180265
-11 68 0
-1 66 0.0972206
+2 96 0
+2 82 -0.00434723
+2 76 -0.0115763
+2 67 -0.0370897
 2 66 -0.085068
-11 66 0
-21 66 -52841
-66 66 1.11004
-11 63 0
-1 61 0.191204
 2 61 -0.185229
-11 61 0
-21 61 -53339.9
-11 55 -0.0186851
+2 56 -0.361834
 2 52 -0.162573
-1 46 0.962069
+2 47 -0.207741
 2 46 -0.930236
-21 46 -54775.5
-1 41 1.18844
-2 41 -1.21673
-21 41 -55232.9
 2 37 -0.216264
-37 37 0.808893
-2 32 -0.162961
-32 32 0.862197
-1 31 1.03677
 2 31 -1.19876
-11 31 -0.172794
-21 31 -56114.8
-31 31 2.21556
-11 28 7.18827e-05
-28 28 1.04909
-2 27 -0.0830881
-27 27 0.94207
-21 21 1
-1 18 -0.00421819
+2 26 -0.720168
 2 18 -0.000307043
-8 18 0.00407312
+2 17 -4.63661e-07
+2 11 -4.20342e-09
+2 9 -1.23894e-12
+2 8 -1.59002e-07
+2 5 -3.46404e-07
+2 3 -0.000428884
+2 1 -6.31029e-07
+6 125 0
+6 120 0
+6 110 0
+6 100 0
+6 95 0
+6 90 0.0037198
+6 70 -0.00513978
+6 65 -0.0115367
+6 40 -0.0834993
+6 35 -0.0740706
+6 18 -4.57016e-05
+6 17 6.54493e-08
+6 11 -4.20342e-09
+6 9 0
+6 8 -1.5896e-07
+6 5 -3.228e-06
+6 3 -2.99542e-08
+6 1 6.19435e-10
+7 18 -0.00242886
+7 13 2.18493e-11
+7 11 2.52205e-08
+7 9 -1.2303e-06
+7 8 -4.0753e-05
+7 5 5.63789e-08
+7 3 5.6379e-08
+7 1 5.6379e-08
+9 18 0.00417594
+9 13 -1.09247e-11
+9 9 1
+9 8 4.17067e-05
+9 5 -5.63789e-08
+9 3 -5.6379e-08
+9 1 -5.6379e-08
 10 18 0.000924955
-11 18 0.000822131
-15 18 -0.0247516
-26 18 -2.47109e-17
+10 8 -6.30801e-06
+10 5 0
+10 3 0
+10 1 0
+14 18 0.0374802
+14 13 1.09247e-11
+14 11 2.52205e-07
+14 9 1.10727e-05
+14 8 0.000504751
+14 5 -5.07411e-07
+14 3 -5.07411e-07
+14 1 -5.07411e-07
+18 68 2.17843e-06
+18 63 2.78937e-06
+18 56 0
+18 46 0
+18 40 0
+18 35 0
+18 18 1.00002
+18 17 1.93124e-09
+18 13 1.38886e-13
+18 11 2.55451e-10
+18 9 -2.20048e-09
+18 8 4.60627e-08
+18 5 9.14862e-07
+18 3 -1.14481e-06
+18 1 -2.04393e-09
+25 125 -39605.7
+25 120 -40147.8
+25 110 -41210.5
+25 100 -42242.5
+25 95 -42746.3
+25 90 -43241.8
+25 70 -45135.3
+25 65 -45585.4
+25 40 -47682.8
+25 35 -48070.2
+25 25 1
 27 18 -1.87319e-13
-28 18 4.88866e-14
+27 3 -1.88902e-12
 29 18 -3.04399e-16
+29 5 -4.97786e-14
+30 18 -1.28689e-16
+30 5 8.30179e-15
+31 31 2.21556
 31 18 -2.79085e-17
-32 18 -3.3199e-10
-33 18 3.13952e-10
-34 18 -4.00604e-16
-36 18 -3.12504e-17
+31 1 6.84626e-16
+37 37 0.808893
 37 18 -9.12322e-08
-47 18 -6.09967e-08
-48 18 -0.000523836
-57 18 -3.72001e-12
+37 3 -8.3646e-07
+40 40 1.10865
+40 18 -1.97671e-16
+40 5 2.60058e-14
+41 18 -3.49479e-17
+41 1 8.82948e-16
+42 18 -5.19145e-07
+42 3 -4.5648e-06
+50 18 -2.41995e-16
+50 5 2.40613e-14
+52 52 0.862585
+52 18 -6.18966e-10
+52 3 -5.07647e-09
+53 18 -2.59383e-06
+53 3 3.74969e-05
+55 18 -2.48875e-16
+55 5 1.72311e-14
+58 18 -1.77629e-09
+58 3 1.86517e-08
+59 18 -6.51794e-16
+59 5 -7.39662e-14
+66 66 1.11004
 66 18 -5.0698e-17
+66 1 9.14985e-17
+67 67 0.988015
 67 18 -4.5482e-15
+67 3 -3.32747e-14
+71 18 -4.29686e-17
+71 1 3.14761e-17
 75 18 -7.04231e-17
-77 18 -2.77506e-16
+75 5 3.59641e-16
+78 18 -4.05664e-16
+78 3 4.8811e-16
 81 18 -1.30875e-17
-96 18 -4.19735e-19
-101 18 -1.04115e-19
+81 1 1.13072e-18
+90 90 1.0252
+90 18 -5.4046e-18
+90 5 1.38872e-18
+97 97 1.02506
+97 18 -2.62597e-18
+97 3 -1.68776e-17
+100 100 1.02516
+100 18 -5.77741e-19
+100 5 1.24377e-20
 121 18 -1.40561e-22
-122 18 -1.06721e-21
-126 18 -2.09008e-23
-127 18 -1.66237e-22
-1 17 -6.47378e-08
-2 17 -4.63661e-07
-11 17 0
-8 14 1.34533e-07
-10 14 -6.72665e-08
-11 14 6.72665e-08
-15 14 -3.36333e-07
-11 13 1.09247e-11
-15 13 2.16309e-10
-11 12 1.23862e-11
-15 12 2.45248e-10
-1 11 -4.20342e-09
-2 11 -4.20342e-09
-8 11 2.52205e-08
-11 11 1
-15 11 2.26985e-07
-1 8 -4.18657e-05
-2 8 -1.59002e-07
-8 8 1.00006
-10 8 -6.30801e-06
-11 8 7.26177e-06
-15 8 -0.000473389
-1 6 1.47134e-07
-2 6 -1.26628e-07
-8 6 -5.63709e-08
-10 6 -8.08598e-12
-11 6 -4.54269e-14
-15 6 6.0878e-07
-75 6 -1.07803e-14
-1 3 3.17213e-06
-2 3 -0.000428884
-8 3 -5.6379e-08
-10 3 0
-11 3 -1.144e-15
-15 3 6.08893e-07
-27 3 -1.88902e-12
-28 3 2.47367e-12
-32 3 -3.19181e-09
-33 3 4.2654e-08
-37 3 -8.3646e-07
-47 3 -5.16888e-07
-48 3 0.0113199
-57 3 -2.96339e-11
-67 3 -3.32747e-14
-77 3 -1.83468e-15
+121 1 3.76456e-29
+130 18 -7.39407e-23
+130 5 9.35515e-29
+124 5 -2.98685e-20
+124 18 -1.0253e-21
+124 124 1.02516
 122 3 -6.85766e-21
-127 3 -1.0682e-21
-1 1 1
-2 1 -6.31029e-07
-8 1 -5.6379e-08
-10 1 0
-11 1 0
-15 1 6.08893e-07
-26 1 3.62494e-16
-31 1 6.84626e-16
-36 1 8.68257e-16
-66 1 9.14985e-17
-81 1 1.13072e-18
-96 1 6.83349e-22
+122 18 -1.06721e-21
+122 122 1.02516
+119 5 -1.59427e-19
+119 18 -5.47265e-21
+119 119 1.02516
+114 5 -7.74007e-19
+114 18 -2.6569e-20
+108 3 1.17254e-22
+108 18 -2.45815e-19
+108 108 1.02515
+107 3 -1.00072e-18
+107 18 -1.55734e-19
+107 107 1.02515
+106 1 1.58898e-24
+106 18 -2.32651e-20
+104 5 -1.37158e-17
+104 18 -4.70628e-19
+102 3 -4.3187e-18
+102 18 -6.72056e-19
 101 1 3.65932e-23
-121 1 3.76456e-29
-126 1 7.17244e-31
+101 18 -1.04115e-19
+101 101 1.02517
+96 1 6.83349e-22
+96 18 -4.19735e-19
+96 96 1.02523
+89 5 -5.07976e-16
+89 18 -1.70672e-17
+89 89 1.0251
+88 3 5.64556e-18
+88 18 -4.26509e-17
+88 88 1.024
+82 3 -5.99769e-16
+82 18 -9.24004e-17
+82 82 1.02207
+74 5 -9.66208e-15
+74 18 -2.30533e-16
+74 74 1.02848
+72 3 -6.36214e-15
+72 18 -9.24877e-16
+70 5 1.43718e-15
+70 18 -1.25924e-16
+70 70 1.03036
+69 5 -2.22072e-14
+69 18 -3.94321e-16
+69 69 1.03518
+65 5 4.3499e-15
+65 18 -1.87716e-16
+65 65 1.03674
+64 5 -4.40264e-14
+64 18 -5.52136e-16
+62 3 -4.67691e-13
+62 18 -6.05744e-14
+61 1 2.01005e-16
+61 18 -5.10312e-17
+61 61 1.21065
+49 5 -1.33059e-13
+49 18 -6.3223e-16
+49 49 1.17371
+48 3 0.0113199
+48 18 -0.000523836
+48 48 0.950959
+43 3 0.0381683
+43 18 -0.000996902
+43 43 0.97214
+38 3 0.00038885
+38 18 -3.24413e-06
+38 38 1.00692
+34 5 -9.87808e-14
+34 18 -4.00604e-16
+34 34 1.23118
+21 26 -56538.4
+21 31 -56114.8
+21 46 -54775.5
+21 56 -53828.6
+21 61 -53339.9
+21 66 -52841
+21 76 -51814.9
+21 96 -49654.8
+21 101 -49094
+21 116 -47365.9
+21 126 -46178.4
+20 1 0.0187834
+20 3 10.5206
+20 5 -8.40741
+20 8 -0.44093
+20 9 0.020222
+20 11 -0.00240875
+20 13 -1.30285e-06
+20 17 -0.0177478
+20 18 -101.449
+20 26 0
+20 31 0
+20 34 0
+20 35 0
+20 37 -43.2607
+20 38 -3.64635
+20 40 0
+20 43 -14.3108
+20 46 0
+20 47 -71.8441
+20 48 -25.3546
+20 49 0
+20 52 -68.5102
+20 56 0
+20 61 0
+20 63 -25.5022
+20 65 0
+20 66 0
+20 67 0
+20 68 -18.274
+20 69 0
+20 70 0
+20 73 0
+20 74 0
+20 76 0
+20 82 0
+20 83 0
+20 84 0
+20 88 0
+20 89 0
+20 90 0
+20 93 0
+20 95 0
+20 96 0
+20 97 0
+20 98 0
+20 99 0
+20 100 0
+20 101 0
+20 107 0
+20 108 0
+20 109 0
+20 110 0
+20 112 0
+20 113 0
+20 116 0
+20 117 0
+20 119 0
+20 120 0
+20 122 0
+20 124 0
+20 125 0
+20 126 0
+20 129 0
+19 1 0
+19 3 0
+19 5 0
+19 8 7.26177e-06
+19 9 0
+19 11 2.52205e-08
+19 13 1.09247e-11
+19 18 0.000822131
+17 1 -5.6379e-08
+17 3 -5.6379e-08
+17 5 -5.63789e-08
+17 8 5.52765e-05
+17 9 1.2303e-06
+17 11 2.52205e-08
+17 17 1
+17 18 0.00407312
diff --git a/tests/m300_arc130.mtx-u8 b/tests/m300_arc130.mtx-u8
index 0378b4ae7c43654cf5b7feddced016e1d93b985a..15c9a33e701d2c681027007981f3d60f373f85a4 100644
--- a/tests/m300_arc130.mtx-u8
+++ b/tests/m300_arc130.mtx-u8
@@ -1,131 +1,131 @@
 130 8
-1 8
+1 2
 2 7
-3 5
-4 6
-5 4
-6 1
-7 4
-8 8
-9 5
-10 7
-11 8
+3 1
+4 4
+5 3
+6 6
+7 5
+8 1
+9 7
+10 6
+11 2
 12 4
-13 1
-14 6
-15 7
-16 7
-17 3
-18 4
-19 1
-20 3
+13 3
+14 7
+15 2
+16 2
+17 8
+18 6
+19 6
+20 5
 21 8
-22 5
-23 2
-24 6
-25 1
-26 7
-27 8
-28 7
-29 8
-30 4
+22 1
+23 4
+24 3
+25 7
+26 2
+27 7
+28 4
+29 5
+30 6
 31 7
-32 8
-33 7
+32 1
+33 2
 34 8
-35 1
-36 7
-37 8
-38 5
-39 2
-40 4
-41 2
-42 4
-43 3
-44 4
-45 2
-46 4
-47 7
+35 3
+36 2
+37 7
+38 6
+39 4
+40 5
+41 6
+42 7
+43 5
+44 3
+45 4
+46 2
+47 1
 48 8
-49 6
-50 2
-51 3
-52 3
+49 8
+50 5
+51 2
+52 7
 53 5
-54 6
-55 1
+54 3
+55 7
 56 2
-57 7
-58 5
-59 6
-60 1
-61 3
-62 3
-63 1
+57 1
+58 7
+59 5
+60 4
+61 6
+62 8
+63 3
 64 5
-65 3
-66 8
-67 7
-68 1
+65 8
+66 7
+67 6
+68 4
 69 6
-70 2
-71 3
-72 3
+70 5
+71 7
+72 8
 73 2
-74 5
-75 8
-76 1
-77 7
-78 6
+74 8
+75 6
+76 2
+77 1
+78 5
 79 3
-80 1
-81 8
-82 3
-83 2
-84 5
+80 4
+81 7
+82 6
+83 1
+84 3
 85 4
-86 3
-87 3
-88 2
-89 2
-90 3
+86 2
+87 1
+88 5
+89 6
+90 6
 91 2
-92 4
+92 1
 93 3
-94 6
-95 2
-96 7
-97 5
-98 6
-99 2
-100 4
-101 8
-102 5
-103 6
+94 4
+95 3
+96 8
+97 7
+98 4
+99 3
+100 5
+101 5
+102 8
+103 1
 104 5
-105 1
-106 2
-107 6
-108 4
-109 5
+105 4
+106 8
+107 5
+108 8
+109 3
 110 4
-111 3
-112 6
-113 5
-114 1
-115 4
-116 1
-117 6
-118 2
-119 1
-120 1
+111 2
+112 1
+113 1
+114 5
+115 3
+116 2
+117 1
+118 1
+119 8
+120 4
 121 7
-122 8
-123 5
-124 6
-125 4
-126 7
-127 7
-128 5
-129 6
-130 2
+122 5
+123 1
+124 8
+125 3
+126 2
+127 1
+128 2
+129 3
+130 7
diff --git a/tests/m300_arc130.mtx-v8 b/tests/m300_arc130.mtx-v8
index 6914c4ba3a73e8e8c41cb41c7886936302b06370..d4e628f543300bd95434f31aa3ddb752fec5e934 100644
--- a/tests/m300_arc130.mtx-v8
+++ b/tests/m300_arc130.mtx-v8
@@ -1,131 +1,131 @@
 130 8
-1 6
-2 5
-3 6
-4 4
-5 6
-6 8
-7 7
-8 8
-9 2
-10 5
+1 7
+2 6
+3 7
+4 6
+5 7
+6 6
+7 6
+8 7
+9 3
+10 6
 11 3
-12 8
-13 1
-14 6
-15 4
-16 7
+12 2
+13 2
+14 3
+15 3
+16 4
 17 4
-18 1
-19 1
+18 3
+19 6
 20 4
-21 8
-22 5
-23 3
-24 5
-25 4
-26 2
-27 5
-28 1
-29 6
-30 2
-31 2
-32 8
-33 3
-34 7
-35 3
-36 2
-37 5
-38 3
+21 5
+22 1
+23 4
+24 3
+25 7
+26 8
+27 1
+28 3
+29 5
+30 5
+31 8
+32 1
+33 4
+34 8
+35 8
+36 5
+37 1
+38 1
 39 5
-40 2
-41 3
+40 8
+41 5
 42 1
-43 7
-44 7
-45 2
+43 2
+44 5
+45 5
 46 8
-47 5
-48 7
-49 1
-50 3
-51 3
-52 8
-53 2
-54 1
-55 8
-56 1
-57 7
+47 1
+48 2
+49 8
+50 5
+51 5
+52 1
+53 1
+54 5
+55 2
+56 8
+57 1
 58 6
-59 4
-60 2
-61 3
-62 7
-63 4
-64 1
-65 2
-66 3
-67 7
-68 5
-69 6
-70 2
-71 3
-72 8
-73 5
-74 1
-75 4
-76 3
-77 6
-78 2
+59 5
+60 5
+61 8
+62 1
+63 7
+64 5
+65 8
+66 8
+67 1
+68 4
+69 8
+70 8
+71 5
+72 1
+73 2
+74 8
+75 2
+76 8
+77 1
+78 1
 79 5
-80 3
-81 2
-82 3
-83 2
-84 5
-85 2
-86 3
-87 7
-88 3
-89 4
-90 2
-91 2
-92 2
-93 2
-94 6
-95 3
+80 5
+81 5
+82 1
+83 1
+84 8
+85 5
+86 5
+87 1
+88 1
+89 8
+90 8
+91 5
+92 1
+93 8
+94 5
+95 7
 96 2
-97 6
-98 3
-99 1
+97 7
+98 4
+99 8
 100 3
 101 2
-102 7
+102 6
 103 3
-104 4
-105 3
+104 5
+105 6
 106 2
-107 8
-108 2
-109 4
-110 2
-111 3
-112 8
-113 2
-114 6
-115 2
-116 3
-117 5
-118 2
-119 6
+107 7
+108 4
+109 8
+110 3
+111 2
+112 7
+113 4
+114 5
+115 6
+116 2
+117 7
+118 4
+119 8
 120 3
 121 2
-122 5
+122 7
 123 3
-124 1
+124 8
 125 3
-126 3
-127 8
+126 2
+127 6
 128 2
-129 3
-130 2
+129 8
+130 6
diff --git a/tests/runtest b/tests/runtest
index 9cd19bc7fa706299c4bd81e3343c87947450b85c..226a9dcb775b19b257c1f73ba19e3749bbef7d73 100755
--- a/tests/runtest
+++ b/tests/runtest
@@ -131,7 +131,14 @@ test_DetermineSplit
 test_logb2
 test_SplitMatrixSimple
 test_SplitMatrixKLFM
+test_SplitMatrixUpperBound
 test_DistributeMatrixMondriaan
+test_DetectConnectedComponents
+test_Heap
+test_TwoColorTree
+test_SubsetSum
+test_ZeroVolumeSearch
+test_FreeNonzeros
 
 # Test of Graph
 test_CreateNewBiPartHyperGraph
@@ -141,6 +148,7 @@ test_BiPartHyperGraph2SparseMatrix
 
 # Test of SparseMatrix 
 test_SparseMatrixSymmetric2Full
+test_SparseMatrixFull2Symmetric
 test_SparseMatrixSymmetricLower2Random
 test_SparseMatrixSymmetricRandom2Lower
 test_AddDummiesToSparseMatrix
@@ -193,6 +201,7 @@ test_SpMatSortNonzeros
 test_SpMatReadIndexAndValueMatrixFiles
 test_SparseMatrixRemoveDuplicates
 test_SparseMatrixStructurallySymmetric
+test_SparseMatrixToCRS_CCS
 
 # Test of Cartesian
 test_CRS2CCS
diff --git a/tests/testHelper_DisconnectedMatrix.c b/tests/testHelper_DisconnectedMatrix.c
new file mode 100644
index 0000000000000000000000000000000000000000..5cfae1c083d5168156eedfbac304b84ba1a86c1c
--- /dev/null
+++ b/tests/testHelper_DisconnectedMatrix.c
@@ -0,0 +1,407 @@
+#include "testHelper_DisconnectedMatrix.h"
+
+#define min(x,y) ( ( (x)<(y) ) ? (x) : (y) )
+#define max(x,y) ( ( (x)>(y) ) ? (x) : (y) )
+
+
+/**
+ * Construct a random (disconnected) matrix.
+ * 
+ * Input:
+ * symmetric         : Whether the matrix should be symmetric
+ * dummies           : Whether the matrix should contain dummy diagonal nonzeros
+ * colWeights        : Whether the matrix should be weighted with column weights
+ * numCompMin        : Minimum number of connected components
+ * numCompMax        : Maximum number of connected components
+ * 
+ * Output:
+ * pA                : The generated matrix
+ * pNumComponents    : The number of components generated
+ * pComponent_m      : Height of each connected component
+ * pComponent_n      : Width of each connected component
+ * pComponent_weights: Weight of each connected component (effective number of nonzeros or sum of column weights)
+ * p_i_to_I          : I = p_i_to_I[cmpnnt][i] is the row index I in the large matrix which corresponds to the row index i in component cmpnnt
+ * p_j_to_J          : J = p_j_to_J[cmpnnt][j] is the column index J in the large matrix which corresponds to the column index j in component cmpnnt
+ * 
+ * Returned memory allocations (to be freed with DestructDisconnectedMatrix()):
+ *     pA:
+ *         pA->i             O(pA->m)
+ *         pA->j             O(pA->n)
+ *         pA->Pstart        O(2+1)
+ *         pA->dummy         O(pA->m)            (only if dummy == TRUE)
+ *         pA->ColWeights    O(pA->n)            (only if colWeights == TRUE)
+ *     pComponent_m:         O(numComponents)
+ *     pComponent_n:         O(numComponents)    (only if symmetric == FALSE)
+ *     pComponent_weights:   O(numComponents)
+ *     p_i_to_I:             O(numComponents)
+ *     p_i_to_I[0]:          O(pA->m)
+ *     p_j_to_J:             O(numComponents)    (only if symmetric == FALSE)
+ *     p_j_to_J[0]:          O(pA->n)            (only if symmetric == FALSE)
+ */
+void ConstructDisconnectedMatrix(struct sparsematrix *pA, int symmetric, int dummies, int colWeights,
+                                 long numCompMin, long numCompMax, long *pNumComponents,
+                                 long **pComponent_m, long **pComponent_n, long **pComponent_weights,
+                                 long ***p_i_to_I, long ***p_j_to_J) {
+    
+    long i, j, I, J, m, n;
+    long cmpnnt;
+    
+    /* Determine number of components (submatrices), the dimensions of each submatrix, and their numbers of nonzeros */
+    long numComponents = Random1(numCompMin, numCompMax);
+    long *component_dummies = (long *)malloc(numComponents*sizeof(long));
+    long *component_m = (long *)malloc(numComponents*sizeof(long));
+    long *component_n;
+    if(symmetric || dummies) {
+        component_n = component_m;
+    }
+    else {
+        component_n = (long *)malloc(numComponents*sizeof(long));
+    }
+    long *component_nnz = (long *)malloc(numComponents*sizeof(long));
+    
+    if (component_dummies == NULL || component_m  == NULL || component_n == NULL || component_nnz  == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    long M=0, N=0, NNZ=0, totalDummies = 0;
+    for(cmpnnt=0; cmpnnt<numComponents; ++cmpnnt) {
+        
+        /* component_nnz[] should uniquely identify a component; i.e. all components should have different nnz.
+         * This helps when identifying the components in test functions.
+         */
+        int present;
+        long nonDummyDiagNonZeros[2];
+        long full_nnz[2];
+        
+        do {
+            /* The dimensions should at least be above 10, such that generating the
+             * matrices won't take too long (or, when below 5, may become impossible).
+             * Additionally, we should have enough freedom to ensure each component has
+             * a unique number of nonzeros
+             */
+            component_m[cmpnnt] = Random1(14, 20) + Random1(numComponents, numCompMax);
+            if(!symmetric && !dummies) {
+                component_n[cmpnnt] = Random1(14, 20) + Random1(numComponents, numCompMax);
+            }
+            long maxdim = (component_m[cmpnnt]>component_n[cmpnnt]) ? component_m[cmpnnt] : component_n[cmpnnt];
+            
+            /* Determine number of nonzeros */
+            if(symmetric) {
+                component_nnz[cmpnnt] = 2*maxdim-1 + Random1(maxdim/2, maxdim);
+            }
+            else {
+                component_nnz[cmpnnt] = 2*maxdim-1 + Random1(maxdim, 2*maxdim);
+            }
+            
+            component_dummies[cmpnnt] = dummies ? Random1(maxdim/4, 3*maxdim/4) : 0;
+            component_nnz[cmpnnt] -= component_dummies[cmpnnt];
+            
+            /* Check that this number of non-dummy nonzeros is unique */
+            present = 0;
+            for(i=0; i<cmpnnt; ++i) {
+                if(symmetric) {
+                    nonDummyDiagNonZeros[0] = component_m[cmpnnt] - component_dummies[cmpnnt];
+                    full_nnz[0] = (component_nnz[cmpnnt]-nonDummyDiagNonZeros[0])*2 + nonDummyDiagNonZeros[0];
+                    
+                    nonDummyDiagNonZeros[1] = component_m[i] - component_dummies[i];
+                    full_nnz[1] = (component_nnz[i]-nonDummyDiagNonZeros[1])*2 + nonDummyDiagNonZeros[1];
+                    
+                    if(full_nnz[0] == full_nnz[1]) {
+                        present = 1;
+                        break;
+                    }
+                }
+                else {
+                    
+                    if(component_nnz[i] == component_nnz[cmpnnt]) {
+                        present = 1;
+                        break;
+                    }
+                }
+            }
+        }
+        while(present);
+        
+        /* Update totals */
+        M += component_m[cmpnnt];
+        N += component_n[cmpnnt];
+        NNZ += component_nnz[cmpnnt];
+        totalDummies += component_dummies[cmpnnt];
+    }
+    
+    /* Allocate space for a mapping from submatrix indices (i,j) to global indices (I,J) */
+    long *i_to_I_alloc, *j_to_J_alloc, **i_to_I, **j_to_J;
+    i_to_I_alloc = (long *)malloc(M*sizeof(long));
+    i_to_I = (long **)malloc(numComponents*sizeof(long *));
+    if(symmetric || dummies) {
+        j_to_J_alloc = i_to_I_alloc;
+        j_to_J = i_to_I;
+    }
+    else {
+        j_to_J_alloc = (long *)malloc(N*sizeof(long));
+        j_to_J = (long **)malloc(numComponents*sizeof(long *));
+    }
+    
+    if (i_to_I_alloc == NULL || i_to_I  == NULL || j_to_J_alloc == NULL || j_to_J  == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    i_to_I[0] = i_to_I_alloc;
+    if(!symmetric && !dummies)
+        j_to_J[0] = j_to_J_alloc;
+    for(cmpnnt=1; cmpnnt<numComponents; ++cmpnnt) {
+        i_to_I[cmpnnt] = &i_to_I[cmpnnt-1][component_m[cmpnnt-1]];
+        if(!symmetric && !dummies)
+            j_to_J[cmpnnt] = &j_to_J[cmpnnt-1][component_n[cmpnnt-1]];
+    }
+    
+    /* Create a permutation of the identity */
+    for(I=0; I<M; ++I) {
+        i_to_I_alloc[I] = I;
+    }
+    if(!symmetric && !dummies) {
+        for(J=0; J<N; ++J) {
+            j_to_J_alloc[J] = J;
+        }
+    }
+    
+    RandomPermute(i_to_I_alloc, NULL, NULL, NULL, 0, M-1);
+    if(!symmetric && !dummies)
+        RandomPermute(j_to_J_alloc, NULL, NULL, NULL, 0, N-1);
+    
+    /* Set up matrix struct */
+    MMSparseMatrixInit(pA);
+    pA->m = M;
+    pA->n = N;
+    pA->NrNzElts = NNZ + totalDummies;
+    pA->NrProcs = 2; /* maximum number of parts */
+
+    pA->i = (long *)malloc(pA->NrNzElts*sizeof(long));
+    pA->j = (long *)malloc(pA->NrNzElts*sizeof(long));
+    pA->Pstart = (long *)malloc((pA->NrProcs+1)*sizeof(long));
+    pA->RowLambda = (int *)malloc(pA->m*sizeof(int));
+    pA->ColLambda = (int *)malloc(pA->n*sizeof(int));
+    
+    if (pA->i == NULL || pA->j == NULL || pA->Pstart == NULL || pA->RowLambda == NULL || pA->ColLambda == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    if(colWeights)
+        pA->MMTypeCode[0]='W'; /* weighted matrix */
+    else
+        pA->MMTypeCode[0]='M'; /* normal matrix */
+    pA->MMTypeCode[1]='C'; /* coordinate scheme */
+    pA->MMTypeCode[2]='P'; /* pattern only */
+    if(symmetric)
+        pA->MMTypeCode[3]='S'; /* symmetric */
+    else
+        pA->MMTypeCode[3]='G'; /* general, no symmetry */
+    pA->NrDummies = 0;
+    pA->dummy = NULL;
+    pA->Pstart[0] = 0;
+    pA->Pstart[1] = pA->NrNzElts;
+    pA->Pstart[2] = pA->NrNzElts;
+    pA->NrDummies = totalDummies;
+    
+    if(dummies) {
+        pA->dummy = (int *)malloc(pA->m*sizeof(int));
+        if (pA->dummy == NULL) {
+            printf("Error\n");
+            exit(1);
+        }
+    }
+    
+    /* Generate each component */
+    long maxdim, t = 0, t2, T, nnzFilled, dummiesUsed;
+    
+    for(cmpnnt=0; cmpnnt<numComponents; ++cmpnnt) {
+        m = component_m[cmpnnt];
+        n = component_n[cmpnnt];
+        maxdim = (m>n) ? m : n;
+        nnzFilled = 0;
+        dummiesUsed = 0;
+        T = t;
+        
+        /* Make sure we create connected components */
+        if(Random1(0,1) == 0) {
+            /* Here, we generate a submatrix with nonzero diagonal
+             * and one nonzero subdiagonal. This way, each row is
+             * connected with the next, and hence all are connected.
+             */
+            for(i=0; i<maxdim; ++i) {
+                I = i_to_I[cmpnnt][i%m];
+                J = j_to_J[cmpnnt][i%n];
+                pA->i[t] = symmetric ? max(I,J) : I;
+                pA->j[t] = symmetric ? min(I,J) : J;
+                ++t;
+                ++nnzFilled;
+                
+                if(dummies) {
+                    pA->dummy[I] = (dummiesUsed < component_dummies[cmpnnt]) ? 1 : 0;
+                    if(pA->dummy[I] == 1) {
+                        ++dummiesUsed;
+                        --nnzFilled;
+                    }
+                }
+                
+                if(i < maxdim-1) {
+                    I = i_to_I[cmpnnt][(i+1)%m];
+                    J = j_to_J[cmpnnt][i%n];
+                    pA->i[t] = symmetric ? max(I,J) : I;
+                    pA->j[t] = symmetric ? min(I,J) : J;
+                    ++t;
+                    ++nnzFilled;
+                }
+            }
+        }
+        else {
+            /* Here, we generate a submatrix with nonzero diagonal
+             * and a dense first column. This way, all rows are
+             * connected with the first column, hence all rows and
+             * columns are connected.
+             */
+            for(i=0; i<maxdim; ++i) {
+                I = i_to_I[cmpnnt][i%m];
+                J = j_to_J[cmpnnt][i%n];
+                pA->i[t] = symmetric ? max(I,J) : I;
+                pA->j[t] = symmetric ? min(I,J) : J;
+                ++t;
+                ++nnzFilled;
+                
+                if(dummies) {
+                    pA->dummy[I] = (dummiesUsed < component_dummies[cmpnnt]) ? 1 : 0;
+                    if(pA->dummy[I] == 1) {
+                        ++dummiesUsed;
+                        --nnzFilled;
+                    }
+                }
+                
+                if(i > 0) {
+                    I = i_to_I[cmpnnt][i%m];
+                    J = j_to_J[cmpnnt][0];
+                    pA->i[t] = symmetric ? max(I,J) : I;
+                    pA->j[t] = symmetric ? min(I,J) : J;
+                    ++t;
+                    ++nnzFilled;
+                }
+            }
+        }
+        
+        /* Fill the submatrix until the desired number of nonzeros */
+        while(nnzFilled < component_nnz[cmpnnt]) {
+            i = Random1(0, m-1);
+            j = Random1(0, n-1);
+            I = i_to_I[cmpnnt][i%m];
+            J = j_to_J[cmpnnt][j%n];
+            
+            I = symmetric ? max(I,J) : I;
+            J = symmetric ? min(I,J) : J;
+            
+            /* Make sure this element is not already nonzero.
+             * This search is expensive in terms of complexity, but
+             * as we are just testing, this does not matter very much.
+             */
+            int present = 0;
+            for(t2=T; t2<t; ++t2) {
+                if(pA->i[t2] == I && pA->j[t2] == J) {
+                    present = 1;
+                    break;
+                }
+            }
+            if(present) {
+                continue;
+            }
+            
+            pA->i[t] = I;
+            pA->j[t] = J;
+            ++t;
+            ++nnzFilled;
+        }
+    }
+    
+    if(symmetric) {
+        /* Update nonzero count, as we now only have counted the elements in the lower-left triangle */
+        NNZ = 0;
+        long nonDummyDiagNonZeros;
+        for(cmpnnt=0; cmpnnt<numComponents; ++cmpnnt) {
+            nonDummyDiagNonZeros = component_m[cmpnnt] - component_dummies[cmpnnt];
+            component_nnz[cmpnnt] = (component_nnz[cmpnnt]-nonDummyDiagNonZeros)*2 + nonDummyDiagNonZeros;
+            NNZ += component_nnz[cmpnnt];
+        }
+    }
+    
+    /* Return value pointers */
+    *pNumComponents = numComponents;
+    *pComponent_m = component_m;
+    *pComponent_n = component_n;
+    *pComponent_weights = component_nnz;
+    *p_i_to_I = i_to_I;
+    *p_j_to_J = j_to_J;
+    
+    
+    if(colWeights) {
+        /* Reassign weights based on columns */
+        long *component_weights = *pComponent_weights;
+        pA->NrColWeights = N;
+        pA->ColWeights = malloc( N * sizeof( long ) );
+        if (pA->ColWeights == NULL) {
+            printf("Error\n");
+            exit(1);
+        }
+        for(J=0; J<N; ++J) {
+            pA->ColWeights[J] = Random1(20, 50);
+        }
+        
+        for(cmpnnt=0; cmpnnt<numComponents; ++cmpnnt) {
+            component_weights[cmpnnt] = 0;
+            for(j=0; j<component_n[cmpnnt]; ++j) {
+                component_weights[cmpnnt] += pA->ColWeights[j_to_J[cmpnnt][j]];
+            }
+            
+            /* Make sure each component has a unique weight, to be able to use it as identification */
+            while(TRUE) {
+                int found = FALSE;
+                for(i=0; i<cmpnnt; ++i) {
+                    if(component_weights[cmpnnt] == component_weights[i]) {
+                        found = TRUE;
+                        ++pA->ColWeights[j_to_J[cmpnnt][0]];
+                        ++component_weights[cmpnnt];
+                        break;
+                    }
+                }
+                if(!found) {
+                    break;
+                }
+            }
+        }
+        
+    }
+    
+    free(component_dummies);
+    
+} /* end ConstructDisconnectedMatrix */
+
+
+/**
+ * Free memory for a matrix constructed with ConstructDisconnectedMatrix().
+ * See ConstructDisconnectedMatrix() for details on the parameters.
+ */
+void DestructDisconnectedMatrix(struct sparsematrix *pA, int symmetric, int dummies, int colWeights,
+                                long **pComponent_m, long **pComponent_n, long **pComponent_weights,
+                                long ***p_i_to_I, long ***p_j_to_J) {
+    free(*pComponent_weights);
+    free(*pComponent_m);
+    free(*p_i_to_I[0]);
+    free(*p_i_to_I);
+    if(!symmetric && !dummies) {
+        free(*pComponent_n);
+        free(*p_j_to_J[0]);
+        free(*p_j_to_J);
+    }
+    
+    MMSparseMatrixFreeMemory(pA);
+    
+} /* end DestructDisconnectedMatrix */
diff --git a/tests/testHelper_DisconnectedMatrix.h b/tests/testHelper_DisconnectedMatrix.h
new file mode 100644
index 0000000000000000000000000000000000000000..ddc1f8bb5b40c2a864ecbb9c0145d544860892aa
--- /dev/null
+++ b/tests/testHelper_DisconnectedMatrix.h
@@ -0,0 +1,11 @@
+#include "Sort.h"
+#include "SparseMatrix.h"
+
+void ConstructDisconnectedMatrix(struct sparsematrix *pA, int symmetric, int dummies, int colWeights,
+                                 long numCompMin, long numCompMax, long *pNumComponents,
+                                 long **pComponent_m, long **pComponent_n, long **pComponent_weights,
+                                 long ***p_i_to_I, long ***p_j_to_J);
+
+void DestructDisconnectedMatrix(struct sparsematrix *pA, int symmetric, int dummies, int colWeights,
+                                long **pComponent_m, long **pComponent_n, long **pComponent_weights,
+                                long ***p_i_to_I, long ***p_j_to_J);
diff --git a/tests/test_BucketDeleteMax.c b/tests/test_BucketDeleteMax.c
index 3d8bdfdca7a560f7a963ece68415c3b7f9765041..775e41f1965a90fd09a13c329ed2241c1beae31d 100644
--- a/tests/test_BucketDeleteMax.c
+++ b/tests/test_BucketDeleteMax.c
@@ -12,9 +12,15 @@ int main(int argc, char **argv) {
     bs = 5; /* bucket size */
     offset = n/2 ;
 
-    /* Insert vertices in increasing order of gain value */
     GB.NrBuckets =0 ;
     GB.Root = NULL ;
+    
+    if ( !InitGainBucket(&GB, n/2) ) {
+        printf("Error\n");
+        exit(1);
+    }
+
+    /* Insert vertices in increasing order of gain value */
     for (j=0; j<bs; j++)
         for (i=0; i<n; i++)
             BucketInsert(&GB, i-offset, j*n + i) ;
@@ -32,7 +38,11 @@ int main(int argc, char **argv) {
 
         }
     }
-
+    
+    if ( !DeleteGainBucket(&GB) ) {
+        printf("Error\n");
+        exit(1);
+    }
     
     if (GB.Root != NULL || GB.NrBuckets != 0){
         printf("Error\n") ;
@@ -55,7 +65,11 @@ void CheckBuckets(struct gainbucket GB, long n, long bs) {
         exit(1);
     }
 
+#ifdef GAINBUCKET_ARRAY
+    pB = &(GB.Root[GB.MaxValue+GB.MaxPresentValue]) ;
+#else
     pB = GB.Root ;
+#endif
 
     offset = n/2 ; 
     i = 0;
@@ -75,7 +89,11 @@ void CheckBuckets(struct gainbucket GB, long n, long bs) {
             j++ ;
             pE = pE->next ;
         }
+#ifdef GAINBUCKET_ARRAY
+        pB = (pB->value > -GB.MaxValue) ? pB-1 : NULL ;
+#else
         pB = pB->next ;
+#endif
         if (j!= bs){  
             printf("Error\n") ;
             exit(1); 
diff --git a/tests/test_BucketInsert.c b/tests/test_BucketInsert.c
index cf54503e40bc1855d6b05bab0cd575d1bb41c772..f67cbac4bad1534a32d2ad247598649d0e4aa8c5 100644
--- a/tests/test_BucketInsert.c
+++ b/tests/test_BucketInsert.c
@@ -12,15 +12,26 @@ int main(int argc, char **argv) {
     bs = 5; /* bucket size */
     offset = n/2 ;
 
-    /* Insert vertices in increasing order of gain value */
     GB.NrBuckets =0 ;
     GB.Root = NULL ;
+    
+    if ( !InitGainBucket(&GB, n/2) ) {
+        printf("Error\n");
+        exit(1);
+    }
+
+    /* Insert vertices in increasing order of gain value */
     for (j=0; j<bs; j++)
         for (i=0; i<n; i++)
             BucketInsert(&GB, i-offset, j*n + i) ;
 
     CheckBuckets(GB, n, bs) ;
     
+    if ( !DeleteGainBucket(&GB) ) {
+        printf("Error\n");
+        exit(1);
+    }
+    
     printf("OK\n") ;
     exit(0);
 
@@ -37,7 +48,11 @@ void CheckBuckets(struct gainbucket GB, long n, long bs) {
         exit(1);
     }
 
+#ifdef GAINBUCKET_ARRAY
+    pB = &(GB.Root[GB.MaxValue+GB.MaxPresentValue]) ;
+#else
     pB = GB.Root ;
+#endif
 
     offset = n/2 ; 
     i = 0;
@@ -57,7 +72,11 @@ void CheckBuckets(struct gainbucket GB, long n, long bs) {
             j++ ;
             pE = pE->next ;
         }
+#ifdef GAINBUCKET_ARRAY
+        pB = (pB->value > -GB.MaxValue) ? pB-1 : NULL ;
+#else
         pB = pB->next ;
+#endif
         if (j!= bs){  
             printf("Error\n") ;
             exit(1); 
diff --git a/tests/test_BucketMove.c b/tests/test_BucketMove.c
index 8558e1ec38224931c25fca54b4846152e1c48f6b..344193c1f33a179c691dfd8faaf5cbd064d41e02 100644
--- a/tests/test_BucketMove.c
+++ b/tests/test_BucketMove.c
@@ -13,9 +13,15 @@ int main(int argc, char **argv) {
     n= 100 ; /* number of buckets */
     bs = 5; /* bucket size */
 
-    /* Insert vertices in increasing order of gain value */
     GB.NrBuckets =0 ;
     GB.Root = NULL ;
+    
+    if ( !InitGainBucket(&GB, n) ) {
+        printf("Error\n");
+        exit(1);
+    }
+
+    /* Insert vertices in increasing order of gain value */
     for (j=0; j<bs; j++)
         for (i=0; i<n; i++)
             BucketInsert(&GB, i, j*n + i) ;
@@ -24,15 +30,28 @@ int main(int argc, char **argv) {
        inside the buckets. These buckets are created at the end of the
        current bucket list */
 
+#ifdef GAINBUCKET_ARRAY
+    pB = &(GB.Root[GB.MaxValue+GB.MaxPresentValue]) ;
+#else
     pB = GB.Root ;
+#endif
     while ( pB != NULL && pB->value >= 0 ) {
         pE = pB->entry ; /* all buckets are nonempty */
         newkey = pB->value - n ;
         BucketMove(&GB, pE, newkey) ;
+#ifdef GAINBUCKET_ARRAY
+        pB = &(GB.Root[GB.MaxValue+GB.MaxPresentValue]) ;
+#else
         pB = GB.Root ;
+#endif
     }
 
     CheckBuckets2(GB, n, bs) ;
+    
+    if ( !DeleteGainBucket(&GB) ) {
+        printf("Error\n");
+        exit(1);
+    }
 
     printf("OK\n") ;
     exit(0);
@@ -50,7 +69,11 @@ void CheckBuckets2(struct gainbucket GB, long n, long bs) {
         exit(1); 
     }
  
+#ifdef GAINBUCKET_ARRAY
+    pB = &(GB.Root[GB.MaxValue+GB.MaxPresentValue]) ;
+#else
     pB = GB.Root ;
+#endif
  
     i = 0;
     while ( pB != NULL ) {
@@ -67,7 +90,11 @@ void CheckBuckets2(struct gainbucket GB, long n, long bs) {
             j++ ;
             pE = pE->next ;
         }
-        pB = pB->next ;  
+#ifdef GAINBUCKET_ARRAY
+        pB = (pB->value > -GB.MaxValue) ? pB-1 : NULL ;
+#else
+        pB = pB->next ;
+#endif
         if (j!= bs){  
             printf("Error\n") ;
             exit(1);
diff --git a/tests/test_ClearGainBucket.c b/tests/test_ClearGainBucket.c
index 7248b21bbff9c348986aac087d2b5872b6c35621..0d8ede1aa11973f2837922eae8a0e592f5d0d48a 100644
--- a/tests/test_ClearGainBucket.c
+++ b/tests/test_ClearGainBucket.c
@@ -11,10 +11,16 @@ int main(int argc, char **argv) {
     n= 100 ; /* number of buckets */
     bs = 5; /* bucket size */
     offset = n/2 ;
-
-    /* Insert vertices in increasing order of gain value */
+    
     GB.NrBuckets =0 ;
     GB.Root = NULL ;
+
+    if ( !InitGainBucket(&GB, n/2) ) {
+        printf("Error\n");
+        exit(1);
+    }
+
+    /* Insert vertices in increasing order of gain value */
     for (j=0; j<bs; j++)
         for (i=0; i<n; i++)
             BucketInsert(&GB, i-offset, j*n + i) ;
@@ -23,6 +29,11 @@ int main(int argc, char **argv) {
 
     ClearGainBucket(&GB) ;
     
+    if ( !DeleteGainBucket(&GB) ) {
+        printf("Error\n");
+        exit(1);
+    }
+    
     if (GB.Root != NULL || GB.NrBuckets != 0){
         printf("Error\n") ;
         exit(1);    
@@ -44,7 +55,11 @@ void CheckBuckets(struct gainbucket GB, long n, long bs) {
         exit(1);
     }
 
+#ifdef GAINBUCKET_ARRAY
+    pB = &(GB.Root[GB.MaxValue+GB.MaxPresentValue]) ;
+#else
     pB = GB.Root ;
+#endif
 
     offset = n/2 ; 
     i = 0;
@@ -64,7 +79,11 @@ void CheckBuckets(struct gainbucket GB, long n, long bs) {
             j++ ;
             pE = pE->next ;
         }
+#ifdef GAINBUCKET_ARRAY
+        pB = (pB->value > -GB.MaxValue) ? pB-1 : NULL ;
+#else
         pB = pB->next ;
+#endif
         if (j!= bs){  
             printf("Error\n") ;
             exit(1); 
diff --git a/tests/test_ComputeInitialGains.c b/tests/test_ComputeInitialGains.c
index 0af5f15f8d28f50457e418ae3e6041308d3f62e9..b44e16a1aed565e039cb4263afba7fe01648ed7b 100644
--- a/tests/test_ComputeInitialGains.c
+++ b/tests/test_ComputeInitialGains.c
@@ -9,6 +9,7 @@ extern int ComputeInitialGains(struct biparthypergraph *pHG);
 int main(int argc, char **argv) {
 
     struct biparthypergraph HG;
+    struct bucket *pB ;
 
     long n, t, i, P;
 
@@ -78,6 +79,11 @@ int main(int argc, char **argv) {
     for (P=0; P<2; P++) {
         HG.GBVtx[P].NrBuckets  = 0;
         HG.GBVtx[P].Root = NULL;
+    
+        if ( !InitGainBucket(&(HG.GBVtx[P]), n/3) ) {
+            printf("Error\n");
+            exit(1);
+        }
     }
 
     ComputeInitialGains(&HG);
@@ -90,9 +96,14 @@ int main(int argc, char **argv) {
 
     /* Check gainbucket data structure */
     for (P=0; P<2; P++) {
+#ifdef GAINBUCKET_ARRAY
+        pB = &(HG.GBVtx[P].Root[HG.GBVtx[P].MaxValue+HG.GBVtx[P].MaxPresentValue]) ;
+#else
+        pB = HG.GBVtx[P].Root ;
+#endif
         if (HG.GBVtx[P].NrBuckets  != 1 ||
-             (HG.GBVtx[P].Root)->value != n/3 ||
-             ((HG.GBVtx[P].Root)->entry)->vtxnr != P) {
+             pB->value != n/3 ||
+             (pB->entry)->vtxnr != P) {
             printf("Error\n");
             exit(1);
         }
@@ -103,6 +114,14 @@ int main(int argc, char **argv) {
         printf("Error\n");
         exit(1);
     }
+    
+    /* Delete gainbucket data structure */
+    for (P=0; P<2; P++) {
+        if ( !DeleteGainBucket(&(HG.GBVtx[P])) ) {
+            printf("Error\n");
+            exit(1);
+        }
+    }
 
     printf("OK\n");
     exit(0);
diff --git a/tests/test_ComputeWeight.c b/tests/test_ComputeWeight.c
index 3330f111b178f9b8c3b710d36f81e5ae76f35170..cec84d2e5200ea63fb13f3915649f280ee6d7b22 100644
--- a/tests/test_ComputeWeight.c
+++ b/tests/test_ComputeWeight.c
@@ -32,6 +32,7 @@ int main(int argc, char **argv) {
             t++;
         }
     }
+    A.MMTypeCode[0]='M';
     A.MMTypeCode[3]='S';
     Options.SymmetricMatrix_UseSingleEntry = SingleEntYes;
 
diff --git a/tests/test_DetectConnectedComponents.c b/tests/test_DetectConnectedComponents.c
new file mode 100644
index 0000000000000000000000000000000000000000..54aab2897c29a53fa45bf9c30c79d878e09f4a7a
--- /dev/null
+++ b/tests/test_DetectConnectedComponents.c
@@ -0,0 +1,156 @@
+#include "ZeroVolumeSearch.h"
+#include "testHelper_DisconnectedMatrix.h"
+
+void test_detectConnectedComponents(int symmetric, int dummies, int colWeights);
+
+int main(int argc, char **argv) {
+
+    printf("Test DetectConnectedComponents: ");
+    
+    SetRandomSeed(111);
+    /*struct timeval tv;
+    gettimeofday(&tv,NULL);
+    unsigned long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;
+    SetRandomSeed(time_in_micros);*/
+    
+    test_detectConnectedComponents(FALSE, FALSE, FALSE);
+    test_detectConnectedComponents(TRUE,  FALSE, FALSE);
+    test_detectConnectedComponents(FALSE, TRUE,  FALSE);
+    test_detectConnectedComponents(TRUE,  TRUE,  FALSE);
+    
+    test_detectConnectedComponents(FALSE, FALSE, TRUE);
+    test_detectConnectedComponents(TRUE,  FALSE, TRUE);
+    test_detectConnectedComponents(FALSE, TRUE,  TRUE);
+    test_detectConnectedComponents(TRUE,  TRUE,  TRUE);
+
+    printf("OK\n");
+    exit(0);
+
+} /* end main */
+
+
+/**
+ * Test DetectConnectedComponents()
+ * 
+ * Input:
+ * symmetric         : Whether the matrix should be symmetric
+ * dummies           : Whether the matrix should contain dummy diagonal nonzeros
+ * colWeights        : Whether the matrix should be weighted with column weights
+ */
+void test_detectConnectedComponents(int symmetric, int dummies, int colWeights) {
+    
+    struct sparsematrix A;
+    struct opts Options;
+    
+    SetDefaultOptions(&Options);
+    Options.SymmetricMatrix_UseSingleEntry = symmetric ? SingleEntYes : SingleEntNo;
+    
+    /* In this function, we distinguish between the components we generate (calling them submatrices) and
+     * the components we detect (calling them just components), to make clean what we are dealing with.
+     */
+    long i, I, cmpnnt, submat;
+    
+    long numSubmatrices, *submatrix_m, *submatrix_n, *submatrix_weights, **i_to_I, **j_to_J;
+    ConstructDisconnectedMatrix(&A, symmetric, dummies, colWeights, 2, 5, &numSubmatrices, &submatrix_m, &submatrix_n, &submatrix_weights, &i_to_I, &j_to_J);
+    
+    long maxWeight = 0;
+    for(submat=0; submat<numSubmatrices; ++submat) {
+        if(submatrix_weights[submat] > maxWeight) {
+            maxWeight = submatrix_weights[submat];
+        }
+    }
+    
+    long numComponentsFound = 0;
+    long *componentWeightsFound = NULL;
+    long *rowAssignmentsFound = NULL;
+    
+    /* Search for connected components (this should fail) */
+    if(DetectConnectedComponents(&A, maxWeight-1, &numComponentsFound, &componentWeightsFound, &rowAssignmentsFound, &Options)) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    /* Search for connected components (this should succeed) */
+    if(!DetectConnectedComponents(&A, maxWeight, &numComponentsFound, &componentWeightsFound, &rowAssignmentsFound, &Options)) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    /* Check number of components */
+    if(numComponentsFound != numSubmatrices) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    /*for(cmpnnt=0; cmpnnt<numComponentsFound; ++cmpnnt) {
+        fprintf(stderr, "%ld ", submatrix_weights[cmpnnt]);
+    }fprintf(stderr, "\n");
+    for(cmpnnt=0; cmpnnt<numComponentsFound; ++cmpnnt) {
+        fprintf(stderr, "%ld ", componentWeightsFound[cmpnnt]);
+    }fprintf(stderr, "\n");*/
+    
+    /* Check amount of nonzeros in each component */
+    long *component2submatrix = (long *)calloc(numSubmatrices, sizeof(long));
+    if (component2submatrix == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    for(submat=0; submat<numSubmatrices; ++submat) {
+        int found = 0;
+        for(cmpnnt=0; cmpnnt<numComponentsFound; ++cmpnnt) {
+            if(component2submatrix[cmpnnt] == 0 && componentWeightsFound[cmpnnt] == submatrix_weights[submat]) {
+                component2submatrix[cmpnnt] = submat;
+                found = 1;
+                break;
+            }
+        }
+        if(!found) {
+            printf("Error\n");
+            exit(1);
+        }
+    }
+    
+    /* Check all row assigments (check rowAssignmentsFound against i_to_I) */
+    long *component_m_found = (long*)calloc(numSubmatrices, sizeof(long));
+    if (component_m_found == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    long submat_nnz;
+    for(I=0; I<A.m; ++I) {
+        submat = component2submatrix[rowAssignmentsFound[I]];
+        submat_nnz = submatrix_weights[submat];
+        
+        /* Search whether the current row is actually part of the submatrix */
+        int found = 0;
+        for(i=0; i<submat_nnz; ++i) {
+            if(i_to_I[submat][i] == I) {
+                found = 1;
+                break;
+            }
+        }
+        if(!found) {
+            printf("Error\n");
+            exit(1);
+        }
+        
+        ++component_m_found[submat];
+    }
+    
+    /* Check the obtained row counts */
+    for(submat=0; submat<numSubmatrices; ++submat) {
+        if(component_m_found[submat] != submatrix_m[submat]) {
+            printf("Error\n");
+            exit(1);
+        }
+    }
+    
+    free(component2submatrix);
+    free(component_m_found);
+    free(componentWeightsFound);
+    free(rowAssignmentsFound);
+    
+    DestructDisconnectedMatrix(&A, symmetric, dummies, colWeights, &submatrix_m, &submatrix_n, &submatrix_weights, &i_to_I, &j_to_J);
+    
+} /* end test_detectConnectedComponents */
+
diff --git a/tests/test_FindOptimalPathMatching.c b/tests/test_FindOptimalPathMatching.c
index 1ff69732dcd6811b32b70bfa7abe3e4d61395a17..abf5be007bb84b6cfb5e97391233599b88be96a2 100644
--- a/tests/test_FindOptimalPathMatching.c
+++ b/tests/test_FindOptimalPathMatching.c
@@ -3,58 +3,85 @@
 
 #define LENGTH 4
 
+
 int main(int argc, char **argv) {
-    long *PathMatchings[3];
-    double *PathWeights;
     long Size;
     long t;
     
-    for (t = 0; t < 3; t++) PathMatchings[t] = (long *)malloc(LENGTH*sizeof(long));
-    PathWeights = (double *)malloc(LENGTH*sizeof(double));
-
+    /* Working arrays for creating optimal path matchings. */
+    char *_PathMatchings = NULL, **PathMatchings = NULL, *PathMatchingOpt = NULL;
+    double *_PathMatchingWeights = NULL, *PathMatchingWeights[2];
+    /* Array containing the matching weights along the constructed path. */
+    double *PathWeights = (double *)malloc(LENGTH*sizeof(double));
+    /* Array containing the indices of the vertices along this path. */
+    long *PathIndices = (long *)malloc(LENGTH*sizeof(long));
+    long MaxNrVtxInMatch = 2;
+    
+    /* Set up PathMatchings */
+    _PathMatchings = (char *)malloc(MaxNrVtxInMatch * LENGTH * sizeof(char));
+    PathMatchings = (char **)malloc(LENGTH * sizeof(char *));
+    for(t=0; t<LENGTH; ++t) {
+        PathMatchings[t] = &(_PathMatchings[t*MaxNrVtxInMatch]);
+    }
+    /* Set up PathMatchingWeights */
+    _PathMatchingWeights = (double *)malloc(2*MaxNrVtxInMatch * sizeof(double));
+    PathMatchingWeights[0] = &(_PathMatchingWeights[0]);
+    PathMatchingWeights[1] = &(_PathMatchingWeights[MaxNrVtxInMatch]);
+    /* Set up PathMatchingOpt */
+    PathMatchingOpt = (char *)malloc(LENGTH * sizeof(char));
+    
+    
     printf("Test FindOptimalPathMatching: ");
     
     /* Solution *=*-*=*-* */
-    for (t = 0; t < LENGTH; t++) PathMatchings[2][t] = -1;
+    for (t = 0; t < LENGTH; t++) PathMatchingOpt[t] = PathMatchings[t][0] = PathMatchings[t][1] = -1;
     PathWeights[0] = 2.0;
     PathWeights[1] = 1.9;
     PathWeights[2] = 0.5;
     PathWeights[3] = 0.4;
     
-    Size = FindOptimalPathMatching(PathMatchings, PathWeights, LENGTH + 1);
+    Size = FindOptimalPathMatching(PathMatchings, PathMatchingWeights, PathMatchingOpt, PathWeights, LENGTH + 1, MaxNrVtxInMatch);
     
-    if (Size != 2 || PathMatchings[2][0] != 0 || PathMatchings[2][1] != 2) {
+    if (Size != 2 || PathMatchingOpt[0] != 1 || PathMatchingOpt[1] != 0 || PathMatchingOpt[2] != 1 || PathMatchingOpt[3] != 0) {
         printf("Error\n");
         exit(1);
     }
 
     /* Solution *-*=*-*=* */
-    for (t = 0; t < LENGTH; t++) PathMatchings[2][t] = -1;
+    for (t = 0; t < LENGTH; t++) PathMatchingOpt[t] = PathMatchings[t][0] = PathMatchings[t][1] = -1;
     PathWeights[0] = 1.9;
     PathWeights[1] = 2.0;
     PathWeights[2] = 0.5;
     PathWeights[3] = 0.5;
     
-    Size = FindOptimalPathMatching(PathMatchings, PathWeights, LENGTH + 1);
+    Size = FindOptimalPathMatching(PathMatchings, PathMatchingWeights, PathMatchingOpt, PathWeights, LENGTH + 1, MaxNrVtxInMatch);
     
-    if (Size != 2 || PathMatchings[2][0] != 1 || PathMatchings[2][1] != 3) {
+    if (Size != 2 || PathMatchingOpt[0] != 0 || PathMatchingOpt[1] != 1 || PathMatchingOpt[2] != 0 || PathMatchingOpt[3] != 1) {
         printf("Error\n");
         exit(1);
     }
 
     /* Solution *=*-*-*=* */
-    for (t = 0; t < LENGTH; t++) PathMatchings[2][t] = -1;
+    for (t = 0; t < LENGTH; t++) PathMatchingOpt[t] = PathMatchings[t][0] = PathMatchings[t][1] = -1;
     PathWeights[0] = 2.0;
     PathWeights[1] = 1.9;
     PathWeights[2] = 0.5;
     PathWeights[3] = 0.6;
     
-    Size = FindOptimalPathMatching(PathMatchings, PathWeights, LENGTH + 1);
+    Size = FindOptimalPathMatching(PathMatchings, PathMatchingWeights, PathMatchingOpt, PathWeights, LENGTH + 1, MaxNrVtxInMatch);
     
-    if (Size != 2 || PathMatchings[2][0] != 0 || PathMatchings[2][1] != 3) {
+    if (Size != 2 || PathMatchingOpt[0] != 1 || PathMatchingOpt[1] != 0 || PathMatchingOpt[2] != 0 || PathMatchingOpt[3] != 1) {
         printf("Error\n");
         exit(1);
     }
+    
+    
+    free(PathWeights);
+    free(PathIndices);
+    free(_PathMatchings);
+    free(PathMatchings);
+    free(_PathMatchingWeights);
+    free(PathMatchingOpt);
 
     printf("OK\n");
     exit(0);
diff --git a/tests/test_FreeNonzeros.c b/tests/test_FreeNonzeros.c
new file mode 100644
index 0000000000000000000000000000000000000000..98babc0399edf4caf0bd4ea531758e5849910ee5
--- /dev/null
+++ b/tests/test_FreeNonzeros.c
@@ -0,0 +1,184 @@
+#include "FreeNonzeros.h"
+#include "DistributeMat.h"
+#include "DistributeVecLib.h"
+#include <math.h>
+
+void test_FreeNonzeros(int symmetric);
+
+int main(int argc, char **argv) {
+
+    printf("Test FreeNonzeros: ");
+    
+    SetRandomSeed(111);
+    /*struct timeval tv;
+    gettimeofday(&tv,NULL);
+    unsigned long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;
+    SetRandomSeed(time_in_micros);*/
+    
+    test_FreeNonzeros(FALSE);
+    test_FreeNonzeros(TRUE);
+    
+    printf("OK\n");
+    exit(0);
+
+} /* end main */
+
+/**
+ * Compute the total communication volume of a matrix.
+ */
+long ComputeVolume(struct sparsematrix *pM, int symmetric) {
+    if(symmetric) {
+        SparseMatrixSymmetric2Full(pM);
+    }
+    
+    long tmp, ComVolV, ComVolU;
+    CalcCom(pM, NULL, ROW, &ComVolV, &tmp, &tmp, &tmp, &tmp);
+    CalcCom(pM, NULL, COL, &ComVolU, &tmp, &tmp, &tmp, &tmp);
+    
+    if(symmetric) {
+        SparseMatrixFull2Symmetric(pM, 'S');
+    }
+    
+    return ComVolV+ComVolU;
+
+} /* end ComputeVolume */
+
+
+/**
+ * Test ImproveFreeNonzeros*()
+ * 
+ * Input:
+ * symmetric: Whether the matrix should be symmetric
+ */
+void test_FreeNonzeros(int symmetric) {
+    
+    struct sparsematrix A;
+    struct sparsematrix *pA = &A;
+    struct opts options;
+    
+    options.SymmetricMatrix_UseSingleEntry = symmetric ? SingleEntYes : SingleEntNo;
+    
+    long t, p, i, j;
+    long P = 5, m = 20, n = 20;
+    
+    /* Set up matrix struct */
+    MMSparseMatrixInit(pA);
+    pA->m = m;
+    pA->n = n;
+    pA->NrNzElts = P*(m+n)+((m-P)*(n-P))/5;
+    pA->NrProcs = P; /* maximum number of parts */
+
+    pA->i = (long *)malloc(pA->NrNzElts*sizeof(long));
+    pA->j = (long *)malloc(pA->NrNzElts*sizeof(long));
+    pA->Pstart = (long *)malloc((P+1)*sizeof(long));
+    
+    if (pA->i == NULL || pA->j == NULL || pA->Pstart == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    pA->MMTypeCode[0]='D'; /* normal matrix */
+    pA->MMTypeCode[1]='C'; /* coordinate scheme */
+    pA->MMTypeCode[2]='P'; /* pattern only */
+    if(symmetric)
+        pA->MMTypeCode[3]='S'; /* symmetric */
+    else
+        pA->MMTypeCode[3]='G'; /* general, no symmetry */
+    
+    /* Fill matrix */
+    t = 0;
+    for(p=0; p<P; ++p) {
+        /* First, fill rows and columns such that we have many processors per row/column */
+        pA->Pstart[p] = t;
+        for(i=P; i<pA->m; ++i) {
+            if(Random1(0, 2) == 0) {
+                continue;
+            }
+            pA->i[t] = i;
+            pA->j[t] = p;
+            ++t;
+        }
+        if(!symmetric) {
+            for(j=P; j<pA->n; ++j) {
+                if(Random1(0, 2) == 0) {
+                    continue;
+                }
+                pA->i[t] = p;
+                pA->j[t] = j;
+                ++t;
+            }
+        }
+    }
+    
+    /* Add additional nonzeros to last partition */
+    i = j = 0;
+    while(TRUE) {
+        j += Random1(5, 10);
+        if(symmetric) {
+            while(j > i) {
+                j -= i;
+                ++i;
+            }
+        }
+        else {
+            while(j >= pA->n-P) {
+                j -= pA->n-P;
+                ++i;
+            }
+        }
+        if(i >= pA->m-P) {
+            break;
+        }
+        pA->i[t] = P+i;
+        pA->j[t] = P+j;
+        ++t;
+    }
+    pA->Pstart[P] = pA->NrNzElts = t;
+    
+    /* We should provide a procs array */
+    int *procs = (int *)malloc(P*sizeof(int));
+    if (procs == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    for(p=0; p<P; ++p) {
+        procs[p] = 1;
+    }
+    
+    /* Compute statistics before applying algorithm */
+    long totalImbalanceBefore = 0, weight;
+    long avgNrNzElts = pA->NrNzElts / P;
+    for(p=0; p<P; ++p) {
+        weight = ComputeWeight(pA, pA->Pstart[p], pA->Pstart[p+1]-1, NULL, &options);
+        totalImbalanceBefore += abs(weight-avgNrNzElts);
+    }
+    long volumeBefore = ComputeVolume(pA, symmetric);
+    
+    /* Run algorithm */
+    ImproveFreeNonzeros(pA, &options, procs, 3, 4);
+    
+    /* Compute statistics after applying algorithm */
+    long totalImbalanceAfter = 0.0;
+    for(p=0; p<P; ++p) {
+        weight = ComputeWeight(pA, pA->Pstart[p], pA->Pstart[p+1]-1, NULL, &options);
+        totalImbalanceAfter += abs(weight-avgNrNzElts);
+    }
+    long volumeAfter = ComputeVolume(pA, symmetric);
+    
+    /* Check that the total imbalance has not increased */
+    if(totalImbalanceAfter > totalImbalanceBefore) {
+        printf("Error1\n");
+        exit(1);
+    }
+    
+    /* Check that the volume has not increased */
+    if(volumeAfter > volumeBefore) {
+        printf("Error2\n");
+        exit(1);
+    }
+    
+    free(procs);
+    MMDeleteSparseMatrix(pA);
+    
+} /* end test_FreeNonzeros */
+
diff --git a/tests/test_GainBucketGetMaxVal.c b/tests/test_GainBucketGetMaxVal.c
index ec81566094aba6049311c7c9fad67e2d4816c368..061cbe487a4f7fdccab1fcaf649c052a8f4b7fd8 100644
--- a/tests/test_GainBucketGetMaxVal.c
+++ b/tests/test_GainBucketGetMaxVal.c
@@ -12,9 +12,15 @@ int main(int argc, char **argv) {
     bs = 5; /* bucket size */
     offset = n/2 ;
 
-    /* Insert vertices in increasing order of gain value */
     GB.NrBuckets =0 ;
     GB.Root = NULL ;
+    
+    if ( !InitGainBucket(&GB, n/2) ) {
+        printf("Error\n");
+        exit(1);
+    }
+
+    /* Insert vertices in increasing order of gain value */
     for (j=0; j<bs; j++)
         for (i=0; i<n; i++)
             BucketInsert(&GB, i-offset, j*n + i) ;
@@ -44,6 +50,11 @@ int main(int argc, char **argv) {
 
         }
     }
+    
+    if ( !DeleteGainBucket(&GB) ) {
+        printf("Error\n");
+        exit(1);
+    }
 
     
     if (GB.Root != NULL || GB.NrBuckets != 0){
@@ -67,7 +78,11 @@ void CheckBuckets(struct gainbucket GB, long n, long bs) {
         exit(1);
     }
 
+#ifdef GAINBUCKET_ARRAY
+    pB = &(GB.Root[GB.MaxValue+GB.MaxPresentValue]) ;
+#else
     pB = GB.Root ;
+#endif
 
     offset = n/2 ; 
     i = 0;
@@ -87,7 +102,11 @@ void CheckBuckets(struct gainbucket GB, long n, long bs) {
             j++ ;
             pE = pE->next ;
         }
+#ifdef GAINBUCKET_ARRAY
+        pB = (pB->value > -GB.MaxValue) ? pB-1 : NULL ;
+#else
         pB = pB->next ;
+#endif
         if (j!= bs){  
             printf("Error\n") ;
             exit(1); 
diff --git a/tests/test_Heap.c b/tests/test_Heap.c
new file mode 100644
index 0000000000000000000000000000000000000000..25e492ef6d797925a4f5290fc0282e1100f2fa07
--- /dev/null
+++ b/tests/test_Heap.c
@@ -0,0 +1,131 @@
+#include "Heap.h"
+#include "Sort.h"
+#include <string.h>
+
+#define max(x,y) ( ( (x)>(y) ) ? (x) : (y) )
+
+
+void test_Heapify();
+void test_HeapPush();
+
+int main(int argc, char **argv) {
+
+    printf("Test Heap: ");
+    
+    SetRandomSeed(111);
+    
+    test_Heapify();
+    test_HeapPush();
+
+    printf("OK\n");
+    exit(0);
+
+} /* end main */
+
+
+int compareLong (const void *a, const void *b) {
+    long diff = *((long*)a) - *((long*)b);
+    if(diff == 0)
+        return 0;
+    return (diff < 0) ? -1 : 1;
+} /* end compareLong */
+
+int compare2ndLong (const void *a, const void *b) {
+    long diff = ((long*)a)[1] - ((long*)b)[1];
+    if(diff == 0)
+        return 0;
+    return (diff < 0) ? -1 : 1;
+} /* end compare2ndLong */
+
+
+
+/**
+ * Test Heapify()
+ */
+void test_Heapify() {
+    long N = 100, i;
+    
+    long *items = (long*)malloc(2*N*sizeof(long));
+    long *heapItems = (long*)malloc(2*N*sizeof(long));
+    long tmp[2];
+    
+    if (items == NULL || heapItems  == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    for(i=0; i<N; ++i) {
+        items[2*i  ] = i;
+        items[2*i+1] = Random1(0, 50);
+    }
+    
+    memcpy(heapItems, items, 2*N*sizeof(long));
+    
+    struct heap Heap;
+    HeapInit(&Heap, 2*sizeof(long), compare2ndLong);
+    Heapify(&Heap, heapItems, N);
+    
+    qsort(items, N, 2*sizeof(long), compare2ndLong);
+    
+    for(i=0; i<N; ++i) {
+        HeapPop(&Heap, tmp);
+        if(tmp[1] != items[2*N-(2*i)-1]) {
+            printf("Error\n");
+            
+        }
+    }
+    
+    if(Heap.numItems > 0) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    HeapDestroy(&Heap);
+    free(items);
+    
+} /* end Heapify */
+
+/**
+ * Test HeapPush()
+ */
+void test_HeapPush() {
+    
+    struct heap Heap;
+    HeapInit(&Heap, sizeof(long), compareLong);
+    long tmp[1], tmp2[1], i, N = 100, max = -1;
+    long *items = (long*)malloc(N*sizeof(long));
+    
+    if (items == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    for(i=0; i<N; ++i) {
+        *tmp = items[i] = Random1(0,N);
+        max = max(max, *tmp);
+        HeapPush(&Heap, tmp);
+        if(!HeapPeek(&Heap, tmp2) || *tmp2 != max) {
+            printf("Error\n");
+            exit(1);
+        }
+    }
+    
+    qsort(items, N, sizeof(long), compareLong);
+    
+    for(i=0; i<N; ++i) {
+        HeapPop(&Heap, tmp);
+        if(*tmp != items[N-i-1]) {
+            printf("Error\n");
+            
+        }
+    }
+    
+    if(Heap.numItems > 0) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    HeapDestroy(&Heap);
+    free(items);
+    
+} /* end HeapPush */
diff --git a/tests/test_SetDefaultOptions.c b/tests/test_SetDefaultOptions.c
index 312d851d71fbaf6d89c749d53fc4e115ef9fbaaa..22703f629dcdef244cfc1f94849a455cbab29e53 100644
--- a/tests/test_SetDefaultOptions.c
+++ b/tests/test_SetDefaultOptions.c
@@ -69,6 +69,9 @@ int main(int argc, char **argv) {
         fclose(File);
         
         CheckOptions(Options);
+        
+        remove("test_SetDefaultOptions.check");
+        remove("test_SetDefaultOptions.check2");
     }
     else {
         printf("Error\n");
diff --git a/tests/test_SparseMatrixFull2Symmetric.c b/tests/test_SparseMatrixFull2Symmetric.c
new file mode 100644
index 0000000000000000000000000000000000000000..747c3a1cb7ed28a726afceaa31ed8f29f04069b6
--- /dev/null
+++ b/tests/test_SparseMatrixFull2Symmetric.c
@@ -0,0 +1,111 @@
+#include "SparseMatrix.h"
+#include <math.h>
+
+void test_SparseMatrixFull2Symmetric();
+
+int main(int argc, char **argv) {
+
+    printf("Test SparseMatrixFull2Symmetric: ");
+    
+    SetRandomSeed(111);
+    /*struct timeval tv;
+    gettimeofday(&tv,NULL);
+    unsigned long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;
+    SetRandomSeed(time_in_micros);*/
+    
+    test_SparseMatrixFull2Symmetric();
+    
+    printf("OK\n");
+    exit(0);
+
+} /* end main */
+
+
+/**
+ * Test SparseMatrixFull2Symmetric()
+ */
+void test_SparseMatrixFull2Symmetric() {
+    
+    struct sparsematrix A, B;
+    struct sparsematrix *pA = &A, *pB = &B;
+    
+    long t, p, i, j;
+    long P = 5, n = 20;
+    
+    /* Set up matrix struct */
+    MMSparseMatrixInit(pA);
+    MMSparseMatrixInit(pB);
+    pA->m = pB->m = n;
+    pA->n = pB->n = n;
+    pA->NrNzElts = pB->NrNzElts = (n*n+n)/2;
+    pA->NrProcs = pB->NrProcs = P; /* maximum number of parts */
+
+    pA->i = (long *)malloc(pA->NrNzElts*sizeof(long));
+    pA->j = (long *)malloc(pA->NrNzElts*sizeof(long));
+    pA->Pstart = (long *)malloc((P+1)*sizeof(long));
+    
+    pB->i = (long *)malloc(pA->NrNzElts*sizeof(long));
+    pB->j = (long *)malloc(pA->NrNzElts*sizeof(long));
+    pB->Pstart = (long *)malloc((P+1)*sizeof(long));
+    
+    if (pA->i == NULL || pA->j == NULL || pA->Pstart == NULL || pB->i == NULL || pB->j == NULL || pB->Pstart == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    pA->MMTypeCode[0]=pB->MMTypeCode[0]='D'; /* normal matrix */
+    pA->MMTypeCode[1]=pB->MMTypeCode[1]='C'; /* coordinate scheme */
+    pA->MMTypeCode[2]=pB->MMTypeCode[2]='P'; /* pattern only */
+    pA->MMTypeCode[3]=pB->MMTypeCode[3]='S'; /* symmetric */
+    
+    /* Fill matrix */
+    t = 0;
+    i = j = 0;
+    while(TRUE) {
+        j += Random1(1, 5);
+        while(j > i) {
+            j -= i;
+            ++i;
+        }
+        if(i >= pA->m) {
+            break;
+        }
+        pA->i[t] = pB->i[t] = i;
+        pA->j[t] = pB->j[t] = j;
+        ++t;
+    }
+    pA->NrNzElts = pB->NrNzElts = t;
+    for(p=0; p<P; ++p) {
+        pA->Pstart[p] = pB->Pstart[p] = (pA->NrNzElts/P)*p;
+    }
+    pA->Pstart[P] = pB->Pstart[P] = pA->NrNzElts;
+    
+    /* Run procedures */
+    SparseMatrixSymmetric2Full(pA);
+    SparseMatrixFull2Symmetric(pA, 'S');
+    
+    /* Check result */
+    if(pA->NrNzElts != pB->NrNzElts || pA->NrProcs != P) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    for(p=0; p<=P; ++p) {
+        if(pA->Pstart[p] != pB->Pstart[p]) {
+            printf("Error\n");
+            exit(1);
+        }
+    }
+    
+    for(t=0; t<pA->NrNzElts; ++t) {
+        if(pA->i[t] != pB->i[t] || pA->j[t] != pB->j[t]) {
+            printf("Error\n");
+            exit(1);
+        }
+    }
+    
+    MMDeleteSparseMatrix(pA);
+    MMDeleteSparseMatrix(pB);
+    
+} /* end test_SparseMatrixFull2Symmetric */
+
diff --git a/tests/test_SparseMatrixToCRS_CCS.c b/tests/test_SparseMatrixToCRS_CCS.c
new file mode 100644
index 0000000000000000000000000000000000000000..ba343a5159bf6410029f062e3cbafe18f0ebc20a
--- /dev/null
+++ b/tests/test_SparseMatrixToCRS_CCS.c
@@ -0,0 +1,132 @@
+#include "SparseMatrix.h"
+
+void test_SparseMatrixToCRS_CCS();
+
+int main(int argc, char **argv) {
+
+    printf("Test SparseMatrixToCRS_CCS: ");
+    
+    SetRandomSeed(111);
+    /*struct timeval tv;
+    gettimeofday(&tv,NULL);
+    unsigned long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;
+    SetRandomSeed(time_in_micros);*/
+    
+    test_SparseMatrixToCRS_CCS();
+
+    printf("OK\n");
+    exit(0);
+
+} /* end main */
+
+
+/**
+ * Test SparseMatrixToCRS_CCS()
+ */
+void test_SparseMatrixToCRS_CCS() {
+    
+    struct sparsematrix A;
+    
+    long i, j, k, t, start, end;
+    
+    MMSparseMatrixInit(&A);
+    A.MMTypeCode[0]='M'; /* normal matrix */
+    A.MMTypeCode[1]='C'; /* coordinate scheme */
+    A.MMTypeCode[2]='P'; /* pattern only */
+    A.MMTypeCode[3]='G'; /* general, no symmetry */
+    A.m = Random1(10, 20);
+    A.n = Random1(10, 20);
+    A.NrNzElts = Random1(A.m+A.n, (A.m+A.n)*3/2);
+    A.i = (long *)malloc(A.NrNzElts*sizeof(long));
+    A.j = (long *)malloc(A.NrNzElts*sizeof(long));
+    
+    if(A.i == NULL || A.j == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    /* Add random nonzeros */
+    for(t=0; t<A.NrNzElts; ++t) {
+        A.i[t] = Random1(0, A.m-1);
+        A.j[t] = Random1(0, A.n-1);
+    }
+    
+    /* SparseMatrixToCRS_CCS() assumes there are no duplicates */
+    if(!SparseMatrixRemoveDuplicates(&A)) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    /* Convert to CCS/CRS */
+    struct CRCS CCS, CRS;
+    if(!SparseMatrixToCRS_CCS(&A, &CCS, &CRS)) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    long *visitedCCS = (long *)calloc(A.NrNzElts, sizeof(long));
+    long *visitedCRS = (long *)calloc(A.NrNzElts, sizeof(long));
+    
+    if(visitedCCS == NULL || visitedCRS == NULL) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    /* Check that all nonzeros in A are present in CCS/CRS */
+    int found;
+    for(t=0; t<A.NrNzElts; ++t) {
+        i = A.i[t];
+        j = A.j[t];
+        
+        /* Check CCS */
+        start = CCS.starts[j];
+        end = CCS.starts[j+1];
+        
+        found = 0;
+        for(k=start; k<end; ++k) {
+            if(CCS.indices[k] == i && visitedCCS[k] == 0) {
+                visitedCCS[k] = 1;
+                found = 1;
+                break;
+            }
+        }
+        if(!found) {
+            printf("Error\n");
+            exit(1);
+        }
+        
+        /* Check CRS */
+        start = CRS.starts[i];
+        end = CRS.starts[i+1];
+        
+        found = 0;
+        for(k=start; k<end; ++k) {
+            if(CRS.indices[k] == j && visitedCRS[k] == 0) {
+                visitedCRS[k] = 1;
+                found = 1;
+                break;
+            }
+        }
+        if(!found) {
+            printf("Error\n");
+            exit(1);
+        }
+    }
+    
+    /* Check that all nonzeros in CCS/CRS are found */
+    for(k=0; k<A.NrNzElts; ++k) {
+        if(visitedCCS[k] != 1 || visitedCRS[k] != 1) {
+            printf("Error\n");
+            exit(1);
+        }
+    }
+    
+    free(visitedCCS);
+    free(visitedCRS);
+    freeCRCS(&CCS);
+    freeCRCS(&CRS);
+    MMSparseMatrixFreeMemory(&A);
+    
+    
+} /* end test_SparseMatrixToCRS_CCS */
+
diff --git a/tests/test_SplitMatrixSimple.c b/tests/test_SplitMatrixSimple.c
index 6cff2857b408a6cd2782518895b21a6d8bae2607..b295a2b6e9b3dc69c5b3581fcbb9b4bbcf2effe3 100644
--- a/tests/test_SplitMatrixSimple.c
+++ b/tests/test_SplitMatrixSimple.c
@@ -48,6 +48,7 @@ int main(int argc, char **argv) {
         }
     }
 
+    A.MMTypeCode[0]='M';
     A.MMTypeCode[3]='G';
     A.NrDummies = 0;
     A.Pstart[0] = 0;
diff --git a/tests/test_SplitMatrixUpperBound.c b/tests/test_SplitMatrixUpperBound.c
new file mode 100644
index 0000000000000000000000000000000000000000..37818d08bb3f5dc740a337ff459210f8f70c2c6d
--- /dev/null
+++ b/tests/test_SplitMatrixUpperBound.c
@@ -0,0 +1,45 @@
+#include "SplitMatrixUpperBound.h"
+#include "testHelper_DisconnectedMatrix.h"
+#include "DistributeMat.h"
+#include "DistributeVecLib.h"
+#include <math.h>
+
+
+int main(int argc, char **argv) {
+
+    printf("Test SplitMatrixUpperBound: ");
+    
+    SetRandomSeed(111);
+    /*struct timeval tv;
+    gettimeofday(&tv,NULL);
+    unsigned long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;
+    SetRandomSeed(time_in_micros);*/
+    
+    
+    struct sparsematrix A;
+    struct opts options;
+    
+    long numSubmatrices, *submatrix_m, *submatrix_n, *submatrix_weights, **i_to_I, **j_to_J;
+    ConstructDisconnectedMatrix(&A, FALSE, FALSE, FALSE, 1, 1, &numSubmatrices, &submatrix_m, &submatrix_n, &submatrix_weights, &i_to_I, &j_to_J);
+    
+    /* Compute solution */
+    int P = 4;
+    if (!SplitMatrixUpperBound(&A, P, &options)) {
+        printf("Error: Unable to compute upper bound solution!\n");
+        exit(0);
+    }
+    
+    /* Check solution */
+    if(!CheckUpperBoundSolution(&A)) {
+        printf("Error: Invalid result.\n");
+        exit(0);
+    }
+    
+    /* Free memory */
+    DestructDisconnectedMatrix(&A, FALSE, FALSE, FALSE, &submatrix_m, &submatrix_n, &submatrix_weights, &i_to_I, &j_to_J);
+    
+    
+    printf("OK\n");
+    exit(0);
+
+} /* end main */
diff --git a/tests/test_SubsetSum.c b/tests/test_SubsetSum.c
new file mode 100644
index 0000000000000000000000000000000000000000..186ff91398939bf6ac2fd5f1bb443984919a4a3a
--- /dev/null
+++ b/tests/test_SubsetSum.c
@@ -0,0 +1,150 @@
+#include "SubsetSum.h"
+
+int test_SubsetSum(int KK_diff, double eps1, double eps2, long minN, long maxN);
+void time_comparison();
+
+int main(int argc, char **argv) {
+
+    printf("Test SubsetSum: ");
+    
+    SetRandomSeed(111);
+    /*struct timeval tv;
+    gettimeofday(&tv,NULL);
+    unsigned long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;
+    SetRandomSeed(time_in_micros);*/
+    
+    double epses[3] = {0.0, 0.03, 0.3};
+    int i, e1, e2;
+    
+    for(i=0; i<2; ++i) {
+        for(e1=0; e1<3; ++e1) {
+            for(e2=0; e2<3; ++e2) {
+                if(!test_SubsetSum(i, epses[e1], epses[e2], 2, 16)) {
+                    printf("Error\n");
+                    exit(1);
+                }
+            }
+        }
+    }
+    
+    /*time_comparison();*/
+    
+    printf("OK\n");
+    exit(0);
+
+} /* end main */
+
+
+/**
+ * Test SubsetSum() or KarmarkarKarp()
+ * 
+ * Input:
+ * KK_diff           : Whether the Karmarkar-Karp differencing algorithm or naive enumeration should be used
+ * eps1              : Maximum load imbalance of part 1
+ * eps2              : Maximum load imbalance of part 2
+ * minN              : Minimum N
+ * maxN              : Maximum N
+ */
+int test_SubsetSum(int KK_diff, double eps1, double eps2, long minN, long maxN) {
+    
+    long i;
+    
+    long N = Random1(minN,maxN);
+    long n1 = Random1(1, N-1);
+    long n2 = N-n1;
+    
+    long w1 = 0, w2 = 0;
+    long *weights = (long *)malloc(N*sizeof(long));
+    
+    /* Fill weights of subset 1 */
+    for(i=0; i<n1; ++i) {
+        weights[i] = Random1(1, 50);
+        w1 += weights[i];
+    }
+    
+    /* Fill weights of subset 2 */
+    for(i=0; i<n2; ++i) {
+        weights[n1+i] = Random1(1, 50);
+        w2 += weights[n1+i];
+    }
+    
+    RandomPermute(weights, NULL, NULL, NULL, 0, N-1);
+    
+    long *permutation = NULL;
+    long *selected = NULL;
+    
+    long w_tot = w1+w2;
+    long w_small = w1<w2 ? w1 : w2;
+    long w_large = w1>w2 ? w1 : w2;
+    long w_lo = w_small * (1+eps1);
+    long w_hi = w_large * (1+eps2);
+    long w_max = w_lo;
+    long w_min = w_tot - w_hi;
+    int found;
+    if(KK_diff)
+        found = KarmarkarKarp(weights, N, w_lo, w_hi, &permutation, &selected);
+    else
+        found = SubsetSumExp(weights, N, w_lo, w_hi, &permutation, &selected);
+    
+    /* By construction, a subset exists */
+    if(!found) {
+        if(KK_diff) {
+            /* However, KK may not be able to find it */
+            free(weights);
+            return TRUE;
+        }
+        return FALSE;
+    }
+    
+    /* Check weights of the found subsets */
+    long sum[2] = {0, 0};
+    for(i=0; i<N; ++i) {
+        sum[selected[i]] += weights[i];
+    }
+    
+    if(!(w_min <= sum[1] && sum[1] <= w_max) || !(w_tot - w_max <= sum[0] && sum[0] <= w_tot - w_min)) {
+        return FALSE;
+    }
+    
+    free(permutation);
+    free(selected);
+    free(weights);
+    
+    return TRUE;
+    
+} /* end test_SubsetSum */
+
+/*
+ * Benchmark utility to compare the SubsetSum and KarmarkarKarp methods.
+ */
+void time_comparison() {
+    long i, N, Nmax = 500, numRuns = 100;
+    
+    clock_t starttime, endtime;
+    double cputime;
+    
+    for(N=2; N<=Nmax; ++N) {
+        
+        printf("%ld ", N);
+        
+        starttime = clock();
+        for(i=0; i<numRuns; ++i) {
+            test_SubsetSum(0, 0.0, 0.0, N, N);
+        }
+        endtime = clock();
+        cputime = ((double) (endtime - starttime)) / CLOCKS_PER_SEC;
+        printf("%lf ", cputime/numRuns);
+        
+        
+        starttime = clock();
+        for(i=0; i<numRuns; ++i) {
+            test_SubsetSum(1, 0.0, 0.0, N, N);
+        }
+        endtime = clock();
+        cputime = ((double) (endtime - starttime)) / CLOCKS_PER_SEC;
+        printf("%lf\n", cputime/numRuns);
+        
+    }
+    
+    
+}
diff --git a/tests/test_TwoColorTree.c b/tests/test_TwoColorTree.c
new file mode 100644
index 0000000000000000000000000000000000000000..317135bc7d21763707fb912add6692c97dc6b1e2
--- /dev/null
+++ b/tests/test_TwoColorTree.c
@@ -0,0 +1,58 @@
+#include "SubsetSum.h"
+
+void test_TwoColorTree();
+
+int main(int argc, char **argv) {
+
+    printf("Test TwoColorTree: ");
+    
+    SetRandomSeed(111);
+    /*struct timeval tv;
+    gettimeofday(&tv,NULL);
+    unsigned long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;
+    SetRandomSeed(time_in_micros);*/
+    
+    test_TwoColorTree();
+
+    printf("OK\n");
+    exit(0);
+
+} /* end main */
+
+
+/**
+ * Test TwoColorTree()
+ */
+void test_TwoColorTree() {
+    long u, v, i, N = 100;
+    
+    struct Graph Tree;
+    GraphInit(&Tree, N);
+    
+    for(v=1; v<N; ++v) {
+        u = Random1(0, v-1);
+        GraphAddEdge(&Tree, u, v);
+    }
+    
+    TwoColorTree(&Tree);
+    
+    struct Node U;
+    for(u=0; u<N; ++u) {
+        U = Tree.nodes[u];
+        
+        if(U.val != 0 && U.val != 1) {
+            printf("Error\n");
+            exit(1);
+        }
+        
+        for(i=0; i<U.deg; ++i) {
+            if(Tree.nodes[U.adjList[i]].val == U.val) {
+                printf("Error\n");
+                exit(1);
+            }
+        }
+    }
+    
+    GraphDestroy(&Tree);
+    
+} /* end test_TwoColorTree */
diff --git a/tests/test_UpdateGains.c b/tests/test_UpdateGains.c
index b26bf23d8a87f36f652a2bdd6d14596fa6ed612d..977c7ed4231c384e4a69200ad340f9a2248938ed 100644
--- a/tests/test_UpdateGains.c
+++ b/tests/test_UpdateGains.c
@@ -90,6 +90,10 @@ int main(int argc, char **argv) {
     for (P=0; P<2; P++) {
         HG.GBVtx[P].NrBuckets  = 0;
         HG.GBVtx[P].Root = NULL;
+        if ( !InitGainBucket(&(HG.GBVtx[P]), 3) ) {
+            printf("Error\n");
+            exit(1);
+        }
     }
     
     /* Insert vertices 0, 1, 3, 4 into gainbucket data structure */
@@ -167,6 +171,14 @@ int main(int argc, char **argv) {
         printf("Error\n");
         exit(1);
     }
+    
+    /* Delete gainbucket data structure */
+    for (P=0; P<2; P++) {
+        if ( !DeleteGainBucket(&(HG.GBVtx[P])) ) {
+            printf("Error\n");
+            exit(1);
+        }
+    }
 
     printf("OK\n");
     exit(0);
diff --git a/tests/test_ZeroVolumeSearch.c b/tests/test_ZeroVolumeSearch.c
new file mode 100644
index 0000000000000000000000000000000000000000..3a312b68f3ec12c8d2612302d7a9f160abcf7e21
--- /dev/null
+++ b/tests/test_ZeroVolumeSearch.c
@@ -0,0 +1,152 @@
+#include "ZeroVolumeSearch.h"
+#include "testHelper_DisconnectedMatrix.h"
+#include "DistributeMat.h"
+#include "DistributeVecLib.h"
+#include <math.h>
+
+void test_ZeroVolumeSearch(int symmetric, int dummies, int colWeights, int large, double eps);
+
+int main(int argc, char **argv) {
+
+    printf("Test ZeroVolumeSearch: ");
+    
+    SetRandomSeed(111);
+    /*struct timeval tv;
+    gettimeofday(&tv,NULL);
+    unsigned long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;
+    SetRandomSeed(time_in_micros);*/
+    
+    int i;
+    double eps[3] = {0.0, 0.03, 0.3};
+    for(i=0; i<3; i++) {
+        test_ZeroVolumeSearch(FALSE, FALSE, FALSE, FALSE, eps[i]);
+        test_ZeroVolumeSearch(TRUE,  FALSE, FALSE, FALSE, eps[i]);
+        test_ZeroVolumeSearch(FALSE, TRUE,  FALSE, FALSE, eps[i]);
+        test_ZeroVolumeSearch(TRUE,  TRUE,  FALSE, FALSE, eps[i]);
+        
+        test_ZeroVolumeSearch(FALSE, FALSE, TRUE, FALSE, eps[i]);
+        test_ZeroVolumeSearch(TRUE,  FALSE, TRUE, FALSE, eps[i]);
+        test_ZeroVolumeSearch(FALSE, TRUE,  TRUE, FALSE, eps[i]);
+        test_ZeroVolumeSearch(TRUE,  TRUE,  TRUE, FALSE, eps[i]);
+        
+        test_ZeroVolumeSearch(FALSE, FALSE, FALSE, TRUE, eps[i]);
+        test_ZeroVolumeSearch(TRUE,  FALSE, FALSE, TRUE, eps[i]);
+        test_ZeroVolumeSearch(FALSE, TRUE,  FALSE, TRUE, eps[i]);
+        test_ZeroVolumeSearch(TRUE,  TRUE,  FALSE, TRUE, eps[i]);
+        
+        test_ZeroVolumeSearch(FALSE, FALSE, TRUE, TRUE, eps[i]);
+        test_ZeroVolumeSearch(TRUE,  FALSE, TRUE, TRUE, eps[i]);
+        test_ZeroVolumeSearch(FALSE, TRUE,  TRUE, TRUE, eps[i]);
+        test_ZeroVolumeSearch(TRUE,  TRUE,  TRUE, TRUE, eps[i]);
+    }
+    
+    printf("OK\n");
+    exit(0);
+
+} /* end main */
+
+
+/**
+ * Test DetectConnectedComponents()
+ * 
+ * Input:
+ * symmetric         : Whether the matrix should be symmetric
+ * dummies           : Whether the matrix should contain dummy diagonal nonzeros
+ * colWeights        : Whether the matrix should be weighted with column weights
+ * large             : Whether the matrix should be large (small), such that Karmarkar-Karp
+ *                     (naive enumeration) is used for the subset-sum problem.
+ * eps               : The maximum allowed load imbalance
+ */
+void test_ZeroVolumeSearch(int symmetric, int dummies, int colWeights, int large, double eps) {
+    
+    struct sparsematrix A;
+    struct opts options;
+    
+    options.SymmetricMatrix_UseSingleEntry = symmetric ? SingleEntYes : SingleEntNo;
+    
+    /* In this function, we distinguish between the components we generate (calling them submatrices) and
+     * the components we detect (calling them just components), to make clean what we are dealing with.
+     */
+    long submat, mid;
+    
+    long numSubmatrices, *submatrix_m, *submatrix_n, *submatrix_weights, **i_to_I, **j_to_J;
+    ConstructDisconnectedMatrix(&A, symmetric, dummies, colWeights, large?20:2, large?25:5, &numSubmatrices, &submatrix_m, &submatrix_n, &submatrix_weights, &i_to_I, &j_to_J);
+    
+    /* Determine sums */
+    long searchWeight = 0, totalWeight = 0;
+    for(submat=0; submat<numSubmatrices; submat++) {
+        if(submat%2 == 0) {
+            searchWeight += submatrix_weights[submat];
+        }
+        totalWeight += submatrix_weights[submat];
+    }
+    
+    /* Check the weights */
+    if(totalWeight != ComputeWeight(&A, 0, A.NrNzElts-1, NULL, &options)) {
+        printf("Error\n");
+        exit(1);
+    }
+    
+    /* Determine which part has minimum weight */
+    if(searchWeight > totalWeight-searchWeight) {
+        searchWeight = totalWeight-searchWeight;
+    }
+    
+    /* Augment the search weight within what is allowed by eps */
+    searchWeight = (long)ceil(((double)searchWeight)/(1+0.5*eps));
+    
+    /* Determine weight of small and large parts */
+    long weightlo = searchWeight;
+    long weighthi = totalWeight-searchWeight;
+    
+    /* Increase the allowed load imbalance */
+    weightlo *= (1+eps);
+    weighthi *= (1+eps);
+    
+    /* Run zero volume search */
+    int foundZeroVolumePartition = ZeroVolumeSearch(&A, weightlo, weighthi, &mid, &options);
+    
+    /* By construction, a zero volume partition exists */
+    if(foundZeroVolumePartition) {
+        
+        A.Pstart[1] = mid;
+        
+        /* Check the weights of the found partitionings */
+        long weight0 = ComputeWeight(&A, 0, mid-1, NULL, &options);
+        long weight1 = ComputeWeight(&A, mid, A.NrNzElts-1, NULL, &options);
+        if(weight0 > weightlo || weight1 > weighthi) {
+            printf("Error\n");
+            exit(1);
+        }
+        
+        /* Calculate communication volume. Change to full matrix for CalcCom() */
+        if(symmetric) {
+            SparseMatrixSymmetric2Full(&A);
+        }
+        long tmp, ComVolV, ComVolU;
+        if (!CalcCom(&A, NULL, ROW, &ComVolV, &tmp, &tmp, &tmp, &tmp) ||
+            !CalcCom(&A, NULL, COL, &ComVolU, &tmp, &tmp, &tmp, &tmp)) {
+            printf("Error\n");
+            exit(1);
+        }
+        
+        if(ComVolV != 0 || ComVolU != 0) {
+            printf("Error\n");
+            exit(1);
+        }
+        
+    }
+    else if(!large) {
+        printf("Error\n");
+        exit(1);
+        /* When using a large amount of components, the algorithm uses karmarkar-karp and
+         * we cannot rely on the fact that a zero volume partitioning exists, because
+         * the K-K heuristic might not find it. Hence we do not error out in that case.
+         */
+    }
+    
+    /* Free memory */
+    DestructDisconnectedMatrix(&A, symmetric, dummies, colWeights, &submatrix_m, &submatrix_n, &submatrix_weights, &i_to_I, &j_to_J);
+    
+} /* end test_ZeroVolumeSearch */
+
diff --git a/tools/Makefile b/tools/Makefile
index 1756605636ec7cea9e1a1e72e30ca43369d64c36..b0b98b404173d9505615a413d5cd448af1c44599 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -6,7 +6,7 @@ include ../mondriaan.mk
 % :: %.c ;
 
 #targets
-TARGETS := Mondriaan MondriaanPlot Profile
+TARGETS := Mondriaan MondriaanPlot MondriaanStats Profile
 LIB = ${MONDRIAANLIBDIR}/libMondriaan${MONDRIAANMAJORVERSION}.a
 ifdef MATLABHOMEDIR
 TARGETS := ${TARGETS} MatlabMondriaan MatlabMondriaanOpt
@@ -50,6 +50,9 @@ Mondriaan: Mondriaan.o ${LIB}
 MondriaanPlot: MondriaanPlot.o ${LIB}
 	make -r OBJDEPS='${^:%.a=}' $@.target
 
+MondriaanStats: MondriaanStats.o ${LIB}
+	make -r OBJDEPS='${^:%.a=}' $@.target
+
 MatlabMondriaan: MatlabMondriaan.mex.o MatlabHelper.mex.o ${LIB}
 	make -r OBJDEPS='${^:%.a=}' $@.${MEXSUFFIX}
 
diff --git a/tools/Mondriaan.defaults b/tools/Mondriaan.defaults
index 1176264f23e369444a444e2988b83d9336f5a285..c344e22c9a352ba7f546df83c6b6fea7b7704b76 100644
--- a/tools/Mondriaan.defaults
+++ b/tools/Mondriaan.defaults
@@ -7,6 +7,9 @@ SplitMethod                                    KLFM
 Partitioner                                    mondriaan 
 Metric                                         lambda1 
 Discard_Free_Nets                              yes 
+ZeroVolumeSearch                               yes 
+ImproveFreeNonzeros                            yes 
+CheckUpperBound                                yes 
 SquareMatrix_DistributeVectorsEqual            no 
 SquareMatrix_DistributeVectorsEqual_AddDummies yes 
 SymmetricMatrix_UseSingleEntry                 no 
diff --git a/tools/MondriaanStats.c b/tools/MondriaanStats.c
new file mode 100644
index 0000000000000000000000000000000000000000..9dbe3db967d096f9195c4387467a6b0ffe77fb42
--- /dev/null
+++ b/tools/MondriaanStats.c
@@ -0,0 +1,382 @@
+#include <Mondriaan.h>
+
+void PrintStatsHelp(int argc, char **argv);
+
+long *ReadVectorFile(char *filename, int dir, struct sparsematrix *pA, int inspecting) {
+	FILE *file = fopen(filename, "r");
+	if (file) {
+		long vecLen;
+		int vecP;
+		long *vec = ReadVector(0, &vecLen, &vecP, file);
+		if(vecLen != ((dir==COL)?pA->m:pA->n) || vecP != pA->NrProcs) {
+			fprintf(stderr, "main(): Invalid vector size!\n");
+			exit(-1);
+		}
+		fclose(file);
+		return vec;
+	}
+	else if(!inspecting) {
+		fprintf(stderr, "main(): Unable to open '%s' for reading!\n", filename);
+		exit(-1);
+	}
+	return NULL;
+}
+
+
+/* This function calculates the total communication volume for
+ * a given distributed sparse matrix A, as produced by MondriaanOpt.
+ * Typically these files end with -I2f, and contain a distribution
+ * over two processors with a third virtual processor containing the
+ * free nonzeros.
+ * Note that these numbers are for only one direction (vector).
+
+ * Input:
+ *    A distributed matrix,
+ *    dir = ROW (for distribution of v) or dir = COL (for u).
+ * Output:
+ *    ComVol is total communication volume for direction dir,
+ */
+int CalcComI2f(const struct sparsematrix *pM, int dir, long *ComVol) {
+	
+	int q, *procs;
+	long l=0, j, t, *index;
+	
+	if (!pM || !ComVol) {
+		fprintf(stderr, "CalcComI2f(): Null arguments!\n");
+		return FALSE;
+	}
+	
+	if (dir == COL) {
+		l = pM->m;
+		index = pM->i;
+	} else if (dir == ROW) {
+		l = pM->n;
+		index = pM->j;
+	} else {
+		fprintf(stderr, "CalcComI2f(): Unknown direction!\n");
+		return FALSE;
+	}
+	
+	procs = (int *)calloc(l, sizeof(int));
+	if (procs == NULL) {
+		fprintf(stderr, "CalcComI2f(): Not enough memory!\n");
+		return FALSE;
+	}
+
+	q=0;
+	for (t=0; t<pM->NrNzElts; t++) {
+		while(t == pM->Pstart[q+1])
+			q++;  /* this terminates because t != pM->Pstart[P] = pM->NrNzElts */
+
+		j = index[t];
+		procs[j] |= (q+1);
+	}
+	
+	*ComVol = 0;
+	for (j=0; j<l; j++) {
+		if(procs[j] == 3) {
+			++(*ComVol);
+		}
+	}
+	
+	free(procs);
+	
+	return TRUE;
+} /* end CalcComI2f */
+
+/* This function calculates the imbalance for
+ * a given distributed sparse matrix A, as produced by MondriaanOpt.
+ * Typically these files end with -I2f, and contain a distribution
+ * over two processors with a third virtual processor containing the
+ * free nonzeros.
+
+ * Input:
+ *    A distributed matrix,
+ * Output:
+ *    Imbalance is the best achievable imbalance
+ *    ImbalanceOpt is the best achievable imbalance as defined by MondriaanOpt
+ */
+int CalcImbalanceI2f(const struct sparsematrix *pM, double *Imbalance, double *ImbalanceOpt) {
+	
+	int q;
+	
+	if (!pM || !Imbalance || !ImbalanceOpt) {
+		fprintf(stderr, "CalcComI2f(): Null arguments!\n");
+		return FALSE;
+	}
+	
+	long weights[pM->NrProcs];
+	for(q=0; q<pM->NrProcs; ++q) {
+		weights[q] = pM->Pstart[q+1] - pM->Pstart[q];
+	}
+	
+	long smallWeight = (weights[0]<weights[1])?weights[0]:weights[1];
+	long largeWeight = (weights[0]>weights[1])?weights[0]:weights[1];
+	long freeWeight = (pM->NrProcs==3)?weights[2]:0;
+	
+	long diffWeight = largeWeight - smallWeight;
+	long addWeight = (diffWeight<freeWeight)?diffWeight:freeWeight;
+	smallWeight += addWeight;
+	freeWeight -= addWeight;
+	
+	smallWeight += freeWeight/2;
+	largeWeight += (freeWeight+1)/2;
+	
+	*Imbalance = (2*largeWeight)/(double)pM->NrNzElts - 1;
+	*ImbalanceOpt = (2*largeWeight)/(double)(pM->NrNzElts + (pM->NrNzElts%2)) - 1;
+	
+	return TRUE;
+} /* end CalcImbalanceI2f */
+
+
+int main(int argc, char **argv) {
+	
+	if(argc < 2 || !strcmp(argv[1], "-h")) {
+		PrintStatsHelp(argc, argv);
+		exit((argc < 2)? 1 : 0);
+	}
+	
+	/* Read options */
+	int valuesAsProcs = FALSE, I2f = FALSE;
+	int auto_uv = TRUE, auto_I2f = TRUE;
+	int i;
+	char *filename_u = NULL, *filename_v = NULL;
+	for(i=2; i<argc; ++i) {
+		if(!strcmp(argv[i], "-I")) {
+			valuesAsProcs = TRUE;
+		}
+		if(!strcmp(argv[i], "-h")) {
+			PrintStatsHelp(argc, argv);
+			exit(0);
+		}
+		if(!strcmp(argv[i], "-I2f")) {
+			valuesAsProcs = TRUE;
+			I2f = TRUE;
+		}
+		if(!strcmp(argv[i], "-no-auto-uv")) {
+			auto_uv = FALSE;
+		}
+		if(!strcmp(argv[i], "-no-auto-I2f")) {
+			auto_I2f = FALSE;
+		}
+		if(!strcmp(argv[i], "-u") && i+1<argc) {
+			filename_u = argv[i+1];
+			++i;
+		}
+		if(!strcmp(argv[i], "-v") && i+1<argc) {
+			filename_v = argv[i+1];
+			++i;
+		}
+	}
+	
+	
+	char *filename = argv[1];
+	FILE *File;
+	struct sparsematrix A;
+	
+	/* Read matrix file from disk or standard input. */
+	if (!strcmp(filename, "-") || !strcmp(filename, "stdin")) {
+		if (!MMReadSparseMatrix(stdin, &A)) {
+			fprintf(stderr, "main(): Could not read matrix from standard input!\n");
+			exit(-1);
+		}
+		
+		filename = "stdin";
+	}
+	else {
+		
+		File = fopen(filename, "r");
+		
+		if (!File) {
+			fprintf(stderr, "main(): Unable to open '%s' for reading!\n", filename);
+			exit(-1);
+		}
+		
+		if (!MMReadSparseMatrix(File, &A)) {
+			fprintf(stderr, "main(): Could not read matrix!\n");
+			exit(-1);
+		}
+		
+		fclose(File);
+	}
+	
+	/* If required, interpret matrix values as processor indices */
+	if(valuesAsProcs) {
+		if(!SpMatValuesToProcessorIndices(&A)) {
+			fprintf(stderr, "main(): Could not interpret values as processor indices!\n");
+			exit(-1);
+		}
+		
+		if(!I2f && auto_I2f && !strcmp(strrchr(filename, '-'), "-I2f")) {
+			fprintf(stderr, " -- Detected I2f format.\n");
+			I2f = TRUE;
+		}
+	}
+	
+	if(A.MMTypeCode[0]!='D') {
+		fprintf(stderr, "main(): Input matrix must be distributed!\n");
+		exit(-1);
+	}
+	
+	/* If matrix is symmetric, turn it to full form */
+	if(A.m == A.n && (A.MMTypeCode[3] == 'S' || A.MMTypeCode[3] == 'K' || A.MMTypeCode[3] == 'H')) {
+		if(!SparseMatrixSymmetric2Full(&A)) {
+			fprintf(stderr, "main(): Could not transform symmetric matrix to full format!\n");
+			exit(-1);
+		}
+	}
+	
+	/* If needed, try to automatically determine vector distribution files */
+	long *u = NULL, *v = NULL;
+	if(auto_uv && !I2f) {
+		char basename[MAX_WORD_LENGTH], filename_vec[MAX_WORD_LENGTH];
+		strcpy(basename, filename);
+		char *suffix = strrchr(basename, '-');
+		if(suffix != NULL) {
+			*suffix = '\0';
+		}
+		
+		if(filename_u == NULL) {
+			sprintf(filename_vec, "%s-u%d", basename, A.NrProcs);
+			u = ReadVectorFile(filename_vec, COL, &A, TRUE);
+			if(u != NULL) {
+				fprintf(stderr, " -- Found %s\n", filename_vec);
+			}
+		}
+		
+		if(filename_v == NULL) {
+			sprintf(filename_vec, "%s-v%d", basename, A.NrProcs);
+			v = ReadVectorFile(filename_vec, ROW, &A, TRUE);
+			if(v != NULL) {
+				fprintf(stderr, " -- Found %s\n", filename_vec);
+			}
+		}
+	}
+	
+	/* Read manually passed u/v files */
+	if(filename_u != NULL) {
+		u = ReadVectorFile(filename_u, COL, &A, FALSE);
+	}
+	if(filename_v != NULL) {
+		v = ReadVectorFile(filename_v, ROW, &A, FALSE);
+	}
+	
+	/* Print general statistics */
+	printf("Matrix:\n");
+	printf("  m             : %ld\n", A.m);
+	printf("  n             : %ld\n", A.n);
+	printf("  nnz           : %ld\n", A.NrNzElts);
+	printf("  P             : %d\n", I2f?2:A.NrProcs);
+	printf("\n");
+	
+	if(I2f) {
+		/* Print weights */
+		printf("Weights:\n");
+		printf("  proc 0        : %ld\n", A.Pstart[1]-A.Pstart[0]);
+		printf("  proc 1        : %ld\n", A.Pstart[2]-A.Pstart[1]);
+		printf("  free          : %ld\n", (A.NrProcs==3)?A.Pstart[3]-A.Pstart[2]:0);
+		printf("\n");
+		
+		/* Print superstep v */
+		long ComVolRow;
+		CalcComI2f(&A, ROW, &ComVolRow);
+		printf("Communication for vector v:\n");
+		printf("  vol           : %ld\n", ComVolRow);
+		printf("  avg = vol/P   : %g\n", ComVolRow/2.0);
+		printf("\n");
+		
+		/* Print superstep u */
+		long ComVolCol;
+		CalcComI2f(&A, COL, &ComVolCol);
+		printf("Communication for vector u:\n");
+		printf("  vol           : %ld\n", ComVolCol);
+		printf("  avg = vol/P   : %g\n", ComVolCol/2.0);
+		printf("\n");
+		
+		/* Print total */
+		double Imbalance, ImbalanceOpt;
+		CalcImbalanceI2f(&A, &Imbalance, &ImbalanceOpt);
+		printf("Total:\n");
+		printf("  vol           : %ld\n", ComVolRow+ComVolCol);
+		printf("  avg = vol/P   : %g\n", (ComVolRow+ComVolCol)/2.0);
+		printf("  imbalance     : %g\n", Imbalance);
+		printf("  imbalance(opt): %g\n", ImbalanceOpt);
+		printf("\n");
+		
+	}
+	else {
+		/* Print weights */
+		printf("Weights & imbalances:\n");
+		long maxWeight = 0, weight;
+		for(int q=0; q<A.NrProcs; ++q) {
+			weight = A.Pstart[q+1] - A.Pstart[q];
+			if(weight > maxWeight) {
+				maxWeight = weight;
+			}
+			printf("  proc %-9d: %ld (%g)\n", q, weight, weight/(A.NrNzElts/(double)A.NrProcs) -1);
+		}
+		printf("\n");
+		
+		/* Print superstep v */
+		long ComVolRow, MaxOutRow, MaxInRow, MaxCompntsRow, TotCompntsRow;
+		CalcCom(&A, v, ROW, &ComVolRow, &MaxOutRow, &MaxInRow, &MaxCompntsRow, &TotCompntsRow);
+		PrintCom(A.NrProcs, A.n, ROW, ComVolRow, MaxOutRow, MaxInRow, MaxCompntsRow, TotCompntsRow);
+		printf("\n");
+		
+		/* Print superstep u */
+		long ComVolCol, MaxOutCol, MaxInCol, MaxCompntsCol, TotCompntsCol;
+		CalcCom(&A, u, COL, &ComVolCol, &MaxOutCol, &MaxInCol, &MaxCompntsCol, &TotCompntsCol);
+		PrintCom(A.NrProcs, A.m, COL, ComVolCol, MaxOutCol, MaxInCol, MaxCompntsCol, TotCompntsCol);
+		printf("\n");
+		
+		/* Print total */
+		long MaxRow = (MaxInRow>MaxOutRow)?MaxInRow:MaxOutRow;
+		long MaxCol = (MaxInCol>MaxOutCol)?MaxInCol:MaxOutCol;
+		double Imbalance = maxWeight/(A.NrNzElts/(double)A.NrProcs) - 1;
+		double ImbalanceOpt = maxWeight/ceil(A.NrNzElts/(double)A.NrProcs) - 1;
+		printf("Total:\n");
+		printf("  vol           : %ld\n", ComVolRow+ComVolCol);
+		printf("  avg = vol/P   : %g\n", (ComVolRow+ComVolCol)/(double)A.NrProcs);
+		printf("  max           : %ld\n", MaxRow+MaxCol);
+		printf("  imbalance     : %g\n", Imbalance);
+		printf("  imbalance(opt): %g\n", ImbalanceOpt);
+		printf("\n");
+	}
+	
+	/* Finish */
+	if(u != NULL) {
+		free(u);
+	}
+	if(v != NULL) {
+		free(v);
+	}
+
+	MMDeleteSparseMatrix(&A);
+	
+	exit(0);
+	
+}
+
+void PrintStatsHelp(int argc, char **argv) {
+
+	printf("\nMondriaan version %s.\n", MONDRIAANVERSION);
+	printf("Usage: %s [matrix] <options>\n\n", (argv && argv[0])?argv[0]:"./MondriaanStats");
+	printf(" [matrix] - the partitioned matrix\n");
+	printf(" <options>:\n");
+	printf(" -I            - Interpret the (real) values of the nonzeros as processor indices.\n");
+	printf("                 Typically used with matrix.mtx-Ix files.\n");
+	printf(" -I2f          - Input file contains a bipartitioning, possibly with free nonzeros.\n");
+	printf("                 Typically used with matrix.mtx-I2f files. -I2f implies -I\n");
+	printf(" -no-auto-uv   - Do not determine the names of the u and v vector files automatically.\n");
+	printf(" -no-auto-I2f  - Do not automatically determine -I2f suffix if -I is passed.\n");
+	printf(" -u <file>     - Use <file> as distribution for u.\n");
+	printf(" -v <file>     - Use <file> as distribution for v.\n");
+	printf(" -h            - Show this help.\n");
+	printf("\n");
+	printf("When no options are given, the input matrix is assumed to be a distributed-matrix.\n");
+	printf("\n");
+	fflush(stdout);
+
+} /* end PrintHelp */
+
+