Revision fa16d46ab0546a7fde5db17c4c4198afad45e5b1 authored by Steffen Boerm on 27 August 2022, 18:33:25 UTC, committed by Steffen Boerm on 27 August 2022, 18:33:25 UTC
1 parent 71b877f
Raw File
hmatrix.c
/* ------------------------------------------------------------
 * This is the file "hmatrix.c" of the H2Lib package.
 * All rights reserved, Steffen Boerm 2010
 * ------------------------------------------------------------ */

#include <stdio.h>

#ifdef USE_NETCDF
#include <string.h>
#include <netcdf.h>
#endif

#include "hmatrix.h"
#include "basic.h"

/* ------------------------------------------------------------
 * Constructors and destructors
 * ------------------------------------------------------------ */

phmatrix
init_hmatrix(phmatrix hm, pccluster rc, pccluster cc)
{
  hm->rc = rc;
  hm->cc = cc;

  hm->r = NULL;
  hm->f = NULL;

  hm->son = NULL;
  hm->rsons = 0;
  hm->csons = 0;

  hm->refs = 0;
  hm->desc = 0;

  return hm;
}

void
uninit_hmatrix(phmatrix hm)
{
  uint      rsons = hm->rsons;
  uint      csons = hm->csons;
  uint      i, j;

  assert(hm->refs == 0);

  if (hm->son) {
    for (j = 0; j < csons; j++)
      for (i = 0; i < rsons; i++)
	unref_hmatrix(hm->son[i + j * rsons]);
    freemem(hm->son);
  }

  if (hm->f)
    del_amatrix(hm->f);

  if (hm->r)
    del_rkmatrix(hm->r);
}

phmatrix
new_hmatrix(pccluster rc, pccluster cc)
{
  phmatrix  hm;

  hm = allocmem(sizeof(hmatrix));

  init_hmatrix(hm, rc, cc);

  return hm;
}

phmatrix
new_rk_hmatrix(pccluster rc, pccluster cc, uint k)
{
  phmatrix  hm;

  hm = new_hmatrix(rc, cc);

  hm->r = new_rkmatrix(rc->size, cc->size, k);

  hm->desc = 1;

  return hm;
}

phmatrix
new_full_hmatrix(pccluster rc, pccluster cc)
{
  phmatrix  hm;

  hm = new_hmatrix(rc, cc);

  hm->f = new_amatrix(rc->size, cc->size);

  hm->desc = 1;

  return hm;
}

phmatrix
new_super_hmatrix(pccluster rc, pccluster cc, uint rsons, uint csons)
{
  phmatrix  hm;
  uint      i, j;

  hm = new_hmatrix(rc, cc);

  hm->rsons = rsons;
  hm->csons = csons;

  hm->son = (phmatrix *) allocmem((size_t) sizeof(phmatrix) * rsons * csons);
  for (j = 0; j < csons; j++)
    for (i = 0; i < rsons; i++)
      hm->son[i + j * rsons] = NULL;

  return hm;
}

phmatrix
clonestructure_hmatrix(pchmatrix src)
{
  const uint rsons = src->rsons;
  const uint csons = src->csons;

  phmatrix  hm1;
  uint      i, j;

  phmatrix  hm;

  if (src->son != NULL) {
    hm = new_super_hmatrix(src->rc, src->cc, rsons, csons);
    for (j = 0; j < csons; ++j) {
      for (i = 0; i < rsons; ++i) {
	hm1 = clonestructure_hmatrix(src->son[i + j * rsons]);
	ref_hmatrix(hm->son + i + j * rsons, hm1);
      }
    }
  }
  else if (src->r != NULL) {
    hm = new_rk_hmatrix(src->rc, src->cc, src->r->k);
  }
  else {
    assert(src->f != NULL);
    hm = new_full_hmatrix(src->rc, src->cc);
  }

  update_hmatrix(hm);

  return hm;
}

phmatrix
clone_hmatrix(pchmatrix src)
{
  const uint rsons = src->rsons;
  const uint csons = src->csons;

  phmatrix  hm1;
  uint      i, j;

  phmatrix  hm;

  if (src->son != NULL) {
    hm = new_super_hmatrix(src->rc, src->cc, rsons, csons);
    for (j = 0; j < csons; ++j) {
      for (i = 0; i < rsons; ++i) {
	hm1 = clone_hmatrix(src->son[i + j * rsons]);
	ref_hmatrix(hm->son + i + j * rsons, hm1);
      }
    }
    update_hmatrix(hm);
  }
  else if (src->r != NULL) {
    hm = new_rk_hmatrix(src->rc, src->cc, src->r->k);
    copy_amatrix(false, &src->r->A, &hm->r->A);
    copy_amatrix(false, &src->r->B, &hm->r->B);
  }
  else {
    assert(src->f != NULL);
    hm = new_full_hmatrix(src->rc, src->cc);
    copy_amatrix(false, src->f, hm->f);
  }

  update_hmatrix(hm);

  return hm;
}

void
update_hmatrix(phmatrix hm)
{
  uint      desc;
  uint      rsons, csons;
  uint      i, j;

  desc = 1;

  if (hm->son) {
    rsons = hm->rsons;
    csons = hm->csons;

    for (j = 0; j < csons; j++)
      for (i = 0; i < rsons; i++)
	desc += hm->son[i + j * rsons]->desc;
  }

  hm->desc = desc;
}

void
del_hmatrix(phmatrix hm)
{
  uninit_hmatrix(hm);

  freemem(hm);
}

/* ------------------------------------------------------------
 * Reference counting
 * ------------------------------------------------------------ */

void
ref_hmatrix(phmatrix *ptr, phmatrix hm)
{
  if (*ptr)
    unref_hmatrix(*ptr);

  *ptr = hm;

  if (hm)
    hm->refs++;
}

void
unref_hmatrix(phmatrix hm)
{
  assert(hm->refs > 0);

  hm->refs--;

  if (hm->refs == 0)
    del_hmatrix(hm);
}

/* ------------------------------------------------------------
 * Statistics
 * ------------------------------------------------------------ */

size_t
getsize_hmatrix(pchmatrix hm)
{
  size_t    sz;
  uint      rsons = hm->rsons;
  uint      csons = hm->csons;
  uint      i, j;

  sz = (size_t) sizeof(hmatrix);

  if (hm->r)
    sz += getsize_rkmatrix(hm->r);

  if (hm->f)
    sz += getsize_amatrix(hm->f);

  for (j = 0; j < csons; j++)
    for (i = 0; i < rsons; i++)
      sz += getsize_hmatrix(hm->son[i + j * rsons]);

  return sz;
}

size_t
getnearsize_hmatrix(pchmatrix hm)
{
  size_t    sz;
  uint      rsons = hm->rsons;
  uint      csons = hm->csons;
  uint      i, j;

  sz = 0;

  if (hm->f)
    sz += getsize_amatrix(hm->f);

  for (j = 0; j < csons; j++)
    for (i = 0; i < rsons; i++)
      sz += getnearsize_hmatrix(hm->son[i + j * rsons]);

  return sz;
}

size_t
getfarsize_hmatrix(pchmatrix hm)
{
  size_t    sz;
  uint      rsons = hm->rsons;
  uint      csons = hm->csons;
  uint      i, j;

  sz = 0;

  if (hm->r)
    sz += getsize_rkmatrix(hm->r);

  for (j = 0; j < csons; j++)
    for (i = 0; i < rsons; i++)
      sz += getfarsize_hmatrix(hm->son[i + j * rsons]);

  return sz;
}

/* ------------------------------------------------------------
 * Simple utility functions
 * ------------------------------------------------------------ */

void
clear_hmatrix(phmatrix hm)
{
  uint      rsons, csons;
  uint      i, j;

  if (hm->son) {
    rsons = hm->rsons;
    csons = hm->csons;

    for (j = 0; j < csons; j++) {
      for (i = 0; i < rsons; i++) {
	clear_hmatrix(hm->son[i + j * rsons]);
      }
    }
  }
  else if (hm->r) {
    setrank_rkmatrix(hm->r, 0);
  }
  else {
    assert(hm->f);
    clear_amatrix(hm->f);
  }
}

void
clear_upper_hmatrix(phmatrix hm, bool strict)
{
  uint      rsons, csons;
  uint      i, j;

  if (hm->son) {
    rsons = hm->rsons;
    csons = hm->csons;

    for (j = 0; j < csons; j++) {
      for (i = 0; i < UINT_MIN(j, rsons); i++) {
	clear_hmatrix(hm->son[i + j * rsons]);
      }
      if (j < rsons) {
	clear_upper_hmatrix(hm->son[j + j * rsons], strict);
      }
    }
  }
  else if (hm->r) {
    setrank_rkmatrix(hm->r, 0);
  }
  else {
    assert(hm->f);
    assert(hm->rc == hm->cc);
    clear_upper_amatrix(hm->f, strict);
  }
}

void
copy_hmatrix(pchmatrix src, phmatrix trg)
{
  const uint rsons = src->rsons;
  const uint csons = src->csons;

  uint      i, j;

  assert(rsons == trg->rsons);
  assert(csons == trg->csons);
  assert(src->rc == trg->rc);
  assert(src->cc == trg->cc);

  if (src->son != NULL) {
    for (j = 0; j < csons; ++j) {
      for (i = 0; i < rsons; ++i) {
	copy_hmatrix(src->son[i + j * rsons], trg->son[i + j * rsons]);
      }
    }
  }
  else if (src->r != NULL) {
    resize_rkmatrix(trg->r, src->rc->size, src->cc->size, src->r->k);
    copy_amatrix(false, &src->r->A, &trg->r->A);
    copy_amatrix(false, &src->r->B, &trg->r->B);
  }
  else if (src->f != NULL) {
    copy_amatrix(false, src->f, trg->f);
  }
}

void
identity_hmatrix(phmatrix hm)
{
  uint      rsons, csons;
  uint      i, j;

  if (hm->son) {
    rsons = hm->rsons;
    csons = hm->csons;

    for (j = 0; j < csons; j++) {
      for (i = 0; i < rsons; i++) {
	identity_hmatrix(hm->son[i + j * rsons]);
      }
    }
  }
  else if (hm->r) {
    setrank_rkmatrix(hm->r, 0);
  }
  else {
    assert(hm->f);
    if (hm->rc == hm->cc) {
      identity_amatrix(hm->f);
    }
    else {
      clear_amatrix(hm->f);
    }
  }
}

void
random_hmatrix(phmatrix hm, uint kmax)
{
  uint      rsons = hm->rsons;
  uint      csons = hm->csons;

  uint      i, j;

  if (hm->son != NULL) {
    for (j = 0; j < csons; ++j) {
      for (i = 0; i < rsons; ++i) {
	random_hmatrix(hm->son[i + j * rsons], kmax);
      }
    }
  }
  else if (hm->r != NULL) {
    resize_rkmatrix(hm->r, hm->rc->size, hm->cc->size, kmax);
    random_amatrix(&hm->r->A);
    random_amatrix(&hm->r->B);
  }
  else {
    assert(hm->f != NULL);
    random_amatrix(hm->f);
  }
}

void
scale_hmatrix(field alpha, phmatrix hm)
{
  uint      rsons = hm->rsons;
  uint      csons = hm->csons;
  uint      i, j;

  if (hm->son) {
    for (j = 0; j < csons; j++)
      for (i = 0; i < rsons; i++)
	scale_hmatrix(alpha, hm->son[i + j * rsons]);
  }
  else if (hm->r)
    scale_rkmatrix(alpha, hm->r);
  else if (hm->f)
    scale_amatrix(alpha, hm->f);
}

/* ------------------------------------------------------------
 * Build H-matrix based on block tree
 * ------------------------------------------------------------ */

phmatrix
build_from_block_hmatrix(pcblock b, uint k)
{
  phmatrix  h, h1;
  pcblock   b1;
  int       rsons, csons;
  int       i, j;

  h = NULL;

  if (b->son) {
    rsons = b->rsons;
    csons = b->csons;

    h = new_super_hmatrix(b->rc, b->cc, rsons, csons);

    for (j = 0; j < csons; j++) {
      for (i = 0; i < rsons; i++) {
	b1 = b->son[i + j * rsons];

	h1 = build_from_block_hmatrix(b1, k);

	ref_hmatrix(h->son + i + j * rsons, h1);
      }
    }
  }
  else if (b->a > 0)
    h = new_rk_hmatrix(b->rc, b->cc, k);
  else
    h = new_full_hmatrix(b->rc, b->cc);

  update_hmatrix(h);

  return h;
}

/* ------------------------------------------------------------
 * Build block tree from H-matrix
 * ------------------------------------------------------------ */

pblock
build_from_hmatrix_block(pchmatrix G)
{
  uint      i, j, rsons, csons;
  pblock    b;

  b = 0;
  if (G->son) {
    rsons = G->rsons;
    csons = G->csons;

    b = new_block((pcluster) G->rc, (pcluster) G->cc, false, rsons, csons);

    for (j = 0; j < csons; j++) {
      for (i = 0; i < rsons; i++) {
	b->son[i + j * rsons] =
	  build_from_hmatrix_block(G->son[i + j * rsons]);
      }
    }
  }
  else if (G->r) {
    b = new_block((pcluster) G->rc, (pcluster) G->cc, true, 0, 0);
  }
  else {
    assert(G->f);
    b = new_block((pcluster) G->rc, (pcluster) G->cc, false, 0, 0);
  }

  update_block(b);

  return b;
}

/* ------------------------------------------------------------
 * Matrix-vector multiplication
 * ------------------------------------------------------------ */

void
mvm_hmatrix_avector(field alpha, bool atrans, pchmatrix a, pcavector x,
		    pavector y)
{
  if (atrans)
    addevaltrans_hmatrix_avector(alpha, a, x, y);
  else
    addeval_hmatrix_avector(alpha, a, x, y);
}

void
fastaddeval_hmatrix_avector(field alpha, pchmatrix hm, pcavector x,
			    pavector y)
{
  pavector  x1, y1;
  avector   xtmp, ytmp;
  uint      rsons, csons;
  uint      xoff, yoff, i, j;

  assert(x->dim == hm->cc->size);
  assert(y->dim == hm->rc->size);

  if (hm->r) {
    addeval_rkmatrix_avector(alpha, hm->r, x, y);
  }
  else if (hm->f) {
    mvm_amatrix_avector(alpha, false, hm->f, x, y);
  }
  else {
    rsons = hm->rsons;
    csons = hm->csons;

    xoff = 0;
    for (j = 0; j < csons; j++) {
      x1 = init_sub_avector(&xtmp, (pavector) x, hm->son[j * rsons]->cc->size,
			    xoff);

      yoff = 0;
      for (i = 0; i < rsons; i++) {
	y1 = init_sub_avector(&ytmp, y, hm->son[i]->rc->size, yoff);

	fastaddeval_hmatrix_avector(alpha, hm->son[i + j * rsons], x1, y1);

	uninit_avector(y1);

	yoff += hm->son[i]->rc->size;
      }
      assert(yoff == hm->rc->size);

      uninit_avector(x1);

      xoff += hm->son[j * rsons]->cc->size;
    }
    assert(xoff == hm->cc->size);
  }
}

void
addeval_hmatrix_avector(field alpha, pchmatrix hm, pcavector x, pavector y)
{
  pavector  xp, yp;
  avector   xtmp, ytmp;
  uint      i, ip;

  assert(x->dim == hm->cc->size);
  assert(y->dim == hm->rc->size);

  /* Permutation of x */
  xp = init_avector(&xtmp, x->dim);
  for (i = 0; i < xp->dim; i++) {
    ip = hm->cc->idx[i];
    assert(ip < x->dim);
    xp->v[i] = x->v[ip];
  }

  /* Permutation of y */
  yp = init_avector(&ytmp, y->dim);
  for (i = 0; i < yp->dim; i++) {
    ip = hm->rc->idx[i];
    assert(ip < y->dim);
    yp->v[i] = y->v[ip];
  }

  /* Matrix-vector multiplication */
  fastaddeval_hmatrix_avector(alpha, hm, xp, yp);

  /* Reverse permutation of y */
  for (i = 0; i < yp->dim; i++) {
    ip = hm->rc->idx[i];
    assert(ip < y->dim);
    y->v[ip] = yp->v[i];
  }

  uninit_avector(yp);
  uninit_avector(xp);
}

void
fastaddevaltrans_hmatrix_avector(field alpha, pchmatrix hm, pcavector x,
				 pavector y)
{
  pavector  x1, y1;
  avector   xtmp, ytmp;
  uint      rsons, csons;
  uint      xoff, yoff, i, j;

  assert(x->dim == hm->rc->size);
  assert(y->dim == hm->cc->size);

  if (hm->r) {
    addevaltrans_rkmatrix_avector(alpha, hm->r, x, y);
  }
  else if (hm->f) {
    mvm_amatrix_avector(alpha, true, hm->f, x, y);
  }
  else {
    rsons = hm->rsons;
    csons = hm->csons;

    yoff = 0;
    for (j = 0; j < csons; j++) {
      y1 = init_sub_avector(&ytmp, y, hm->son[j * rsons]->cc->size, yoff);

      xoff = 0;
      for (i = 0; i < rsons; i++) {
	x1 =
	  init_sub_avector(&xtmp, (pavector) x, hm->son[i]->rc->size, xoff);

	fastaddevaltrans_hmatrix_avector(alpha, hm->son[i + j * rsons], x1,
					 y1);

	uninit_avector(x1);

	xoff += hm->son[i]->rc->size;
      }
      assert(xoff == hm->rc->size);

      uninit_avector(y1);

      yoff += hm->son[j * rsons]->cc->size;
    }
    assert(yoff == hm->cc->size);
  }
}

void
addevaltrans_hmatrix_avector(field alpha, pchmatrix hm, pcavector x,
			     pavector y)
{
  pavector  xp, yp;
  avector   xtmp, ytmp;
  uint      i, ip;

  assert(x->dim == hm->rc->size);
  assert(y->dim == hm->cc->size);

  /* Permutation of x */
  xp = init_avector(&xtmp, x->dim);
  for (i = 0; i < xp->dim; i++) {
    ip = hm->rc->idx[i];
    assert(ip < x->dim);
    xp->v[i] = x->v[ip];
  }

  /* Permutation of y */
  yp = init_avector(&ytmp, y->dim);
  for (i = 0; i < yp->dim; i++) {
    ip = hm->cc->idx[i];
    assert(ip < y->dim);
    yp->v[i] = y->v[ip];
  }

  /* Matrix-vector multiplication */
  fastaddevaltrans_hmatrix_avector(alpha, hm, xp, yp);

  /* Reverse permutation of y */
  for (i = 0; i < yp->dim; i++) {
    ip = hm->cc->idx[i];
    assert(ip < y->dim);
    y->v[ip] = yp->v[i];
  }

  uninit_avector(yp);
  uninit_avector(xp);
}

static void
addevalsymm_offdiag(field alpha, pchmatrix hm, uint roff, uint coff,
		    pcavector xp, pavector yp)
{
  avector   tmp1, tmp2;
  pavector  xp1, yp1;
  uint      rsons, csons;
  uint      roff1, coff1;
  uint      i, j;

  if (hm->f) {
    xp1 = init_sub_avector(&tmp1, (pavector) xp, hm->cc->size, coff);
    yp1 = init_sub_avector(&tmp2, yp, hm->rc->size, roff);

    addeval_amatrix_avector(alpha, hm->f, xp1, yp1);

    uninit_avector(yp1);
    uninit_avector(xp1);

    xp1 = init_sub_avector(&tmp1, (pavector) xp, hm->rc->size, roff);
    yp1 = init_sub_avector(&tmp2, yp, hm->cc->size, coff);

    addevaltrans_amatrix_avector(alpha, hm->f, xp1, yp1);

    uninit_avector(yp1);
    uninit_avector(xp1);
  }
  else if (hm->r) {
    xp1 = init_sub_avector(&tmp1, (pavector) xp, hm->cc->size, coff);
    yp1 = init_sub_avector(&tmp2, yp, hm->rc->size, roff);

    addeval_rkmatrix_avector(alpha, hm->r, xp1, yp1);

    uninit_avector(yp1);
    uninit_avector(xp1);

    xp1 = init_sub_avector(&tmp1, (pavector) xp, hm->rc->size, roff);
    yp1 = init_sub_avector(&tmp2, yp, hm->cc->size, coff);

    addevaltrans_rkmatrix_avector(alpha, hm->r, xp1, yp1);

    uninit_avector(yp1);
    uninit_avector(xp1);
  }
  else {
    assert(hm->son != 0);

    rsons = hm->rsons;
    csons = hm->csons;

    coff1 = coff;
    for (j = 0; j < csons; j++) {
      roff1 = roff;

      for (i = 0; i < rsons; i++) {
	addevalsymm_offdiag(alpha, hm->son[i + j * rsons], roff1, coff1, xp,
			    yp);

	roff1 += hm->son[i]->rc->size;
      }
      assert(roff1 == roff + hm->rc->size);

      coff1 += hm->son[j * rsons]->cc->size;
    }
    assert(coff1 == coff + hm->cc->size);
  }
}

static void
addevalsymm_diag(field alpha, pchmatrix hm, uint off, pcavector xp,
		 pavector yp)
{
  avector   tmp1, tmp2;
  pavector  xp1, yp1;
  pfield    aa;
  uint      lda, sons;
  uint      roff, coff;
  uint      n;
  uint      i, j;

  assert(hm->rc == hm->cc);

  if (hm->f) {
    assert(hm->rc->size == hm->f->rows);
    assert(hm->cc->size == hm->f->cols);

    aa = hm->f->a;
    lda = hm->f->ld;

    n = hm->rc->size;
    xp1 = init_sub_avector(&tmp1, (pavector) xp, n, off);
    yp1 = init_sub_avector(&tmp2, yp, n, off);

    for (j = 0; j < n; j++) {
      yp1->v[j] += alpha * aa[j + j * lda] * xp1->v[j];
      for (i = j + 1; i < n; i++) {
	yp1->v[i] += alpha * aa[i + j * lda] * xp1->v[j];
	yp1->v[j] += alpha * CONJ(aa[i + j * lda]) * xp1->v[i];
      }
    }

    uninit_avector(yp1);
    uninit_avector(xp1);
  }
  else {
    assert(hm->son != 0);
    assert(hm->rsons == hm->csons);

    sons = hm->rsons;

    coff = off;
    for (j = 0; j < sons; j++) {
      roff = coff;

      addevalsymm_diag(alpha, hm->son[j + j * sons], coff, xp, yp);

      roff += hm->rc->son[j]->size;
      for (i = j + 1; i < sons; i++) {
	addevalsymm_offdiag(alpha, hm->son[i + j * sons], roff, coff, xp, yp);

	roff += hm->son[i]->rc->size;
      }
      assert(roff == off + hm->rc->size);

      coff += hm->son[j * sons]->cc->size;
    }
    assert(coff == off + hm->rc->size);
  }
}

void
fastaddevalsymm_hmatrix_avector(field alpha, pchmatrix hm, pcavector xp,
				pavector yp)
{
  assert(hm->rc == hm->cc);

  addevalsymm_diag(alpha, hm, 0, xp, yp);
}

void
addevalsymm_hmatrix_avector(field alpha, pchmatrix hm, pcavector x,
			    pavector y)
{
  pavector  xp, yp;
  avector   xtmp, ytmp;
  uint      n;
  const uint *idx;
  uint      i, ip;

  assert(hm->rc == hm->cc);
  assert(x->dim == hm->cc->size);
  assert(y->dim == hm->rc->size);

  n = hm->rc->size;
  idx = hm->rc->idx;

  /* Permutation of x */
  xp = init_avector(&xtmp, n);
  for (i = 0; i < n; i++) {
    ip = idx[i];
    assert(ip < n);
    xp->v[i] = x->v[ip];
  }

  /* Permutation of y */
  yp = init_avector(&ytmp, n);
  for (i = 0; i < n; i++) {
    ip = idx[i];
    assert(ip < n);
    yp->v[i] = y->v[ip];
  }

  /* Matrix-vector multiplication */
  fastaddevalsymm_hmatrix_avector(alpha, hm, xp, yp);

  /* Reverse permutation of y */
  for (i = 0; i < n; i++) {
    ip = idx[i];
    assert(ip < n);
    y->v[ip] = yp->v[i];
  }

  /* Clean up */
  uninit_avector(yp);
  uninit_avector(xp);
}

/* ------------------------------------------------------------
 * Enumeration
 * ------------------------------------------------------------ */

static void
enumerate(pcblock b, uint bname, phmatrix hm, phmatrix *hn)
{
  uint      bname1;
  uint      i, j;

  assert(hm->rc == b->rc);
  assert(hm->cc == b->cc);

  hn[bname] = hm;

  bname1 = bname + 1;

  if (hm == 0 || hm->son == 0)
    for (j = 0; j < b->csons; j++)
      for (i = 0; i < b->rsons; i++) {
	enumerate(b->son[i + j * b->rsons], bname1, 0, hn);

	bname1 += b->son[i + j * b->rsons]->desc;
      }
  else {
    assert(b->rsons == hm->rsons);
    assert(b->csons == hm->csons);

    for (j = 0; j < b->csons; j++)
      for (i = 0; i < b->rsons; i++) {
	enumerate(b->son[i + j * b->rsons], bname1, hm->son[i + j * b->rsons],
		  hn);

	bname1 += b->son[i + j * b->rsons]->desc;
      }
  }
  assert(bname1 == bname + b->desc);
}

phmatrix *
enumerate_hmatrix(pcblock b, phmatrix hm)
{
  phmatrix *hn;

  hn = (phmatrix *) allocmem((size_t) sizeof(phmatrix) * b->desc);

  enumerate(b, 0, hm, hn);

  return hn;
}

/* ------------------------------------------------------------
 * Simple utility functions
 * ------------------------------------------------------------ */

real
norm2_hmatrix(pchmatrix H)
{
  return norm2_matrix((mvm_t) mvm_hmatrix_avector, (void *) H, H->rc->size,
		      H->cc->size);
}

real
norm2diff_hmatrix(pchmatrix a, pchmatrix b)
{
  return norm2diff_matrix((mvm_t) mvm_hmatrix_avector, (void *) a,
			  (mvm_t) mvm_hmatrix_avector, (void *) b,
			  a->rc->size, a->cc->size);
}

/* ------------------------------------------------------------
 * File I/O
 * ------------------------------------------------------------ */

#ifdef USE_NETCDF
static void
write_count(pchmatrix G, size_t * blocks, size_t * coeffs)
{
  uint      rsons, csons;
  uint      i, j;

  /* Increase submatrix counter */
  (*blocks)++;

  if (G->f) {
    /* If it's an amatrix: add number of coefficients to coeffs */
    (*coeffs) += G->f->rows * G->f->cols;
  }
  else if (G->r) {
    /* If it's an rkmatrix: also add number of coefficients to coeffs */
    (*coeffs) +=
      (getrows_rkmatrix(G->r) +
       getcols_rkmatrix(G->r)) * getrank_rkmatrix(G->r);
  }
  else if (G->son) {
    rsons = G->rsons;
    csons = G->csons;

    /* If it's subdivided: check sons */
    for (j = 0; j < csons; j++)
      for (i = 0; i < rsons; i++)
	write_count(G->son[i + j * rsons], blocks, coeffs);
  }
}

static void
write_cdf(pchmatrix G, size_t blocks, size_t coeffs,
	  size_t * blockidx, size_t * coeffidx,
	  int nc_file,
	  int nc_type, int nc_rows, int nc_cols, int nc_rank,
	  int nc_rsons, int nc_csons, int nc_coeff)
{
  size_t    start, count;
  ptrdiff_t stride;
  pamatrix  f;
  prkmatrix r;
  uint      rsons, csons;
  uint      rows, cols, k;
  uint      lda, ldb, ldf;
  int       val;
  uint      i, j;

  assert(*blockidx <= blocks);

  rows = getrows_hmatrix(G);
  cols = getcols_hmatrix(G);

  /* Write number of rows to nc_rows[*blockidx] */
  start = *blockidx;
  count = 1;
  stride = 1;
  val = rows;
  nc_put_vars(nc_file, nc_rows, &start, &count, &stride, &val);

  /* Write number of columns to nc_cols[*blockidx] */
  val = cols;
  nc_put_vars(nc_file, nc_cols, &start, &count, &stride, &val);

  if (G->f) {
    f = G->f;
    ldf = f->ld;
    assert(f->rows == rows);
    assert(f->cols == cols);

    /* Write type 1 to nc_type[*blockidx] */
    val = 1;
    nc_put_vars(nc_file, nc_type, &start, &count, &stride, &val);

    /* An amatrix has no rank, rsons, or csons */
    val = 0;
    nc_put_vars(nc_file, nc_rank, &start, &count, &stride, &val);
    nc_put_vars(nc_file, nc_rsons, &start, &count, &stride, &val);
    nc_put_vars(nc_file, nc_csons, &start, &count, &stride, &val);

    /* Write coefficients */
    start = *coeffidx;
    count = rows;
    assert(start + rows * cols <= coeffs);
    for (j = 0; j < cols; j++) {
      nc_put_vars(nc_file, nc_coeff, &start, &count, &stride, f->a + j * ldf);
      start += rows;
    }
    *coeffidx = start;

    /* Increase blocks counter */
    (*blockidx)++;
  }
  else if (G->r) {
    r = G->r;
    lda = r->A.ld;
    ldb = r->B.ld;
    k = r->A.cols;
    assert(r->B.cols == k);
    assert(r->A.rows == rows);
    assert(r->B.rows == cols);

    /* Write type 2 to nc_type[*blockidx] */
    val = 2;
    nc_put_vars(nc_file, nc_type, &start, &count, &stride, &val);

    /* Write rank to nc_rank[*blockidx] */
    val = k;
    nc_put_vars(nc_file, nc_rank, &start, &count, &stride, &val);

    /* An rkmatrix has no rsons, or csons */
    val = 0;
    nc_put_vars(nc_file, nc_rsons, &start, &count, &stride, &val);
    nc_put_vars(nc_file, nc_csons, &start, &count, &stride, &val);

    /* Write coefficients of A */
    start = *coeffidx;
    count = rows;
    assert(start + rows * k <= coeffs);
    for (j = 0; j < k; j++) {
      nc_put_vars(nc_file, nc_coeff, &start, &count, &stride,
		  r->A.a + j * lda);
      start += rows;
    }

    /* Write coefficients of B */
    count = cols;
    assert(start + cols * k <= coeffs);
    for (j = 0; j < k; j++) {
      nc_put_vars(nc_file, nc_coeff, &start, &count, &stride,
		  r->B.a + j * ldb);
      start += cols;
    }
    *coeffidx = start;

    /* Increase submatrix counter */
    (*blockidx)++;
  }
  else if (G->son) {
    rsons = G->rsons;
    csons = G->csons;

    /* Write type 3 to nc_type */
    val = 3;
    nc_put_vars(nc_file, nc_type, &start, &count, &stride, &val);

    /* Subdivided matrices have no rank */
    val = 0;
    nc_put_vars(nc_file, nc_rank, &start, &count, &stride, &val);

    /* Write rsons and csons.
     * rsons == 0 means that the sons use the same row cluster
     * as the father. The same convention is used for csons. */
    val = (G->rc == G->son[0]->rc ? 0 : rsons);
    nc_put_vars(nc_file, nc_rsons, &start, &count, &stride, &val);
    val = (G->cc == G->son[0]->cc ? 0 : csons);
    nc_put_vars(nc_file, nc_csons, &start, &count, &stride, &val);

    /* Increase submatrix counter */
    (*blockidx)++;

    /* Take care of sons */
    for (j = 0; j < csons; j++)
      for (i = 0; i < rsons; i++)
	write_cdf(G->son[i + j * rsons], blocks, coeffs,
		  blockidx, coeffidx,
		  nc_file,
		  nc_type, nc_rows, nc_cols, nc_rank,
		  nc_rsons, nc_csons, nc_coeff);
  }
}

void
write_cdf_hmatrix(pchmatrix G, const char *name)
{
  size_t    blocks, blockidx;
  size_t    coeffs, coeffidx;
  int       nc_file, nc_type, nc_rows, nc_cols, nc_rank;
  int       nc_rsons, nc_csons, nc_coeff, nc_blocks, nc_coeffs;
  int       result;

  /* Count number of blocks and coefficients */
  blocks = 0;
  coeffs = 0;
  write_count(G, &blocks, &coeffs);

  /* Create NetCDF file */
  result = nc_create(name, NC_64BIT_OFFSET, &nc_file);
  assert(result == NC_NOERR);

  /* Define "blocks" dimension */
  result = nc_def_dim(nc_file, "blocks", blocks, &nc_blocks);
  assert(result == NC_NOERR);

  /* Define "coeffs" dimension */
  result = nc_def_dim(nc_file, "coeffs", coeffs, &nc_coeffs);
  assert(result == NC_NOERR);

  /* Define "type" variable */
  result = nc_def_var(nc_file, "type", NC_INT, 1, &nc_blocks, &nc_type);
  assert(result == NC_NOERR);

  /* Define "rows" variable */
  result = nc_def_var(nc_file, "rows", NC_INT, 1, &nc_blocks, &nc_rows);
  assert(result == NC_NOERR);

  /* Define "cols" variable */
  result = nc_def_var(nc_file, "cols", NC_INT, 1, &nc_blocks, &nc_cols);
  assert(result == NC_NOERR);

  /* Define "rank" variable */
  result = nc_def_var(nc_file, "rank", NC_INT, 1, &nc_blocks, &nc_rank);
  assert(result == NC_NOERR);

  /* Define "rsons" variable */
  result = nc_def_var(nc_file, "rsons", NC_INT, 1, &nc_blocks, &nc_rsons);
  assert(result == NC_NOERR);

  /* Define "csons" variable */
  result = nc_def_var(nc_file, "csons", NC_INT, 1, &nc_blocks, &nc_csons);
  assert(result == NC_NOERR);

  /* Define "coeff" variable */
  result = nc_def_var(nc_file, "coeff", NC_DOUBLE, 1, &nc_coeffs, &nc_coeff);
  assert(result == NC_NOERR);

  /* Finish NetCDF define mode */
  result = nc_enddef(nc_file);
  assert(result == NC_NOERR);

  /* Write matrices to NetCDF variables */
  blockidx = 0;
  coeffidx = 0;
  write_cdf(G, blocks, coeffs, &blockidx, &coeffidx,
	    nc_file, nc_type, nc_rows, nc_cols, nc_rank,
	    nc_rsons, nc_csons, nc_coeff);
  assert(blockidx == blocks);
  assert(coeffidx == coeffs);

  /* Close file */
  result = nc_close(nc_file);
  assert(result == NC_NOERR);
}

static void
prefix_name(char *buf, int bufsize, const char *prefix, const char *name)
{
  if (prefix)
    snprintf(buf, bufsize, "%s_%s", prefix, name);
  else
    strncpy(buf, name, bufsize);
}

void
write_cdfpart_hmatrix(pchmatrix G, int nc_file, const char *prefix)
{
  size_t    blocks, blockidx;
  size_t    coeffs, coeffidx;
  char     *buf;
  int       bufsize;
  int       nc_type, nc_rows, nc_cols, nc_rank;
  int       nc_rsons, nc_csons, nc_coeff, nc_blocks, nc_coeffs;
  int       result;

  /* Prepare buffer for prefixed names */
  bufsize = strlen(prefix) + 16;
  buf = (char *) allocmem(sizeof(char) * bufsize);

  /* Count number of blocks and coefficients */
  blocks = 0;
  coeffs = 0;
  write_count(G, &blocks, &coeffs);

  /* Switch NetCDF file to define mode */
  result = nc_redef(nc_file);
  assert(result == NC_NOERR || result == NC_EINDEFINE);

  /* Define "blocks" dimension */
  prefix_name(buf, bufsize, prefix, "blocks");
  result = nc_def_dim(nc_file, buf, blocks, &nc_blocks);
  assert(result == NC_NOERR);

  /* Define "coeffs" dimension */
  prefix_name(buf, bufsize, prefix, "coeffs");
  result = nc_def_dim(nc_file, buf, coeffs, &nc_coeffs);
  assert(result == NC_NOERR);

  /* Define "type" variable */
  prefix_name(buf, bufsize, prefix, "type");
  result = nc_def_var(nc_file, buf, NC_INT, 1, &nc_blocks, &nc_type);
  assert(result == NC_NOERR);

  /* Define "rows" variable */
  prefix_name(buf, bufsize, prefix, "rows");
  result = nc_def_var(nc_file, buf, NC_INT, 1, &nc_blocks, &nc_rows);
  assert(result == NC_NOERR);

  /* Define "cols" variable */
  prefix_name(buf, bufsize, prefix, "cols");
  result = nc_def_var(nc_file, buf, NC_INT, 1, &nc_blocks, &nc_cols);
  assert(result == NC_NOERR);

  /* Define "rank" variable */
  prefix_name(buf, bufsize, prefix, "rank");
  result = nc_def_var(nc_file, buf, NC_INT, 1, &nc_blocks, &nc_rank);
  assert(result == NC_NOERR);

  /* Define "rsons" variable */
  prefix_name(buf, bufsize, prefix, "rsons");
  result = nc_def_var(nc_file, buf, NC_INT, 1, &nc_blocks, &nc_rsons);
  assert(result == NC_NOERR);

  /* Define "csons" variable */
  prefix_name(buf, bufsize, prefix, "csons");
  result = nc_def_var(nc_file, buf, NC_INT, 1, &nc_blocks, &nc_csons);
  assert(result == NC_NOERR);

  /* Define "coeff" variable */
  prefix_name(buf, bufsize, prefix, "coeff");
  result = nc_def_var(nc_file, buf, NC_DOUBLE, 1, &nc_coeffs, &nc_coeff);
  assert(result == NC_NOERR);

  /* Finish NetCDF define mode */
  result = nc_enddef(nc_file);
  assert(result == NC_NOERR);

  /* Write matrices to NetCDF variables */
  blockidx = 0;
  coeffidx = 0;
  write_cdf(G, blocks, coeffs, &blockidx, &coeffidx,
	    nc_file, nc_type, nc_rows, nc_cols, nc_rank,
	    nc_rsons, nc_csons, nc_coeff);
  assert(blockidx == blocks);
  assert(coeffidx == coeffs);

  /* Clean up */
  nc_sync(nc_file);
  freemem(buf);
}

static phmatrix
read_cdf_part(int nc_file,
	      int nc_type, int nc_rows, int nc_cols, int nc_rank,
	      int nc_rsons, int nc_csons, int nc_coeffs,
	      pcluster *rc, pcluster *cc,
	      uint * ridx, uint * cidx, size_t * blockidx, size_t * coeffidx)
{
  phmatrix  G, G1;
  prkmatrix r;
  pamatrix  f;
  pcluster *rc1, *cc1;
  uint     *ridx1, *cidx1;
  uint      rows, cols;
  uint      rsons, csons;
  uint      lda, ldb, ldf;
  uint      k;
  uint      i, j;
  size_t    start, count;
  ptrdiff_t stride;
  int       val, result;

  /* Get number of rows */
  start = *blockidx;
  count = 1;
  stride = 1;
  result = nc_get_vars(nc_file, nc_rows, &start, &count, &stride, &val);
  assert(result == NC_NOERR);
  rows = val;

  /* Get number of columns */
  result = nc_get_vars(nc_file, nc_cols, &start, &count, &stride, &val);
  assert(result == NC_NOERR);
  cols = val;

  /* If we have not inherited a row cluster, create one */
  if (*rc == 0) {
    /* If we have not inherited a row index, create one */
    if (ridx == 0) {
      ridx = (uint *) allocmem(sizeof(uint) * rows);
      for (i = 0; i < rows; i++)
	ridx[i] = i;
    }

    *rc = new_cluster(rows, ridx, 0, 1);
  }
  else {
    assert((*rc)->size == rows);

    ridx = (*rc)->idx;
  }

  /* If we have not inherited a column cluster, create one */
  if (*cc == 0) {
    /* If we have not inherited a column index, create one */
    if (cidx == 0) {
      cidx = (uint *) allocmem(sizeof(uint) * cols);
      for (j = 0; j < cols; j++)
	cidx[j] = j;
    }

    *cc = new_cluster(cols, cidx, 0, 1);
  }
  else {
    assert((*cc)->size == cols);

    cidx = (*cc)->idx;
  }

  /* Get matrix type */
  result = nc_get_vars(nc_file, nc_type, &start, &count, &stride, &val);
  assert(result == NC_NOERR);

  G = 0;
  switch (val) {
  case 1:
    /* Create amatrix */
    G = new_full_hmatrix(*rc, *cc);
    f = G->f;
    ldf = f->ld;

    /* Get coefficients */
    start = *coeffidx;
    count = rows;
    for (j = 0; j < cols; j++) {
      nc_get_vars(nc_file, nc_coeffs, &start, &count, &stride,
		  f->a + j * ldf);
      start += rows;
    }
    *coeffidx = start;

    /* Switch to next submatrix */
    (*blockidx)++;

    break;

  case 2:
    /* Get rank */
    result = nc_get_vars(nc_file, nc_rank, &start, &count, &stride, &val);
    assert(result == NC_NOERR);
    k = val;

    /* Create matrix */
    G = new_rk_hmatrix(*rc, *cc, k);
    r = G->r;
    lda = r->A.ld;
    ldb = r->B.ld;

    /* Get matrix A */
    start = *coeffidx;
    count = rows;
    for (j = 0; j < k; j++) {
      nc_get_vars(nc_file, nc_coeffs, &start, &count, &stride,
		  r->A.a + j * lda);
      start += rows;
    }

    /* Get matrix B */
    count = cols;
    for (j = 0; j < k; j++) {
      nc_get_vars(nc_file, nc_coeffs, &start, &count, &stride,
		  r->B.a + j * ldb);
      start += cols;
    }
    *coeffidx = start;

    /* Switch to next submatrix */
    (*blockidx)++;

    break;

  case 3:
    /* Get number of row sons */
    result = nc_get_vars(nc_file, nc_rsons, &start, &count, &stride, &val);
    assert(result == NC_NOERR);
    rsons = val;

    /* Get number of column sons */
    result = nc_get_vars(nc_file, nc_csons, &start, &count, &stride, &val);
    assert(result == NC_NOERR);
    csons = val;

    /* If we need the row sons, ensure that we have them */
    if (rsons > 0) {
      /* Update row cluster if it didn't have sons before */
      if ((*rc)->sons == 0)
	setsons_cluster(*rc, rsons);
      else
	assert((*rc)->sons == rsons);

      rc1 = (*rc)->son;
    }
    else {
      rsons = 1;
      rc1 = rc;
    }

    /* If we need the column sons, ensure that we have them */
    if (csons > 0) {
      /* Update column cluster if it didn't have sons before */
      if ((*cc)->sons == 0)
	setsons_cluster(*cc, csons);
      else
	assert((*cc)->sons == csons);

      cc1 = (*cc)->son;
    }
    else {
      csons = 1;
      cc1 = cc;
    }

    /* Create H-matrix */
    G = new_super_hmatrix(*rc, *cc, rsons, csons);

    /* Switch to next submatrix */
    (*blockidx)++;

    cidx1 = cidx;
    for (j = 0; j < csons; j++) {
      ridx1 = ridx;

      for (i = 0; i < rsons; i++) {
	G1 = read_cdf_part(nc_file,
			   nc_type, nc_rows, nc_cols, nc_rank,
			   nc_rsons, nc_csons, nc_coeffs,
			   rc1 + i, cc1 + j, ridx1, cidx1,
			   blockidx, coeffidx);
	ref_hmatrix(G->son + i + j * rsons, G1);

	ridx1 += G1->rc->size;
      }
      assert(ridx1 == ridx + rows);

      cidx1 += G->son[j * rsons]->cc->size;
    }
    assert(cidx1 == cidx + cols);

    break;

  default:
    (void) fprintf(stderr, "Unexpected matrix type %d\n", val);
    abort();
  }

  update_hmatrix(G);

  return G;
}

phmatrix
read_cdf_hmatrix(const char *name, pccluster rc, pccluster cc)
{
  phmatrix  G;
  pcluster  rc0, cc0;
  size_t    blocks, blockidx;
  size_t    coeffs, coeffidx;
  char      dimname[NC_MAX_NAME + 1];
  int       nc_file, nc_type, nc_rows, nc_cols, nc_rank;
  int       nc_rsons, nc_csons, nc_coeff, nc_blocks, nc_coeffs;
  int       result;

  /* Open NetCDF file */
  result = nc_open(name, NC_NOWRITE, &nc_file);
  assert(result == NC_NOERR);

  /* Get "blocks" dimension */
  result = nc_inq_dimid(nc_file, "blocks", &nc_blocks);
  assert(result == NC_NOERR);
  result = nc_inq_dim(nc_file, nc_blocks, dimname, &blocks);
  assert(result == NC_NOERR);

  /* Get "coeffs" dimension */
  result = nc_inq_dimid(nc_file, "coeffs", &nc_coeffs);
  assert(result == NC_NOERR);
  result = nc_inq_dim(nc_file, nc_coeffs, dimname, &coeffs);
  assert(result == NC_NOERR);

  /* Get "type" variable */
  result = nc_inq_varid(nc_file, "type", &nc_type);
  assert(result == NC_NOERR);

  /* Get "rows" variable */
  result = nc_inq_varid(nc_file, "rows", &nc_rows);
  assert(result == NC_NOERR);

  /* Get "cols" variable */
  result = nc_inq_varid(nc_file, "cols", &nc_cols);
  assert(result == NC_NOERR);

  /* Get "rank" variable */
  result = nc_inq_varid(nc_file, "rank", &nc_rank);
  assert(result == NC_NOERR);

  /* Get "rsons" variable */
  result = nc_inq_varid(nc_file, "rsons", &nc_rsons);
  assert(result == NC_NOERR);

  /* Get "csons" variable */
  result = nc_inq_varid(nc_file, "csons", &nc_csons);
  assert(result == NC_NOERR);

  /* Get "coeff" variable */
  result = nc_inq_varid(nc_file, "coeff", &nc_coeff);
  assert(result == NC_NOERR);

  /* Read matrices from NetCDF variables */
  rc0 = (pcluster) rc;
  cc0 = (pcluster) cc;
  blockidx = 0;
  coeffidx = 0;

  G = read_cdf_part(nc_file,
		    nc_type, nc_rows, nc_cols, nc_rank,
		    nc_rsons, nc_csons, nc_coeff,
		    &rc0, &cc0, 0, 0, &blockidx, &coeffidx);
  assert(rc0 == rc);
  assert(cc0 == cc);
  assert(blockidx == blocks);
  assert(coeffidx == coeffs);

  return G;
}

phmatrix
read_cdfpart_hmatrix(int nc_file, const char *prefix,
		     pccluster rc, pccluster cc)
{
  phmatrix  G;
  pcluster  rc0, cc0;
  size_t    blocks, blockidx;
  size_t    coeffs, coeffidx;
  char      dimname[NC_MAX_NAME + 1];
  char     *buf;
  int       bufsize;
  int       nc_type, nc_rows, nc_cols, nc_rank;
  int       nc_rsons, nc_csons, nc_coeff, nc_blocks, nc_coeffs;
  int       result;

  /* Prepare buffer for prefixed names */
  bufsize = strlen(prefix) + 16;
  buf = (char *) allocmem(sizeof(char) * bufsize);

  /* Get "blocks" dimension */
  prefix_name(buf, bufsize, prefix, "blocks");
  result = nc_inq_dimid(nc_file, buf, &nc_blocks);
  assert(result == NC_NOERR);
  result = nc_inq_dim(nc_file, nc_blocks, dimname, &blocks);
  assert(result == NC_NOERR);

  /* Get "coeffs" dimension */
  prefix_name(buf, bufsize, prefix, "coeffs");
  result = nc_inq_dimid(nc_file, buf, &nc_coeffs);
  assert(result == NC_NOERR);
  result = nc_inq_dim(nc_file, nc_coeffs, dimname, &coeffs);
  assert(result == NC_NOERR);

  /* Get "type" variable */
  prefix_name(buf, bufsize, prefix, "type");
  result = nc_inq_varid(nc_file, buf, &nc_type);
  assert(result == NC_NOERR);

  /* Get "rows" variable */
  prefix_name(buf, bufsize, prefix, "rows");
  result = nc_inq_varid(nc_file, buf, &nc_rows);
  assert(result == NC_NOERR);

  /* Get "cols" variable */
  prefix_name(buf, bufsize, prefix, "cols");
  result = nc_inq_varid(nc_file, buf, &nc_cols);
  assert(result == NC_NOERR);

  /* Get "rank" variable */
  prefix_name(buf, bufsize, prefix, "rank");
  result = nc_inq_varid(nc_file, buf, &nc_rank);
  assert(result == NC_NOERR);

  /* Get "rsons" variable */
  prefix_name(buf, bufsize, prefix, "rsons");
  result = nc_inq_varid(nc_file, buf, &nc_rsons);
  assert(result == NC_NOERR);

  /* Get "csons" variable */
  prefix_name(buf, bufsize, prefix, "csons");
  result = nc_inq_varid(nc_file, buf, &nc_csons);
  assert(result == NC_NOERR);

  /* Get "coeff" variable */
  prefix_name(buf, bufsize, prefix, "coeff");
  result = nc_inq_varid(nc_file, buf, &nc_coeff);
  assert(result == NC_NOERR);

  /* Read matrices from NetCDF variables */
  rc0 = (pcluster) rc;
  cc0 = (pcluster) cc;
  blockidx = 0;
  coeffidx = 0;

  G = read_cdf_part(nc_file,
		    nc_type, nc_rows, nc_cols, nc_rank,
		    nc_rsons, nc_csons, nc_coeff,
		    &rc0, &cc0, 0, 0, &blockidx, &coeffidx);
  assert(rc == 0 || rc0 == rc);
  assert(cc == 0 || cc0 == cc);
  assert(blockidx == blocks);
  assert(coeffidx == coeffs);

  /* Clean up */
  freemem(buf);

  return G;
}

void
write_cdfcomplete_hmatrix(pchmatrix G, const char *name)
{
  int       nc_file;
  int       result;

  /* Create NetCDF file */
  result = nc_create(name, NC_64BIT_OFFSET, &nc_file);
  assert(result == NC_NOERR);

  /* Write row cluster tree */
  write_cdfpart_cluster(G->rc, nc_file, "rc");

  if (G->cc != G->rc) {
    /* Write column cluster tree */
    write_cdfpart_cluster(G->cc, nc_file, "cc");
  }

  /* Write hmatrix */
  write_cdfpart_hmatrix(G, nc_file, "ma");

  /* Close NetCDF file */
  nc_close(nc_file);
}

phmatrix
read_cdfcomplete_hmatrix(const char *name)
{
  pcluster  rc, cc;
  phmatrix  G;
  int       nc_file, nc_sons;
  int       result;

  /* Open NetCDF file */
  result = nc_open(name, NC_NOWRITE, &nc_file);
  assert(result == NC_NOERR);

  /* Read row cluster tree */
  rc = read_cdfpart_cluster(nc_file, "rc");

  /* Check whether there is a separate column cluster tree */
  result = nc_inq_varid(nc_file, "cc_sons", &nc_sons);
  if (result == NC_NOERR) {
    /* If there is, read it */
    cc = read_cdfpart_cluster(nc_file, "cc");
  }
  else {
    /* Otherwise assume it's the row cluster tree */
    cc = rc;
  }

  /* Read hmatrix */
  G = read_cdfpart_hmatrix(nc_file, "ma", rc, cc);

  /* Close NetCDF file */
  nc_close(nc_file);

  return G;
}
#endif

static void
write_hlib_part(pchmatrix G, uint roff, uint coff, FILE * out)
{
  pcrkmatrix r;
  pcamatrix f;
  uint      rows, cols;
  uint      rsons, csons;
  uint      roff1, coff1;
  uint      lda, ldb, ldf;
  uint      i, j, k, l;

  if (G->r) {
    r = G->r;
    k = r->k;

    rows = r->A.rows;
    cols = r->B.rows;

    lda = r->A.ld;
    ldb = r->B.ld;

    assert(G->rc->size == rows);
    assert(G->cc->size == cols);
    assert(r->A.cols == k);
    assert(r->B.cols == k);

    fprintf(out, "Type 1\n"
	    "row_offset = %u\n"
	    "col_offset = %u\n"
	    "rows = %u\n"
	    "cols = %u\n"
	    "k = %u\n" "kt = %u\n", roff, coff, rows, cols, k, k);

    for (l = 0; l < k; l++)
      for (i = 0; i < rows; i++)
	fprintf(out, FIELD_CS(.16, e) "\n", FIELD_ARG(r->A.a[i + lda * l]));

    for (l = 0; l < k; l++)
      for (j = 0; j < cols; j++)
	fprintf(out, FIELD_CS(.16, e) "\n", FIELD_ARG(r->B.a[j + ldb * l]));
  }
  else if (G->f) {
    f = G->f;

    rows = f->rows;
    cols = f->cols;

    ldf = f->ld;

    fprintf(out, "Type 2\n"
	    "row_offset = %u\n"
	    "col_offset = %u\n"
	    "rows = %u\n" "cols = %u\n", roff, coff, rows, cols);

    for (j = 0; j < cols; j++)
      for (i = 0; i < rows; i++)
	fprintf(out, FIELD_CS(.16, e) "\n", FIELD_ARG(f->a[i + ldf * j]));
  }
  else {
    assert(G->son != 0);

    rows = G->rc->size;
    cols = G->cc->size;

    rsons = G->rsons;
    csons = G->csons;

    fprintf(out, "Type 3\n"
	    "row_offset = %u\n"
	    "col_offset = %u\n"
	    "rows = %u\n"
	    "cols = %u\n"
	    "block_rows = %u\n"
	    "block_cols = %u\n", roff, coff, rows, cols, rsons, csons);

    coff1 = coff;
    for (j = 0; j < csons; j++) {
      roff1 = roff;
      for (i = 0; i < rsons; i++) {
	write_hlib_part(G->son[i + j * rsons], roff1, coff1, out);

	roff1 += G->son[i]->rc->size;
      }
      assert(roff1 == roff + G->rc->size);

      coff1 += G->son[j * rsons]->cc->size;
    }
    assert(coff1 == coff + G->cc->size);
  }
}

void
write_hlib_hmatrix(pchmatrix G, const char *filename)
{
  FILE     *out;

  out = fopen(filename, "w");
  assert(out != 0);

  fprintf(out, "Beginn der Matrix\n");
  write_hlib_part(G, 0, 0, out);
  fprintf(out, "Ende der Matrix\n");
}

static phmatrix
read_hlib_part(FILE * in, uint roff, uint coff, pcluster *rc,
	       pcluster *cc, uint * ridx, uint * cidx, uint * lineno)
{
  char      buf[80];
  char     *line, *c;
  uint      fields;
  phmatrix  G, G1;
  prkmatrix r;
  pamatrix  f;
  pcluster *rc1, *cc1;
  uint     *ridx1, *cidx1;
  uint      rows, cols;
  uint      rsons, csons;
  uint      roff1, coff1;
  uint      lda, ldb, ldf;
  uint      mtype;
  uint      i, j, k, l;

  G = NULL;
  G1 = NULL;

  line = fgets(buf, 80, in);
  (*lineno)++;

  if (line == 0) {
    fprintf(stderr, "Unexpected end of file\n");

    return 0;
  }

  /* Determine matrix type */
  fields = sscanf(line, "Type %u", &mtype);
  if (fields != 1) {
    for (c = line; *c != '\0' && *c != '\n'; c++);
    if (*c == '\n')
      *c = '\0';

    fprintf(stderr, "Expected \"Type ...\", got \"%s\"\n", line);

    return 0;
  }

  /* Read offsets and compare to what we expect */
  line = fgets(buf, 80, in);
  (*lineno)++;
  fields = sscanf(line, "row_offset = %u", &roff1);
  assert(fields == 1);

  line = fgets(buf, 80, in);
  (*lineno)++;
  fields = sscanf(line, "col_offset = %u", &coff1);
  assert(fields == 1);

  assert(roff == roff1);
  assert(coff == coff1);

  /* Read number of rows and columns */
  line = fgets(buf, 80, in);
  (*lineno)++;
  fields = sscanf(line, "rows = %u", &rows);
  assert(fields == 1);

  line = fgets(buf, 80, in);
  (*lineno)++;
  fields = sscanf(line, "cols = %u", &cols);
  assert(fields == 1);

  /* If we have not inherited a row index, create one */
  if (ridx == 0) {
    ridx = (uint *) allocmem(sizeof(uint) * rows);
    for (i = 0; i < rows; i++)
      ridx[i] = i;
  }

  /* If we have not inherited a column index, create one */
  if (cidx == 0) {
    cidx = (uint *) allocmem(sizeof(uint) * cols);
    for (j = 0; j < cols; j++)
      cidx[j] = j;
  }

  /* If we have not inherited a row cluster, create one */
  if (*rc == 0)
    *rc = new_cluster(rows, ridx, 0, 1);

  /* If we have not inherited a column cluster, create one */
  if (*cc == 0)
    *cc = new_cluster(cols, cidx, 0, 1);

  switch (mtype) {
  case 1:			/* rkmatrix */
    /* Read rank */
    line = fgets(buf, 80, in);
    (*lineno)++;
    fields = sscanf(line, "k = %u", &k);
    assert(fields == 1);

    line = fgets(buf, 80, in);
    (*lineno)++;
    fields = sscanf(line, "kt = %u", &k);
    assert(fields == 1);

    /* Create matrix */
    G = new_rk_hmatrix(*rc, *cc, k);
    r = G->r;
    lda = r->A.ld;
    ldb = r->B.ld;

    /* Read A */
    for (l = 0; l < k; l++)
      for (i = 0; i < rows; i++) {
	line = fgets(buf, 80, in);
	(*lineno)++;
	fields = sscanf(line, FIELD_SCANF_CS(, e),
			FIELD_ADDR(r->A.a + i + l * lda));
	assert(fields == 1);
      }

    /* Read B */
    for (l = 0; l < k; l++)
      for (j = 0; j < cols; j++) {
	line = fgets(buf, 80, in);
	(*lineno)++;
	fields = sscanf(line, FIELD_SCANF_CS(, e),
			FIELD_ADDR(r->B.a + j + l * ldb));
	assert(fields == 1);
      }
    break;

  case 2:			/* amatrix */
    /* Create matrix */
    G = new_full_hmatrix(*rc, *cc);
    f = G->f;
    ldf = f->ld;

    /* Read coefficients */
    for (j = 0; j < cols; j++)
      for (i = 0; i < rows; i++) {
	line = fgets(buf, 80, in);
	(*lineno)++;
	fields = sscanf(line, FIELD_SCANF_CS(, e),
			FIELD_ADDR(f->a + i + j * ldf));
	assert(fields == 1);
      }
    break;

  case 3:			/* subdivided matrix */
    /* Read number of block rows and columns */
    line = fgets(buf, 80, in);
    (*lineno)++;
    fields = sscanf(line, "block_rows = %u", &rsons);
    assert(fields == 1);

    line = fgets(buf, 80, in);
    (*lineno)++;
    fields = sscanf(line, "block_cols = %u", &csons);
    assert(fields == 1);

    /* Update row cluster if it didn't have sons before */
    if ((*rc)->sons == 0)
      setsons_cluster(*rc, rsons);
    else
      assert((*rc)->sons == rsons);

    /* Update column cluster if it didn't have sons before */
    if ((*cc)->sons == 0)
      setsons_cluster(*cc, csons);
    else
      assert((*cc)->sons == csons);

    G = new_super_hmatrix(*rc, *cc, rsons, csons);

    rc1 = (*rc)->son;
    cc1 = (*cc)->son;

    coff1 = coff;
    cidx1 = cidx;
    for (j = 0; j < csons; j++) {
      roff1 = roff;
      ridx1 = ridx;

      for (i = 0; i < rsons; i++) {
	G1 = read_hlib_part(in, roff1, coff1, rc1 + i, cc1 + j, ridx1, cidx1,
			    lineno);
	ref_hmatrix(G->son + i + j * rsons, G1);

	roff1 += G1->rc->size;
	ridx1 += G1->rc->size;
      }
      assert(roff1 == roff + rows);

      coff1 += G1->cc->size;
      cidx1 += G1->cc->size;
    }
    assert(coff1 == coff + cols);
    break;

  default:
    ;
  }

  update_hmatrix(G);

  return G;
}

phmatrix
read_hlib_hmatrix(const char *filename)
{
  phmatrix  G;
  FILE     *in;
  char      buf[80], *res;
  pcluster  rc, cc;
  uint      lineno;

  in = fopen(filename, "r");
  assert(in != 0);

  res = fgets(buf, 80, in);
  assert(res != NULL);
  lineno = 1;

  rc = 0;
  cc = 0;
  G = read_hlib_part(in, 0, 0, &rc, &cc, 0, 0, &lineno);

  res = fgets(buf, 80, in);
  assert(res != NULL);
  lineno++;

  fclose(in);

  return G;
}

static phmatrix
read_hlibsymm_part(FILE * in, uint roff, pcluster *rc,
		   uint * ridx, uint * lineno)
{
  char      buf[80];
  char     *line, *c;
  uint      fields;
  phmatrix  G, G1;
  prkmatrix r;
  pamatrix  f;
  pcluster *rc1;
  uint     *ridx1, *cidx1;
  uint      rows, cols;
  uint      rsons, csons;
  uint      roff1, coff1;
  uint      lda, ldb, ldf;
  uint      mtype;
  uint      i, j, k, l;

  line = fgets(buf, 80, in);
  (*lineno)++;

  if (line == 0) {
    fprintf(stderr, "Unexpected end of file\n");

    return 0;
  }

  /* Determine matrix type */
  fields = sscanf(line, "Type %u", &mtype);
  if (fields != 1) {
    for (c = line; *c != '\0' && *c != '\n'; c++);
    if (*c == '\n')
      *c = '\0';

    fprintf(stderr, "Expected \"Type ...\", got \"%s\"\n", line);

    return 0;
  }

  /* Read offsets and compare to what we expect */
  line = fgets(buf, 80, in);
  (*lineno)++;
  fields = sscanf(line, "row_offset = %u", &roff1);
  assert(fields == 1);

  line = fgets(buf, 80, in);
  (*lineno)++;
  fields = sscanf(line, "col_offset = %u", &coff1);
  assert(fields == 1);

  assert(roff == roff1);
  assert(roff == coff1);

  /* Read number of rows and columns */
  line = fgets(buf, 80, in);
  (*lineno)++;
  fields = sscanf(line, "rows = %u", &rows);
  assert(fields == 1);

  line = fgets(buf, 80, in);
  (*lineno)++;
  fields = sscanf(line, "cols = %u", &cols);
  assert(fields == 1);

  assert(rows == cols);

  /* If we have not inherited a row index, create one */
  if (ridx == 0) {
    ridx = (uint *) allocmem(sizeof(uint) * rows);
    for (i = 0; i < rows; i++)
      ridx[i] = i;
  }

  /* If we have not inherited a row cluster, create one */
  if (*rc == 0)
    *rc = new_cluster(rows, ridx, 0, 1);

  G = 0;
  switch (mtype) {
  case 1:			/* rkmatrix */
    /* Read rank */
    line = fgets(buf, 80, in);
    (*lineno)++;
    fields = sscanf(line, "k = %u", &k);
    assert(fields == 1);

    line = fgets(buf, 80, in);
    (*lineno)++;
    fields = sscanf(line, "kt = %u", &k);
    assert(fields == 1);

    /* Create matrix */
    G = new_rk_hmatrix(*rc, *rc, k);
    r = G->r;
    lda = r->A.ld;
    ldb = r->B.ld;

    /* Read A */
    for (l = 0; l < k; l++)
      for (i = 0; i < rows; i++) {
	line = fgets(buf, 80, in);
	(*lineno)++;
	fields = sscanf(line, FIELD_SCANF_CS(, e),
			FIELD_ADDR(r->A.a + i + l * lda));
	assert(fields == 1);
      }

    /* Read B */
    for (l = 0; l < k; l++)
      for (j = 0; j < cols; j++) {
	line = fgets(buf, 80, in);
	(*lineno)++;
	fields = sscanf(line, FIELD_SCANF_CS(, e),
			FIELD_ADDR(r->B.a + j + l * ldb));
	assert(fields == 1);
      }
    break;

  case 2:			/* amatrix */
    /* Create matrix */
    G = new_full_hmatrix(*rc, *rc);
    f = G->f;
    ldf = f->ld;

    /* Read coefficients */
    for (j = 0; j < cols; j++)
      for (i = 0; i < rows; i++) {
	line = fgets(buf, 80, in);
	(*lineno)++;
	fields = sscanf(line, FIELD_SCANF_CS(, e),
			FIELD_ADDR(f->a + i + j * ldf));
	assert(fields == 1);
      }
    break;

  case 3:			/* subdivided matrix */
    /* Read number of block rows and columns */
    line = fgets(buf, 80, in);
    (*lineno)++;
    fields = sscanf(line, "block_rows = %u", &rsons);
    assert(fields == 1);

    line = fgets(buf, 80, in);
    (*lineno)++;
    fields = sscanf(line, "block_cols = %u", &csons);
    assert(fields == 1);
    assert(rsons == csons);

    /* Update row cluster if it didn't have sons before */
    if ((*rc)->sons == 0)
      setsons_cluster(*rc, rsons);
    else
      assert((*rc)->sons == rsons);

    G = new_super_hmatrix(*rc, *rc, rsons, csons);

    rc1 = (*rc)->son;

    coff1 = roff;
    cidx1 = ridx;
    for (j = 0; j < csons; j++) {
      roff1 = roff;
      ridx1 = ridx;

      for (i = 0; i < rsons; i++) {
	G1 = (i == j ?
	      read_hlibsymm_part(in, roff1, rc1 + i, ridx1, lineno) :
	      read_hlib_part(in, roff1, coff1, rc1 + i, rc1 + j, ridx1, ridx1,
			     lineno));
	ref_hmatrix(G->son + i + j * rsons, G1);

	roff1 += G1->rc->size;
	ridx1 += G1->rc->size;
      }
      assert(roff1 == roff + rows);

      coff1 += G->son[j * rsons]->cc->size;
      cidx1 += G->son[j * rsons]->cc->size;
    }
    assert(coff1 == roff + cols);
    break;

  default:
    ;
  }

  return G;
}

phmatrix
read_hlibsymm_hmatrix(const char *filename)
{
  phmatrix  G;
  FILE     *in;
  char      buf[80], *line;
  pcluster  rc;
  uint      lineno;

  in = fopen(filename, "r");
  assert(in != 0);

  line = fgets(buf, 80, in);
  assert(line != 0);
  lineno = 1;

  rc = 0;
  G = read_hlibsymm_part(in, 0, &rc, 0, &lineno);

  line = fgets(buf, 80, in);
  assert(line != 0);
  lineno++;

  fclose(in);

  return G;
}

/* ------------------------------------------------------------
 * Drawing
 * ------------------------------------------------------------ */

#ifdef USE_CAIRO
static void
cairodraw(cairo_t * cr, pchmatrix hm, bool storage, uint levels)
{
  cairo_text_extents_t extents;
  char      buf[5];
  uint      rsons, csons;
  uint      rsize, csize;
  uint      roff, coff;
  uint      i, j;

  if (hm->son && levels != 1) {
    rsons = hm->rsons;
    csons = hm->csons;

    coff = 0;
    for (j = 0; j < csons; j++) {
      roff = 0;
      for (i = 0; i < rsons; i++) {
	cairo_save(cr);
	cairo_translate(cr, coff, roff);
	cairodraw(cr, hm->son[i + j * rsons], storage, levels - 1);
	cairo_restore(cr);

	roff += hm->son[i + j * rsons]->rc->size;
      }
      assert(roff == hm->rc->size);

      coff += hm->son[j * rsons]->cc->size;
    }
    assert(coff == hm->cc->size);
  }
  else {
    rsize = hm->rc->size;
    csize = hm->cc->size;

    if (hm->son) {
      cairo_rectangle(cr, 0.0, 0.0, csize, rsize);
      cairo_save(cr);
      cairo_set_source_rgb(cr, 0.9, 0.9, 1.0);
      cairo_fill_preserve(cr);
      cairo_restore(cr);
      cairo_stroke(cr);
    }
    else if (hm->r) {
      if (storage) {
	cairo_save(cr);
	cairo_set_source_rgb(cr, 0.2, 1.0, 0.2);
	cairo_rectangle(cr, 0.0, 0.0, csize, UINT_MIN(rsize, hm->r->k));
	cairo_rectangle(cr, 0.0, 0.0, UINT_MIN(csize, hm->r->k), rsize);
	cairo_fill(cr);
	cairo_restore(cr);

	cairo_rectangle(cr, 0.0, 0.0, csize, rsize);
	cairo_stroke(cr);

	if (hm->r->k > 0) {
	  sprintf(buf, "%d", hm->r->k);
	  cairo_set_font_size(cr, UINT_MIN(rsize, csize) / 2.5);
	  cairo_text_extents(cr, buf, &extents);

	  cairo_save(cr);
	  cairo_translate(cr, (csize - extents.width) / 2.0,
			  (rsize + extents.height) / 2.0);
	  cairo_show_text(cr, buf);
	  cairo_restore(cr);
	}

      }
      else {
	cairo_rectangle(cr, 0.0, 0.0, csize, rsize);
	if (hm->r->k > 0) {
	  cairo_save(cr);
	  cairo_set_source_rgb(cr, 0.2, 1.0, 0.2);
	  cairo_fill_preserve(cr);
	  cairo_restore(cr);
	}
	if (hm->r->k == 0) {
	  cairo_save(cr);
	  cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
	  cairo_fill_preserve(cr);
	  cairo_restore(cr);
	}

	cairo_stroke(cr);

	if (hm->r->k > 0) {
	  sprintf(buf, "%d", hm->r->k);
	  cairo_set_font_size(cr, UINT_MIN(rsize, csize) / 2.5);
	  cairo_text_extents(cr, buf, &extents);

	  cairo_save(cr);
	  cairo_translate(cr, (csize - extents.width) / 2.0,
			  (rsize + extents.height) / 2.0);
	  cairo_show_text(cr, buf);
	  cairo_restore(cr);
	}
      }
    }
    else if (hm->f) {
      cairo_rectangle(cr, 0.0, 0.0, csize, rsize);
      cairo_save(cr);
      cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
      cairo_fill_preserve(cr);
      cairo_restore(cr);
      cairo_stroke(cr);
    }
    else {
      cairo_rectangle(cr, 0.0, 0.0, csize, rsize);
      cairo_stroke(cr);
    }
  }
}

void
draw_cairo_hmatrix(cairo_t * cr, pchmatrix hm, bool storage, uint levels)
{
  double    sx, sy, ex, ey;
  uint      rsize, csize;
  double    scalex, scaley, scale;

  /* Save Cairo state */
  cairo_save(cr);

  /* Obtain size of block */
  rsize = hm->rc->size;
  csize = hm->cc->size;

  /* Obtain size of current Cairo bounding box */
  cairo_clip_extents(cr, &sx, &sy, &ex, &ey);

  /* Compute scaling factor */
  scalex = (ex - sx) / rsize;
  scaley = (ey - sy) / csize;
  scale = (scalex < scaley ? scalex : scaley);

  /* Center block in bounding box */
  cairo_translate(cr, 0.5 * (ex - sx - scale * rsize),
		  0.5 * (ey - sy - scale * csize));

  /* Scale coordinates */
  cairo_scale(cr, scale, scale);
  cairo_set_line_width(cr, cairo_get_line_width(cr) / scale);

  /* Start drawing */
  cairodraw(cr, hm, storage, levels);

  /* Restore Cairo state */
  cairo_restore(cr);
}
#endif

void
copy_sparsematrix_hmatrix(psparsematrix sp, phmatrix hm)
{

  uint      i, j, k, l, m, rsize, csize;
  uint     *ridx, *cidx, *row, *col;
  pfield    coeff;
  pamatrix  f;

  if (hm->son) {
    for (j = 0; j < hm->csons; j++) {
      for (i = 0; i < hm->rsons; i++) {
	copy_sparsematrix_hmatrix(sp, hm->son[i + j * (hm->rsons)]);
      }
    }
  }
  else if (hm->r) {		/*hm->r is defined, but hm->r->A = hm->r->B = NulL => all entries are 0 */

  }
  else if (hm->f) {
    rsize = hm->rc->size;
    csize = hm->cc->size;
    ridx = hm->rc->idx;
    cidx = hm->cc->idx;
    row = sp->row;
    col = sp->col;
    coeff = sp->coeff;
    f = hm->f;

    for (j = 0; j < f->cols; j++)
      for (i = 0; i < f->rows; i++)
	f->a[i + j * f->rows] = 0.0;

    for (k = 0; k < rsize; k++) {
      i = ridx[k];
      for (m = row[i]; m < row[i + 1]; m++) {
	for (l = 0; l < csize; l++) {

	  if (col[m] == cidx[l]) {

	    setentry_amatrix(hm->f, k, l, coeff[m]);
	  }
	}
      }
    }

  }
}
back to top