From 8ec22431a23af2496b4bc2837b5843e557098e41 Mon Sep 17 00:00:00 2001
From: Matti Pellika <matti.pellikka@tut.fi>
Date: Mon, 30 Mar 2009 14:10:58 +0000
Subject: [PATCH] Actually added the files mentioned in previous commit just
 now.

Also added an example file celldriver.cpp and transformer.geo
in /utils/misc
---
 Geo/CellComplex.cpp                   | 520 +++++++++++++++++
 Geo/CellComplex.h                     | 419 ++++++++++++++
 contrib/kbipack/Makefile              |  77 +++
 contrib/kbipack/Makefile.bak          |  77 +++
 contrib/kbipack/Makefile~             |  75 +++
 contrib/kbipack/compute_normal_form.c | 355 ++++++++++++
 contrib/kbipack/gmp_blas.c            | 227 ++++++++
 contrib/kbipack/gmp_blas.h            | 103 ++++
 contrib/kbipack/gmp_blas.o            | Bin 0 -> 10724 bytes
 contrib/kbipack/gmp_matrix.c          | 673 ++++++++++++++++++++++
 contrib/kbipack/gmp_matrix.c~         | 678 ++++++++++++++++++++++
 contrib/kbipack/gmp_matrix.h          | 142 +++++
 contrib/kbipack/gmp_matrix.h~         | 144 +++++
 contrib/kbipack/gmp_matrix.o          | Bin 0 -> 20660 bytes
 contrib/kbipack/gmp_matrix_io.c       | 135 +++++
 contrib/kbipack/gmp_matrix_io.h       |  37 ++
 contrib/kbipack/gmp_matrix_io.o       | Bin 0 -> 8060 bytes
 contrib/kbipack/gmp_normal_form.c     | 783 +++++++++++++++++++++++++
 contrib/kbipack/gmp_normal_form.c~    | 784 ++++++++++++++++++++++++++
 contrib/kbipack/gmp_normal_form.h     | 154 +++++
 contrib/kbipack/gmp_normal_form.o     | Bin 0 -> 16308 bytes
 contrib/kbipack/kbihnf.m              |  58 ++
 contrib/kbipack/kbisnf.m              |  58 ++
 contrib/kbipack/licence               | 340 +++++++++++
 contrib/kbipack/readme                | 106 ++++
 utils/misc/celldriver.cpp             |  63 +++
 utils/misc/transformer.geo            |  99 ++++
 27 files changed, 6107 insertions(+)
 create mode 100644 Geo/CellComplex.cpp
 create mode 100644 Geo/CellComplex.h
 create mode 100755 contrib/kbipack/Makefile
 create mode 100755 contrib/kbipack/Makefile.bak
 create mode 100755 contrib/kbipack/Makefile~
 create mode 100755 contrib/kbipack/compute_normal_form.c
 create mode 100755 contrib/kbipack/gmp_blas.c
 create mode 100755 contrib/kbipack/gmp_blas.h
 create mode 100755 contrib/kbipack/gmp_blas.o
 create mode 100755 contrib/kbipack/gmp_matrix.c
 create mode 100755 contrib/kbipack/gmp_matrix.c~
 create mode 100755 contrib/kbipack/gmp_matrix.h
 create mode 100755 contrib/kbipack/gmp_matrix.h~
 create mode 100755 contrib/kbipack/gmp_matrix.o
 create mode 100755 contrib/kbipack/gmp_matrix_io.c
 create mode 100755 contrib/kbipack/gmp_matrix_io.h
 create mode 100755 contrib/kbipack/gmp_matrix_io.o
 create mode 100755 contrib/kbipack/gmp_normal_form.c
 create mode 100755 contrib/kbipack/gmp_normal_form.c~
 create mode 100755 contrib/kbipack/gmp_normal_form.h
 create mode 100755 contrib/kbipack/gmp_normal_form.o
 create mode 100755 contrib/kbipack/kbihnf.m
 create mode 100755 contrib/kbipack/kbisnf.m
 create mode 100755 contrib/kbipack/licence
 create mode 100755 contrib/kbipack/readme
 create mode 100755 utils/misc/celldriver.cpp
 create mode 100755 utils/misc/transformer.geo

diff --git a/Geo/CellComplex.cpp b/Geo/CellComplex.cpp
new file mode 100644
index 0000000000..7b117e1cef
--- /dev/null
+++ b/Geo/CellComplex.cpp
@@ -0,0 +1,520 @@
+// Gmsh - Copyright (C) 1997-2009 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+//
+// Contributed by Matti Pellikka, 16.3.2009.
+
+#include "CellComplex.h"
+
+CellComplex::CellComplex( std::vector<GEntity*> domain, std::vector<GEntity*> subdomain ){
+  
+  _domain = domain;
+  _subdomain = subdomain;
+  
+  // subdomain need to be inserted first!
+  insertCells(true);
+  insertCells(false);
+  
+  
+}
+void CellComplex::insertCells(bool subdomain){  
+  
+  std::vector<GEntity*> domain;
+  
+  if(subdomain) domain = _subdomain;
+  else domain = _domain;
+  
+  std::vector<int> vertices;
+  int vertex = 0;
+  
+  for(unsigned int j=0; j < domain.size(); j++) {  
+    for(unsigned int i=0; i < domain.at(j)->getNumMeshElements(); i++){ 
+      vertices.clear();
+      for(int k=0; k < domain.at(j)->getMeshElement(i)->getNumVertices(); k++){              
+        MVertex* vertex = domain.at(j)->getMeshElement(i)->getVertex(k);
+        vertices.push_back(vertex->getNum()); 
+        //_cells[0].insert(new ZeroSimplex(vertex->getNum(), subdomain, vertex->x(), vertex->y(), vertex->z() )); // Add vertices
+        _cells[0].insert(new ZeroSimplex(vertex->getNum(), subdomain));
+      } 
+      if(domain.at(j)->getMeshElement(i)->getDim() == 3){
+        _cells[3].insert(new ThreeSimplex(vertices, subdomain)); // Add volumes
+      }
+      
+      for(int k=0; k < domain.at(j)->getMeshElement(i)->getNumFaces(); k++){
+        vertices.clear();
+        for(int l=0; l < domain.at(j)->getMeshElement(i)->getFace(k).getNumVertices(); l++){
+          vertex = domain.at(j)->getMeshElement(i)->getFace(k).getVertex(l)->getNum();
+          vertices.push_back(vertex); 
+        } 
+        _cells[2].insert(new TwoSimplex(vertices, subdomain)); // Add faces
+      }
+      
+      for(int k=0; k < domain.at(j)->getMeshElement(i)->getNumEdges(); k++){
+        vertices.clear();
+        for(int l=0; l < domain.at(j)->getMeshElement(i)->getEdge(k).getNumVertices(); l++){
+          vertex = domain.at(j)->getMeshElement(i)->getEdge(k).getVertex(l)->getNum();
+          vertices.push_back(vertex); 
+        }
+        _cells[1].insert(new OneSimplex(vertices, subdomain)); // Add edges
+      }
+              
+    }               
+  }
+  
+}
+
+int Simplex::kappa(Cell* tau) const{
+  for(int i=0; i < tau->getNumVertices(); i++){
+    if( !(this->hasVertex(tau->getVertex(i))) ) return 0;
+  }
+  
+  if(this->getDim() - tau->getDim() != 1) return 0;
+  
+  int value=1;
+  for(int i=0; i < tau->getNumVertices(); i++){
+    if(this->getVertex(i) != tau->getVertex(i)) return value;
+    value = value*-1;
+  }
+  
+  return 1;  
+}
+std::set<Cell*, Less_Cell>::iterator CellComplex::findCell(int dim, std::vector<int>& vertices){
+  if(dim == 0) return _cells[dim].find(new ZeroSimplex(vertices.at(0)));
+  if(dim == 1) return _cells[dim].find(new OneSimplex(vertices));
+  if(dim == 2) return _cells[dim].find(new TwoSimplex(vertices));
+  return _cells[3].find(new ThreeSimplex(vertices));
+}
+
+std::set<Cell*, Less_Cell>::iterator CellComplex::findCell(int dim, int vertex, int dummy){
+  if(dim == 0) return _cells[dim].lower_bound(new ZeroSimplex(vertex));
+  if(dim == 1) return _cells[dim].lower_bound(new OneSimplex(vertex, dummy));
+  if(dim == 2) return _cells[dim].lower_bound(new TwoSimplex(vertex, dummy));
+  return _cells[3].lower_bound(new ThreeSimplex(vertex, dummy));
+}
+
+
+std::vector<Cell*> CellComplex::bd(Cell* sigma){  
+  std::vector<Cell*> boundary;
+  int dim = sigma->getDim();
+  if(dim < 1) return boundary;
+  
+  
+  citer start = findCell(dim-1, sigma->getVertex(0), 0);
+  if(start == lastCell(dim-1)) return boundary;
+  
+  citer end = findCell(dim-1, sigma->getVertex(sigma->getNumVertices()-1), sigma->getVertex(sigma->getNumVertices()-1));
+  if(end != lastCell(dim-1)) end++;
+
+  //for(citer cit = firstCell(dim-1); cit != lastCell(dim-1); cit++){
+  for(citer cit = start; cit != end; cit++){
+    if(kappa(sigma, *cit) != 0){
+      boundary.push_back(*cit);
+      if(boundary.size() == sigma->getBdMaxSize()){
+        return boundary;
+      }
+    }
+  }
+  return boundary;    
+}
+std::vector< std::set<Cell*, Less_Cell>::iterator > CellComplex::bdIt(Cell* sigma){
+  
+  int dim = sigma->getDim();  
+  std::vector< std::set<Cell*, Less_Cell>::iterator >  boundary;
+  if(dim < 1){
+    return boundary;
+  }
+  
+  citer start = findCell(dim-1, sigma->getVertex(0), 0);
+  if(start == lastCell(dim-1)) return boundary;
+  
+  citer end = findCell(dim-1, sigma->getVertex(sigma->getNumVertices()-1), sigma->getVertex(sigma->getNumVertices()-1));
+  if(end != lastCell(dim-1)) end++;
+    
+  for(citer cit = start; cit != end; cit++){
+    if(kappa(sigma, *cit) != 0){
+      boundary.push_back(cit);
+      if(boundary.size() == sigma->getBdMaxSize()){
+        return boundary;
+      }
+    }
+  }
+  
+  return boundary;
+}
+
+
+
+std::vector<Cell*> CellComplex::cbd(Cell* tau){  
+  std::vector<Cell*> coboundary;
+  int dim = tau->getDim();
+  if(dim > 2){
+    return coboundary;
+  }
+  
+  for(citer cit = firstCell(dim+1); cit != lastCell(dim+1); cit++){
+    if(kappa(*cit, tau) != 0){
+      coboundary.push_back(*cit);
+      if(coboundary.size() == tau->getCbdMaxSize()){
+        return coboundary;
+      }
+    }
+  }
+  return coboundary;    
+}
+std::vector< std::set<Cell*, Less_Cell>::iterator > CellComplex::cbdIt(Cell* tau){
+  
+  std::vector< std::set<Cell*, Less_Cell>::iterator >  coboundary;
+  int dim = tau->getDim();
+  if(dim > 2){
+    return coboundary;
+  }
+  
+  for(citer cit = firstCell(dim+1); cit != lastCell(dim+1); cit++){
+    if(kappa(*cit, tau) != 0){
+      coboundary.push_back(cit);
+      if(coboundary.size() == tau->getCbdMaxSize()){
+        return coboundary;
+      }
+    }
+  }
+  
+  return coboundary;
+}
+
+
+void CellComplex::removeCell(Cell* cell){
+    _cells[cell->getDim()].erase(cell);
+  delete cell;
+}
+void CellComplex::removeCellIt(std::set<Cell*, Less_Cell>::iterator cell){
+  Cell* c = *cell;
+  int dim = c->getDim();
+  _cells[dim].erase(cell);
+  delete c;
+}
+
+void CellComplex::removeCellQset(Cell*& cell, std::set<Cell*, Less_Cell>& Qset){
+   Qset.erase(cell);
+  delete cell;
+}
+
+void CellComplex::enqueueCellsIt(std::vector< std::set<Cell*, Less_Cell>::iterator >& cells, 
+                               std::queue<Cell*>& Q, std::set<Cell*, Less_Cell>& Qset){
+  for(unsigned int i=0; i < cells.size(); i++){
+    Cell* cell = *cells.at(i);
+    citer cit = Qset.find(cell);
+    if(cit == Qset.end()){
+      Qset.insert(cell);
+      Q.push(cell);
+    } 
+  }
+}
+void CellComplex::enqueueCells(std::vector<Cell*>& cells, std::queue<Cell*>& Q, std::set<Cell*, Less_Cell>& Qset){
+  for(unsigned int i=0; i < cells.size(); i++){
+    citer cit = Qset.find(cells.at(i));
+    if(cit == Qset.end()){
+      Qset.insert(cells.at(i));
+      Q.push(cells.at(i));
+    }
+  }
+}
+
+
+int CellComplex::coreductionMrozek(Cell* generator){
+  
+  int coreductions = 0;
+  
+  std::queue<Cell*> Q;
+  std::set<Cell*, Less_Cell> Qset;
+  
+  Q.push(generator);
+  Qset.insert(generator);
+  //removeCell(generator);
+  
+  std::vector<Cell*> bd_s;
+  //std::vector< std::set<Cell*, Less_Cell>::iterator > bd_s;
+  std::vector<Cell*> cbd_c;
+  //std::vector< std::set<Cell*, Less_Cell>::iterator > cbd_c;
+  Cell* s;
+  int round = 0;
+  while( !Q.empty() ){
+    round++;
+    //printf("%d ", round);
+    s = Q.front();
+    Q.pop();
+    removeCellQset(s, Qset);
+    
+    //bd_s = bdIt(s);
+    bd_s = bd(s);
+    if( bd_s.size() == 1 && inSameDomain(s, bd_s.at(0)) ){
+      removeCell(s);
+      //cbd_c = cbdIt(*bd_s.at(0));
+      cbd_c = cbd(bd_s.at(0));
+      //enqueueCellsIt(cbd_c, Q, Qset);
+      enqueueCells(cbd_c, Q, Qset);
+      //removeCellIt(bd_s.at(0));
+      removeCell(bd_s.at(0));
+      coreductions++;
+    }
+    else if(bd_s.empty()){
+      //cbd_c = cbdIt(s);
+      //enqueueCellsIt(cbd_c, Q, Qset);
+      cbd_c = cbd(s);
+      enqueueCells(cbd_c, Q, Qset);
+    }
+    
+  
+  }
+  printf("Coreduction: %d loops with %d coreductions\n", round, coreductions);
+  return coreductions;
+}
+
+
+int CellComplex::coreduction(int dim){
+  
+  if(dim < 0 || dim > 2) return 0;
+  
+  std::vector<Cell*> bd_c;
+  int count = 0;
+  
+  bool coreduced = true;
+  while (coreduced){
+    coreduced = false;
+    for(citer cit = firstCell(dim+1); cit != lastCell(dim+1); cit++){
+      Cell* cell = *cit;
+      
+      bd_c = bd(cell);
+      if(bd_c.size() == 1 && inSameDomain(cell, bd_c.at(0)) ){
+        removeCell(cell);
+        //cit = firstCell(dim-1);
+        removeCell(bd_c.at(0));
+        count++;
+        coreduced =true;
+      }
+      
+    }
+  }
+  
+  return count;
+    
+}
+
+
+int CellComplex::reduction(int dim){
+  if(dim < 1 || dim > 3) return 0;
+  
+  std::vector<Cell*> cbd_c;
+  int count = 0;
+  
+  bool reduced = true;
+  while (reduced){
+    reduced = false;
+    for(citer cit = firstCell(dim-1); cit != lastCell(dim-1); cit++){
+      Cell* cell = *cit;
+      //if(reductionMrozek(cell) !=0) cit = firstCell(dim-1);   
+      cbd_c = cbd(cell);
+      if(cbd_c.size() == 1 && inSameDomain(cell, cbd_c.at(0)) ){
+        removeCell(cell);
+        //cit = firstCell(dim-1);
+        removeCell(cbd_c.at(0));
+        count++;
+        reduced =true;
+      }
+      
+    }
+  }
+  return count;  
+}
+
+void CellComplex::reduceComplex(){
+  printf("Cell complex before reduction: %d volumes, %d faces, %d edges and %d vertices.\n",
+         getSize(3), getSize(2), getSize(1), getSize(0));
+  reduction(3);
+  reduction(2);
+  reduction(1);
+  printf("Cell complex after reduction: %d volumes, %d faces, %d edges and %d vertices.\n",
+         getSize(3), getSize(2), getSize(1), getSize(0));
+}
+void CellComplex::coreduceComplex(){
+  printf("Cell complex before coreduction: %d volumes, %d faces, %d edges and %d vertices.\n",
+         getSize(3), getSize(2), getSize(1), getSize(0));
+  while (getSize(0) != 0){
+    citer cit = firstCell(0);
+    Cell* cell = *cit;
+    removeCell(cell);
+    //complex.coreductionMrozek(kaka);
+    coreduction(0);
+    coreduction(1);
+    coreduction(2);
+    
+   }
+  
+  printf("Cell complex after coreduction: %d volumes, %d faces, %d edges and %d vertices.\n",
+         getSize(3), getSize(2), getSize(1), getSize(0));
+}
+
+void CellComplex::constructHMatrix(int dim){
+  
+  // h: C_dim -> C_(dim-1)
+  
+  if(dim > 3 || dim < 1){
+    return;
+  }
+  
+  if( _cells[dim].size() == 0 ){
+    _HMatrix[dim] = create_gmp_matrix_zero(1, 1);
+    return;
+  }
+  unsigned int cols = _cells[dim].size(); 
+  
+  if( _cells[dim-1].size() == 0){ 
+    _HMatrix[dim] = create_gmp_matrix_zero(1, cols);
+    return;
+  }
+  unsigned int rows = _cells[dim-1].size();
+  
+  mpz_t elems[rows*cols];
+  mpz_array_init( elems[0], rows*cols, 512 );  
+  
+  citer high = firstCell(dim);
+  citer low = firstCell(dim-1);
+  
+  //printf("laa %d %d %d \n", rows, cols, rows*cols);
+  for(unsigned int i=0; i < rows*cols; i++){
+    //printf("%d, ", i);
+    if(low == lastCell(dim-1)){
+      //printf("rowfull %d", i);
+      high++;
+      low = firstCell(dim-1);
+    }
+    mpz_set_ui(elems[i], kappa(*high, *low));
+    low++;
+  }
+  
+  _HMatrix[dim] = create_gmp_matrix(rows, cols, elems);
+
+  return; 
+}
+
+
+void CellComplex::getDomainVertices(std::set<MVertex*, Less_MVertex>& domainVertices, bool subdomain){
+
+  std::vector<GEntity*> domain;
+  if(subdomain) domain = _subdomain;
+  else domain = _domain;
+  
+  for(unsigned int j=0; j < domain.size(); j++) {
+    for(unsigned int i=0; i < domain.at(j)->getNumMeshElements(); i++){
+      for(int k=0; k < domain.at(j)->getMeshElement(i)->getNumVertices(); k++){
+        MVertex* vertex = domain.at(j)->getMeshElement(i)->getVertex(k);
+        domainVertices.insert(vertex);
+      }
+    }
+  }
+      
+/*  for(unsigned int j=0; j < _domain.size(); j++) { 
+    for(unsigned int i=0; i < _domain.at(j)->getNumMeshVertices(); i++){
+      MVertex* vertex =  _domain.at(j)->getMeshVertex(i);
+      domainVertices.insert(vertex);
+    }
+    
+    std::list<GFace*> faces = _domain.at(j)->faces();
+    for(std::list<GFace*>::iterator fit = faces.begin(); fit != faces.end(); fit++){
+      GFace* face = *fit;
+      for(unsigned int i=0; i < face->getNumMeshVertices(); i++){
+        MVertex* vertex =  face->getMeshVertex(i);
+        domainVertices.insert(vertex);
+      }
+    }
+    std::list<GEdge*> edges = _domain.at(j)->edges();
+    for(std::list<GEdge*>::iterator eit = edges.begin(); eit != edges.end(); eit++){
+      GEdge* edge = *eit;
+      for(unsigned int i=0; i < edge->getNumMeshVertices(); i++){
+        MVertex* vertex =  edge->getMeshVertex(i);
+        domainVertices.insert(vertex);
+      }
+    }
+    std::list<GVertex*> vertices = _domain.at(j)->vertices();
+    for(std::list<GVertex*>::iterator vit = vertices.begin(); vit != vertices.end(); vit++){
+      GVertex* vertex = *vit;
+      for(unsigned int i=0; i < vertex->getNumMeshVertices(); i++){
+        MVertex* mvertex =  vertex->getMeshVertex(i);
+        domainVertices.insert(mvertex);
+      }
+    }
+    
+  }
+ */
+  return;
+}
+
+int CellComplex::writeComplexMSH(const std::string &name){
+  
+    
+  FILE *fp = fopen(name.c_str(), "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+  
+  fprintf(fp, "$NOD\n");
+  
+  std::set<MVertex*, Less_MVertex> domainVertices;
+  getDomainVertices(domainVertices, true);
+  getDomainVertices(domainVertices, false);
+  
+  fprintf(fp, "%d\n", domainVertices.size());
+  
+  for(std::set<MVertex*, Less_MVertex>::iterator vit = domainVertices.begin(); vit != domainVertices.end(); vit++){
+    MVertex* vertex = *vit;
+    fprintf(fp, "%d %.16g %.16g %.16g\n", vertex->getNum(), vertex->x(), vertex->y(), vertex->z() );
+  }
+  
+      
+  fprintf(fp, "$ENDNOD\n");
+  fprintf(fp, "$ELM\n");
+
+  fprintf(fp, "%d\n", _cells[0].size() + _cells[1].size() + _cells[2].size() + _cells[3].size());
+  
+  int index = 1;
+  
+  for(citer cit = firstCell(0); cit != lastCell(0); cit++) {
+    Cell* vertex = *cit;
+    fprintf(fp, "%d %d %d %d %d %d\n", index, 15, 0, 1, 1, vertex->getVertex(0));
+    index++;
+  }
+  
+  
+  for(citer cit = firstCell(1); cit != lastCell(1); cit++) {
+    Cell* edge = *cit;
+    fprintf(fp, "%d %d %d %d %d %d %d\n", index, 1, 0, 1, 2, edge->getVertex(0), edge->getVertex(1));
+    index++;
+  }
+  
+  for(citer cit = firstCell(2); cit != lastCell(2); cit++) {
+    Cell* face = *cit;
+    fprintf(fp, "%d %d %d %d %d %d %d %d\n", index, 2, 0, 1, 3, face->getVertex(0), face->getVertex(1), face->getVertex(2));
+    index++;
+  }
+  for(citer cit = firstCell(3); cit != lastCell(3); cit++) {
+    Cell* volume = *cit;
+    fprintf(fp, "%d %d %d %d %d %d %d %d %d\n", index, 4, 0, 1, 4, volume->getVertex(0), volume->getVertex(1), volume->getVertex(2), volume->getVertex(3));
+    index++;
+  }
+    
+  fprintf(fp, "$ENDELM\n");
+  
+  return 1;
+}
+
+
+void CellComplex::printComplex(int dim){
+  for (citer cit = firstCell(dim); cit != lastCell(dim); cit++){
+    Cell* cell = *cit;
+    for(int i = 0; i < cell->getNumVertices(); i++){
+      printf("%d ", cell->getVertex(i));
+    }
+    printf("\n");
+  }
+}
diff --git a/Geo/CellComplex.h b/Geo/CellComplex.h
new file mode 100644
index 0000000000..55aa4bf4f0
--- /dev/null
+++ b/Geo/CellComplex.h
@@ -0,0 +1,419 @@
+// Gmsh - Copyright (C) 1997-2009 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+//
+// Contributed by Matti Pellikka, 16.3.2009.
+
+#ifndef _CELLCOMPLEX_H_
+#define _CELLCOMPLEX_H_
+
+#include <stdio.h>
+#include <string>
+#include <algorithm>
+#include <set>
+#include <queue>
+#include "MElement.h"
+#include "GModel.h"
+#include "GEntity.h"
+#include "GRegion.h"
+#include "GFace.h"
+#include "GVertex.h"
+#include "gmp.h"
+extern "C" {
+  #include "gmp_normal_form.h" // perhaps make c++ headers instead?
+}
+
+// Abstract class representing a cell of a cell complex.
+class Cell
+{  
+  protected:
+   
+   // cell dimension
+   int _dim;
+   // maximum number of lower dimensional cells on the boundary of this cell
+   int _bdMaxSize;
+   // maximum number of higher dimensional cells on the coboundary of this cell
+   int _cbdMaxSize;
+
+   int _bdSize;
+   int _cbdSize;
+   
+   // whether this cell belongs to a subdomain
+   // used to in relative homology computation
+   bool _inSubdomain;
+   
+  public:
+   Cell(){}
+   virtual ~Cell(){}
+   
+   virtual int getDim() const { return _dim; };
+   //virtual int getTag() const { return _tag; };
+   
+   // get the number of vertices this cell has
+   virtual int getNumVertices() const = 0; //{return _vertices.size();}
+   
+   virtual int getVertex(int vertex) const = 0; //{return _vertices.at(vertex);}
+   virtual std::vector<int> getVertexVector() const = 0;
+   
+   // returns 1 or -1 if lower dimensional cell tau is on the boundary of this cell
+   // otherwise returns 0
+   // implementation will vary depending on cell type
+   virtual int kappa(Cell* tau) const = 0;
+   
+   // true if this cell has given vertex
+   virtual bool hasVertex(int vertex) const = 0;
+   
+   virtual unsigned int getBdMaxSize() const { return _bdMaxSize; };
+   virtual unsigned int getCbdMaxSize() const { return _cbdMaxSize; };
+
+   virtual int getBdSize() { return _bdSize; }
+   virtual void setBdSize(int size) { _bdSize = size; }
+   virtual int getCbdSize() { return _cbdSize; }
+   virtual void setCbdSize(int size) { _cbdSize = size; }
+   
+   virtual bool inSubdomain() const { return _inSubdomain; }
+   
+   // print cell vertices
+   virtual void printCell() const = 0;
+   
+   // return the coordinates of this cell (only used on zero-cells if anywhere)
+   virtual inline double x() const { return 0; }
+   virtual inline double y() const { return 0; }
+   virtual inline double z() const { return 0; }
+   
+   bool operator==(const Cell* c2){
+     if(this->getNumVertices() != c2->getNumVertices()){
+       return false;
+     }
+     for(int i=0; i < this->getNumVertices();i++){
+       if(this->getVertex(i) != c2->getVertex(i)){
+         return false;
+       }
+     }
+     return true;
+   }
+   
+   bool operator<(const Cell* c2) const {
+     if(this->getNumVertices() != c2->getNumVertices()){
+       return (this->getNumVertices() < c2->getNumVertices());
+     }
+     for(int i=0; i < this->getNumVertices();i++){
+       if(this->getVertex(i) < c2->getVertex(i)){
+         return true;
+       }
+     }
+     return false;
+   }
+   
+   
+   
+};
+
+// Simplicial cell type.
+class Simplex : public Cell
+{ 
+   
+ public:
+   Simplex(){
+     _cbdSize = 1000; // big number
+     _bdSize = 1000;
+   }
+   virtual ~Simplex(){}  
+  
+   // kappa for simplices
+   // checks whether lower dimensional simplex tau is on the boundary of this cell
+   virtual int kappa(Cell* tau) const; 
+   
+};
+
+// Zero simplex cell type.
+class ZeroSimplex : public Simplex
+{
+ private:
+   // number of the vertex
+   // same as the corresponding vertex in the finite element mesh
+   int _v;
+   // coordinates of this zero simplex, if needed
+   double _x, _y, _z;
+ public:
+   
+   ZeroSimplex(int vertex, bool subdomain=false, double x=0, double y=0, double z=0) : Simplex(){
+     _v = vertex;
+     _dim = 0;
+     _bdMaxSize = 0;
+     _cbdMaxSize = 1000; // big number
+     _x = x;
+     _y = y;
+     _z = z;
+     _inSubdomain = subdomain;
+   }
+   
+   virtual ~ZeroSimplex(){}
+   
+   virtual int getNumVertices() const { return 1; }
+   virtual int getVertex(int vertex) const {return _v; }
+   virtual bool hasVertex(int vertex) const {return (_v == vertex); }
+   
+   virtual std::vector<int> getVertexVector() const { return std::vector<int>(1,_v); }
+
+   virtual inline double x() const { return _x; }
+   virtual inline double y() const { return _y; }
+   virtual inline double z() const { return _z; }
+   
+   virtual void printCell()const { printf("Vertices: %d\n", _v); }
+   
+};
+
+// One simplex cell type.
+class OneSimplex : public Simplex
+{
+  private:
+   // numbers of the vertices of this simplex
+   // same as the corresponding vertices in the finite element mesh
+   int _v[2];
+  public:
+   
+   OneSimplex(std::vector<int> vertices, bool subdomain=false) : Simplex(){
+     sort(vertices.begin(), vertices.end());
+     _v[0] = vertices.at(0);
+     _v[1] = vertices.at(1);
+     _dim = 1;
+     _bdMaxSize = 2;
+     _cbdMaxSize = 1000;
+     _inSubdomain = subdomain;
+   }
+   
+   // constructor for the dummy one simplex
+   // used to find another definite one simplex from the cell complex
+   // first vertex gives the lower bound from where to look
+   OneSimplex(int vertex, int dummy){
+     _v[0] = vertex;
+     _v[1] = dummy;
+   }
+   
+   virtual ~OneSimplex(){}
+   
+   virtual int getNumVertices() const { return 2; }
+   virtual int getVertex(int vertex) const {return _v[vertex]; }
+   virtual bool hasVertex(int vertex) const {return (_v[0] == vertex || _v[1] == vertex); }
+   
+   virtual std::vector<int> getVertexVector() const { 
+     return std::vector<int>(_v, _v + sizeof(_v)/sizeof(int) ); }
+   
+   virtual void printCell() const { printf("Vertices: %d %d\n", _v[0], _v[1]); }
+};
+
+// Two simplex cell type.
+class TwoSimplex : public Simplex
+{
+  private:
+   // numbers of the vertices of this simplex
+   // same as the corresponding vertices in the finite element mesh
+   int _v[3];
+  public:
+   
+   TwoSimplex(std::vector<int> vertices, bool subdomain=false) : Simplex(){
+     sort(vertices.begin(), vertices.end());
+     _v[0] = vertices.at(0);
+     _v[1] = vertices.at(1);
+     _v[2] = vertices.at(2);
+     _dim = 2;
+     _bdMaxSize = 3;
+     _cbdMaxSize = 2;
+     _inSubdomain = subdomain;
+   }
+   // constructor for the dummy one simplex
+   TwoSimplex(int vertex, int dummy){
+     _v[0] = vertex;
+     _v[1] = dummy;
+     _v[2] = dummy;
+   }
+   
+   virtual ~TwoSimplex(){}
+   
+   virtual int getNumVertices() const { return 3; }
+   virtual int getVertex(int vertex) const {return _v[vertex]; }
+   virtual bool hasVertex(int vertex) const {return 
+       (_v[0] == vertex || _v[1] == vertex || _v[2] == vertex); }
+   virtual std::vector<int> getVertexVector() const { 
+     return std::vector<int>(_v, _v + sizeof(_v)/sizeof(int) ); }
+   
+   virtual void printCell() const { printf("Vertices: %d %d %d\n", _v[0], _v[1], _v[2]); }
+};
+
+// Three simplex cell type.
+class ThreeSimplex : public Simplex
+{
+  private:
+   // numbers of the vertices of this simplex
+   // same as the corresponding vertices in the finite element mesh
+   int _v[4];
+  public:
+   
+   ThreeSimplex(std::vector<int> vertices, bool subdomain=false) : Simplex(){
+     sort(vertices.begin(), vertices.end());
+     _v[0] = vertices.at(0);
+     _v[1] = vertices.at(1);
+     _v[2] = vertices.at(2);
+     _v[3] = vertices.at(3);
+     _dim = 3;
+     _bdMaxSize = 4;
+     _cbdMaxSize = 0;
+     _inSubdomain = subdomain;
+   }
+   // constructor for the dummy one simplex
+   ThreeSimplex(int vertex, int dummy){
+     _v[0] = vertex;
+     _v[1] = dummy;
+     _v[2] = dummy;
+     _v[3] = dummy;
+   }
+   
+   virtual ~ThreeSimplex(){}
+   
+   virtual int getNumVertices() const { return 4; }
+   virtual int getVertex(int vertex) const {return _v[vertex]; }
+   virtual bool hasVertex(int vertex) const {return 
+       (_v[0] == vertex || _v[1] == vertex || _v[2] == vertex || _v[3] == vertex); }
+   virtual std::vector<int> getVertexVector() const { 
+     return std::vector<int>(_v, _v + sizeof(_v)/sizeof(int) ); }
+   
+   virtual void printCell() const { printf("Vertices: %d %d %d %d\n", _v[0], _v[1], _v[2], _v[3]); }
+};
+
+// Ordering for the cells.
+class Less_Cell{
+  public:
+   bool operator()(const Cell* c1, const Cell* c2) const {
+     if(c1->getNumVertices() != c2->getNumVertices()){
+       return (c1->getNumVertices() < c2->getNumVertices());
+     }
+     for(int i=0; i < c1->getNumVertices();i++){
+       if(c1->getVertex(i) < c2->getVertex(i)){
+         return true;
+       }
+       else if (c1->getVertex(i) > c2->getVertex(i)){
+         return false;
+       }
+     }
+     return false;
+   }
+};
+
+// Ordering for the finite element mesh vertices
+class Less_MVertex{
+  public:
+   bool operator()(const MVertex* v1, const MVertex* v2) const {
+     return (v1->getNum() < v2->getNum());
+   }
+};
+
+// A class representing a cell complex made out of a finite element mesh.
+class CellComplex
+{
+ private:
+   
+   // the domain in the model which this cell complex covers
+   std::vector<GEntity*> _domain;
+   // a subdomain of the given domain
+   // used in relative homology computation, may be empty
+   std::vector<GEntity*> _subdomain;
+   
+   // sorted container of unique cells in this cell complex 
+   // mapped according to their dimension
+   std::map< int, std::set<Cell*, Less_Cell> >  _cells;
+   
+   // boundary operator matrices for this cell complex
+   // h_k: C_k -> C_(k-1)
+   std::map<int, gmp_matrix*>  _HMatrix; 
+   
+   
+ public:
+   // iterator for the cells of same dimension
+   typedef std::set<Cell*, Less_Cell>::iterator citer;
+   
+  protected: 
+   // enqueue cells in queue if they are not there already
+   virtual void enqueueCells(std::vector<Cell*>& cells, 
+                             std::queue<Cell*>& Q, std::set<Cell*, Less_Cell>& Qset);
+   virtual void enqueueCellsIt(std::vector< std::set<Cell*, Less_Cell>::iterator >& cells,
+                               std::queue<Cell*>& Q, std::set<Cell*, Less_Cell>& Qset);
+     
+   // remove cell from the queue set
+   virtual void removeCellQset(Cell*& cell, std::set<Cell*, Less_Cell>& Qset);
+   
+   // get all the finite element mesh vertices in the domain of this cell complex
+   virtual void getDomainVertices(std::set<MVertex*, Less_MVertex>& domainVertices, 
+                                  bool subdomain);
+   
+   // insert cells into this cell complex
+   virtual void insertCells(bool subdomain);
+   
+  public: 
+   CellComplex( std::vector<GEntity*> domain, std::vector<GEntity*> subdomain );
+   virtual ~CellComplex(){}
+   
+   // get the number of certain dimensional cells
+   virtual int getSize(int dim){ return _cells[dim].size(); }
+   
+   // iterators to the first and last cells of certain dimension
+   virtual citer firstCell(int dim) {return _cells[dim].begin(); }
+   virtual citer lastCell(int dim) {return _cells[dim].end(); }
+  
+   // find a cell in this cell complex
+   virtual std::set<Cell*, Less_Cell>::iterator findCell(int dim, std::vector<int>& vertices);
+   virtual std::set<Cell*, Less_Cell>::iterator findCell(int dim, int vertex, int dummy=0);
+   
+   
+   // kappa for two cells of this cell complex
+   // implementation will vary depending on cell type
+   virtual int kappa(Cell* sigma, Cell* tau) const { return sigma->kappa(tau); }
+   
+   // cells on the boundary of a cell
+   virtual std::vector<Cell*> bd(Cell* sigma);
+   virtual std::vector< std::set<Cell*, Less_Cell>::iterator > bdIt(Cell* sigma);
+   // cells on the coboundary of a cell
+   virtual std::vector<Cell*> cbd(Cell* tau);
+   virtual std::vector< std::set<Cell*, Less_Cell>::iterator > cbdIt(Cell* tau);
+   
+   // remove a cell from this cell complex
+   virtual void removeCell(Cell* cell);
+   virtual void removeCellIt(std::set<Cell*, Less_Cell>::iterator cell);
+   
+   // check whether two cells both belong to subdomain or if neither one does
+   virtual bool inSameDomain(Cell* c1, Cell* c2) const { return 
+       ( (!c1->inSubdomain() && !c2->inSubdomain()) || (c1->inSubdomain() && c2->inSubdomain()) ); }
+   
+   // coreduction of this cell complex
+   // removes corection pairs of cells of dimension dim and dim+1
+   virtual int coreduction(int dim);
+   // reduction of this cell complex
+   // removes reduction pairs of cell of dimension dim and dim-1
+   virtual int reduction(int dim);
+   
+   // useful functions for (co)reduction of cell complex
+   virtual void reduceComplex();
+   virtual void coreduceComplex();
+   
+   // queued coreduction presented in Mrozek's paper
+   // slower, but produces cleaner result
+   virtual int coreductionMrozek(Cell* generator);
+   
+   
+   // construct boundary operator matrix dim->dim-1
+   virtual void constructHMatrix(int dim);
+   // get the boundary operator matrix dim->dim-1
+   virtual gmp_matrix* getHMatrix(int dim) { return _HMatrix[dim]; }
+   
+   // print the vertices of cells of certain dimension
+   virtual void printComplex(int dim);
+   
+   // write this cell complex in legacy .msh format
+   virtual int writeComplexMSH(const std::string &name); 
+   
+};
+
+
+
+#endif
diff --git a/contrib/kbipack/Makefile b/contrib/kbipack/Makefile
new file mode 100755
index 0000000000..50e6f52bae
--- /dev/null
+++ b/contrib/kbipack/Makefile
@@ -0,0 +1,77 @@
+## Original Makefile by Saku Suuriniemi
+
+#FLAGS = -O3 -funroll-all-loops -ansi -pedantic -Wall -U DEBUG
+# Flags for different diagnostics: 
+#FLAGS = -g -ansi -pedantic -Wall -fprofile-arcs -ftest-coverage -pg
+#FLAGS = -g -ansi -pedantic -Wall -pg 
+
+# If gmp.h is not found de facto (e.g. when it is privately intalled), 
+# its path should be put here
+#INCL  = 
+
+#LIBS   = -lgmp
+#LIBDIR = .
+#SRC    = gmp_normal_form.c gmp_matrix_io.c gmp_matrix.c gmp_blas.c
+
+# The compiler options are for gcc. If it is not available, 
+# suitable flags must be set for the compiler
+#CC     = gcc
+#RM     = rm -f
+#RANLIB = ranlib
+
+#libkbi.a: $(SRC) 
+#	$(RM) *.o
+#	$(CC) -c $(FLAGS) $(SRC)
+#	ar r libkbi.a *.o
+#	$(RM) *.o
+#	$(RANLIB) libkbi.a 
+
+#compute_normal_form: libkbi compute_normal_form.c 
+#	$(CC) $(FLAGS) -L $(LIBDIR) $(INCL) compute_normal_form.c -o compute_normal_form -lkbi $(LIBS)
+#clean:
+#	$(RM) compute_normal_form
+#	$(RM) *.o
+
+## Makefile for Gmsh
+
+include ../../variables
+
+LIB = ../../lib/libGmshKbi${LIBEXT}
+
+INC = ${DASH}I.
+
+CFLAGS = ${OPTIM} ${FLAGS} ${INC} ${SYSINCLUDE} -lgmp
+
+SRC = gmp_normal_form.c gmp_matrix_io.c gmp_matrix.c gmp_blas.c
+      
+OBJ = ${SRC:.c=${OBJEXT}}
+
+.SUFFIXES: ${OBJEXT} .c
+
+${LIB}: ${OBJ}
+	${AR} ${ARFLAGS}${LIB} ${OBJ}
+	${RANLIB} ${LIB}
+
+cpobj: ${OBJ} 
+	cp -f ${OBJ} ../../lib/
+
+.c${OBJEXT}:
+	${CC} ${CFLAGS} ${DASH}c $<
+       
+clean:
+	${RM} *.o *.obj
+	
+depend:
+	(sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \
+         ${CXX} -MM ${CFLAGS} ${SRC} | sed 's/.o:/$${OBJEXT}:/g' \
+        ) > Makefile.new
+	cp Makefile Makefile.bak
+	cp Makefile.new Makefile
+	rm -f Makefile.new
+
+# DO NOT DELETE THIS LINE	
+gmp_normal_form${OBJEXT}: gmp_normal_form.c gmp_blas.h gmp_matrix.h \
+  gmp_normal_form.h
+gmp_matrix_io${OBJEXT}: gmp_matrix_io.c gmp_matrix_io.h gmp_matrix.h gmp_blas.h
+gmp_matrix${OBJEXT}: gmp_matrix.c gmp_matrix.h gmp_blas.h
+gmp_blas${OBJEXT}: gmp_blas.c gmp_blas.h
diff --git a/contrib/kbipack/Makefile.bak b/contrib/kbipack/Makefile.bak
new file mode 100755
index 0000000000..edcef1ca84
--- /dev/null
+++ b/contrib/kbipack/Makefile.bak
@@ -0,0 +1,77 @@
+#FLAGS = -O3 -funroll-all-loops -ansi -pedantic -Wall -U DEBUG
+# Flags for different diagnostics: 
+#FLAGS = -g -ansi -pedantic -Wall -fprofile-arcs -ftest-coverage -pg
+#FLAGS = -g -ansi -pedantic -Wall -pg 
+
+# If gmp.h is not found de facto (e.g. when it is privately intalled), 
+# its path should be put here
+#INCL  = 
+
+#LIBS   = -lgmp
+#LIBDIR = .
+#SRC    = gmp_normal_form.c gmp_matrix_io.c gmp_matrix.c gmp_blas.c
+
+# The compiler options are for gcc. If it is not available, 
+# suitable flags must be set for the compiler
+#CC     = gcc
+#RM     = rm -f
+#RANLIB = ranlib
+
+#libkbi.a: $(SRC) 
+#	$(RM) *.o
+#	$(CC) -c $(FLAGS) $(SRC)
+#	ar r libkbi.a *.o
+#	$(RM) *.o
+#	$(RANLIB) libkbi.a 
+
+#compute_normal_form: libkbi compute_normal_form.c 
+#	$(CC) $(FLAGS) -L $(LIBDIR) $(INCL) compute_normal_form.c -o compute_normal_form -lkbi $(LIBS)
+#clean:
+#	$(RM) compute_normal_form
+#	$(RM) *.o
+
+##
+
+include ../../variables
+
+LIB = ../../lib/libGmshKbi${LIBEXT}
+
+INC = ${DASH}I.
+
+CFLAGS = ${OPTIM} ${FLAGS} ${INC} ${SYSINCLUDE} -lgmp
+
+SRC = gmp_normal_form.c\
+      gmp_matrix_io.c\
+      gmp_matrix.c\
+      gmp_blas.c
+      
+OBJ = ${SRC:.c=${OBJEXT}}
+
+.SUFFIXES: ${OBJEXT} .c
+
+${LIB}: ${OBJ}
+	${AR} ${ARFLAGS}${LIB} ${OBJ}
+	${RANLIB} ${LIB}
+
+cpobj: ${OBJ} 
+	cp -f ${OBJ} ../../lib/
+
+.c${OBJEXT}:
+	${CC} ${CFLAGS} ${DASH}c $<
+       
+clean:
+	${RM} *.o *.obj
+	
+depend:
+	(sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \
+         ${CXX} -MM ${CFLAGS} ${SRC} | sed 's/.o:/$${OBJEXT}:/g' \
+        ) > Makefile.new
+	cp Makefile Makefile.bak
+	cp Makefile.new Makefile
+	rm -f Makefile.new
+
+# DO NOT DELETE THIS LINE	
+gmp_matrix${OBJEXT}: gmp_matrix.c gmp_blas.h gmp_matrix.h
+gmp_blas${OBJEXT}: gmp_blas.c gmp_blas.h
+gmp_matrix_io${OBJEXT}: gmp_matrix_io.c gmp_matrix_io.h
+gmp_normal_form${OBJEXT}: gmp_normal_form.c gmp_blas.h gmp_matrix.h gmp_normal_form.h
diff --git a/contrib/kbipack/Makefile~ b/contrib/kbipack/Makefile~
new file mode 100755
index 0000000000..3c361030c3
--- /dev/null
+++ b/contrib/kbipack/Makefile~
@@ -0,0 +1,75 @@
+#FLAGS = -O3 -funroll-all-loops -ansi -pedantic -Wall -U DEBUG
+# Flags for different diagnostics: 
+#FLAGS = -g -ansi -pedantic -Wall -fprofile-arcs -ftest-coverage -pg
+#FLAGS = -g -ansi -pedantic -Wall -pg 
+
+# If gmp.h is not found de facto (e.g. when it is privately intalled), 
+# its path should be put here
+#INCL  = 
+
+#LIBS   = -lgmp
+#LIBDIR = .
+#SRC    = gmp_normal_form.c gmp_matrix_io.c gmp_matrix.c gmp_blas.c
+
+# The compiler options are for gcc. If it is not available, 
+# suitable flags must be set for the compiler
+#CC     = gcc
+#RM     = rm -f
+#RANLIB = ranlib
+
+#libkbi.a: $(SRC) 
+#	$(RM) *.o
+#	$(CC) -c $(FLAGS) $(SRC)
+#	ar r libkbi.a *.o
+#	$(RM) *.o
+#	$(RANLIB) libkbi.a 
+
+#compute_normal_form: libkbi compute_normal_form.c 
+#	$(CC) $(FLAGS) -L $(LIBDIR) $(INCL) compute_normal_form.c -o compute_normal_form -lkbi $(LIBS)
+#clean:
+#	$(RM) compute_normal_form
+#	$(RM) *.o
+
+##
+
+include ../../variables
+
+LIB = ../../lib/libGmshKbi${LIBEXT}
+
+INC = ${DASH}I.
+
+CFLAGS = ${OPTIM} ${FLAGS} ${INC} ${SYSINCLUDE} -lgmp
+
+SRC = gmp_normal_form.c gmp_matrix_io.c gmp_matrix.c gmp_blas.c
+      
+OBJ = ${SRC:.c=${OBJEXT}}
+
+.SUFFIXES: ${OBJEXT} .c
+
+${LIB}: ${OBJ}
+	${AR} ${ARFLAGS}${LIB} ${OBJ}
+	${RANLIB} ${LIB}
+
+cpobj: ${OBJ} 
+	cp -f ${OBJ} ../../lib/
+
+.c${OBJEXT}:
+	${CC} ${CFLAGS} ${DASH}c $<
+       
+clean:
+	${RM} *.o *.obj
+	
+depend:
+	(sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \
+         ${CXX} -MM ${CFLAGS} ${SRC} | sed 's/.o:/$${OBJEXT}:/g' \
+        ) > Makefile.new
+	cp Makefile Makefile.bak
+	cp Makefile.new Makefile
+	rm -f Makefile.new
+
+# DO NOT DELETE THIS LINE	
+gmp_normal_form${OBJEXT}: gmp_normal_form.c gmp_blas.h gmp_matrix.h \
+  gmp_normal_form.h
+gmp_matrix_io${OBJEXT}: gmp_matrix_io.c gmp_matrix_io.h gmp_matrix.h gmp_blas.h
+gmp_matrix${OBJEXT}: gmp_matrix.c gmp_matrix.h gmp_blas.h
+gmp_blas${OBJEXT}: gmp_blas.c gmp_blas.h
diff --git a/contrib/kbipack/compute_normal_form.c b/contrib/kbipack/compute_normal_form.c
new file mode 100755
index 0000000000..f86e043cc2
--- /dev/null
+++ b/contrib/kbipack/compute_normal_form.c
@@ -0,0 +1,355 @@
+/* 
+   COMPUTE_NORMAL_FORM
+
+   Hermite and Smith normal forms of integer matrices. 
+
+   Implementation: Dense matrix with GMP-library's mpz_t elements to 
+                   hold huge integers. 
+
+   Algorithm: Kannan - Bachem algorithm with improvement by
+              Chou and Collins. Expects a large number of unit invariant 
+	      factors and takes advantage of them as they appear.
+
+   References: 
+    [1] Ravindran Kannan, Achim Bachem: 
+        "Polynomial algorithms for computing the Smith and Hermite normal 
+	forms of an integer matrix", 
+	SIAM J. Comput., vol. 8, no. 5, pp. 499-507, 1979.
+    [2] Tsu-Wu J.Chou, George E. Collins: 
+        "Algorithms for the solution of systems of linear Diophantine 
+	equations", 
+	SIAM J. Comput., vol. 11, no. 4, pp. 687-708, 1982.
+    [3] GMP homepage http://www.swox.com/gmp/
+    [4] GNU gmp page http://www.gnu.org/software/gmp/
+
+   Copyright (C) 3.11.2003 Saku Suuriniemi TUT/CEM
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   Saku Suuriniemi, TUT/Electromagetics
+   P.O.Box 692, FIN-33101 Tampere, Finland
+   saku.suuriniemi@tut.fi
+   
+   $Id: compute_normal_form.c,v 1.1 2009-03-30 14:10:57 matti Exp $
+*/
+
+
+#include<stdlib.h>
+#include<stdio.h>
+#include<string.h>
+#include"gmp_matrix_io.h"
+#include"gmp_normal_form.h"
+
+typedef enum {HERMITE, SMITH}     nf_flag;
+typedef enum {NOT_WANTED, WANTED} w_flag;
+
+static int nf_parse_options(int arg_counter, char ** args, 
+			    char ** p_infile_name, char ** p_outfile_name, 
+			    nf_flag *       p_normal_form_requested, 
+			    inverted_flag * p_left_inverted, 
+			    inverted_flag * p_right_inverted, 
+			    w_flag *        p_help_wanted, 
+			    w_flag *        p_verbose_wanted)
+{
+  size_t ind1, ind2;
+  size_t outfilename_length;
+  
+  /* If no infile is specified, the error is indisputable. 
+      => Print help and exit. 
+     Infile should be the last argument. */
+
+  if(strncmp(args[arg_counter-1], "-", 1) == 0)
+    {
+      *(p_help_wanted) = WANTED;
+      return EXIT_SUCCESS;
+    }
+
+  /* Should I find arguments? */
+  if(arg_counter > 2)
+    {
+      for(ind1 = 1; ind1 < arg_counter-1; ind1++)
+	{
+	  /* No multiple infile names ! */
+	  if(strncmp(args[ind1], "-", 1) != 0)
+	    {
+	      *(p_help_wanted) = WANTED;
+	      return EXIT_SUCCESS;
+	    }
+
+	  /* Parse the option letters after '-' one by one */
+	  for(ind2 = 1; ind2 < strlen(args[ind1]); ind2++)
+	    {
+	      switch(args[ind1][ind2])
+		{
+		case 'h':
+		  *(p_help_wanted) = WANTED;
+		  return EXIT_SUCCESS;
+		  break;
+		case 'H':
+		  *(p_normal_form_requested) = HERMITE;
+		  break;
+		case 'S':
+		  *(p_normal_form_requested) = SMITH;
+		  break;
+		case 'l':
+		  *(p_left_inverted) = INVERTED;
+		  break;
+		case 'r':
+		  *(p_right_inverted) = INVERTED;
+		  break;
+		case 'v':
+		  *(p_verbose_wanted) = WANTED;
+		  break;
+		}
+	    }	  
+	}
+    }
+
+  /* Get the infile name */
+  * p_infile_name = (char *) calloc(strlen(args[arg_counter-1]) + 1, 
+				    sizeof(char));
+  if(* p_infile_name == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  strcpy(* p_infile_name, args[arg_counter-1]);
+
+  /* Create the outfile name */
+  outfilename_length = strcspn( * p_infile_name, ".");
+  * p_outfile_name = (char *) calloc(outfilename_length + 1, 
+				     sizeof(char));
+  if( * p_outfile_name == NULL)
+    {
+      free( * p_infile_name);
+      return EXIT_FAILURE;
+    }
+
+  strncpy(* p_outfile_name, * p_infile_name, outfilename_length);
+  (* p_outfile_name)[outfilename_length] = '\0';
+
+  return EXIT_SUCCESS;
+}
+
+static int nf_print_help(void)
+{
+  fprintf(stdout, "Usage: compute_normal_form [-hHSlrv] infile\n");
+  fprintf(stdout, "\n");
+  fprintf(stdout, "Description: Compute the Hermite/Smith normal form of an integer matrix.\n");
+  fprintf(stdout, "\n");
+  fprintf(stdout, "Input: \"infile\" is an ascii file, which contains an integer matrix\n");
+  fprintf(stdout, "       in the sparse coordinate format:\n"); 
+  fprintf(stdout, "\n");
+  fprintf(stdout, "        # All comments before the data\n");
+  fprintf(stdout, "        rows columns nonzeros\n");
+  fprintf(stdout, "        row1 col1    value1\n");
+  fprintf(stdout, "        row2 col2    value2\n");
+  fprintf(stdout, "        ...\n");
+  fprintf(stdout, "\n");
+  fprintf(stdout, "       No multiple infiles, please.\n");
+  fprintf(stdout, "\n");
+  fprintf(stdout, "Options: -h This help\n");
+  fprintf(stdout, "         -H Compute the Hermite normal form (default)\n");
+  fprintf(stdout, "         -S Compute the Smith normal form\n");
+  fprintf(stdout, "         -l Left factor inverted (A = P^(T)LU instead of PA = LU for Hermite, \n");
+  fprintf(stdout, "                                  U^(-1)A = SV instead of A = USV for Smith)\n");
+  fprintf(stdout, "         -r Right factor inverted (PAU^(-1) = L instead of PA = LU for Hermite, \n");
+  fprintf(stdout, "                                   AV^(-1) = US instead of A = USV for Smith)\n");
+  fprintf(stdout, "         -v Verbose. Print the matrix and the decomposition to sdtout, \n");
+  fprintf(stdout, "                     so that the user may check that the problem is read and written\n");
+  fprintf(stdout, "                     correctly.\n");
+  fprintf(stdout, "\n");
+  fprintf(stdout, "Output: For inpur matrix in e.g. \"infile.coord\", writes ascii files\n");
+  fprintf(stdout, "        \"infile.left\", \"infile.can\", and \"infile.right\", which \n");
+  fprintf(stdout, "        contain the left factor, canonical form, and the right factor, \n");
+  fprintf(stdout, "        respectively. The matrices are written in the sparse coordinate\n");
+  fprintf(stdout, "        format.\n");
+  fprintf(stdout, "\n");
+  fprintf(stdout, "Note: The return values may be big enough to not fit the standard integer types.\n");
+  fprintf(stdout, "\n");
+  fprintf(stdout, "Copyright (C) 3.11.2003 Saku Suuriniemi, Tampere University of Technology/Electromagnetics\n");
+  fprintf(stdout, "                        P.O.Box 692, FIN-33101 Tampere, Finland. saku.suuriniemi@tut.fi\n");
+  fprintf(stdout, "\n");
+  fprintf(stdout, "Licenced under GNU General Public Licence (GPL) version 2 or later, ABSOLUTELY NO WARRANTY.\n");
+
+  return EXIT_SUCCESS;
+}
+
+
+static char* append_suffix(const char * file_base_name, const char * suffix)
+{
+  char * filename;
+
+  if((file_base_name == NULL) || (suffix == NULL))
+    {
+      return NULL;
+    }
+
+  filename = 
+    (char*) calloc(strlen(file_base_name)+strlen(suffix)+2,
+		   sizeof(char));
+
+  if(filename == NULL)
+    {
+      return NULL;
+    }
+
+  strcpy(filename, file_base_name);
+  strcat(filename, ".");
+  strcat(filename, suffix);
+
+  return filename;
+}
+
+
+
+int main(int argc, char * argv[])
+{
+  char            * infile_name;
+  char            * file_base_name;
+  char            * outfile_name;
+  gmp_matrix      * A;
+  gmp_normal_form * normal_form;
+  w_flag            help_wanted;
+  w_flag            verbose_wanted;
+  nf_flag           normal_form_requested;
+  inverted_flag     left_inverted;
+  inverted_flag     right_inverted;
+
+  if(argc == 1)
+    {
+      fprintf(stdout, "Please try \"compute_normal_form -h\" for help.\n");
+      return EXIT_SUCCESS;
+    }
+
+  /* Default settings before option parsing */
+  normal_form_requested = HERMITE;
+  left_inverted         = NOT_INVERTED;
+  right_inverted        = NOT_INVERTED;
+  help_wanted           = NOT_WANTED;
+  verbose_wanted        = NOT_WANTED;
+
+  if( nf_parse_options(argc, argv, 
+		       &infile_name, &file_base_name, 
+		       &normal_form_requested, 
+		       &left_inverted, &right_inverted, 
+		       &help_wanted, 
+		       &verbose_wanted) 
+      == EXIT_FAILURE )
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* In this case, no dynamical memory has been allocated by 
+     nf_parse_options ! */
+  if(help_wanted == WANTED)
+    {
+      return nf_print_help();
+    }
+
+  if(verbose_wanted == WANTED)
+    {
+      fprintf(stdout, "Computing ");
+      if(normal_form_requested == HERMITE)
+	{
+	  fprintf(stdout, "Hermite");
+	}
+      else
+	{
+	  fprintf(stdout, "Smith");
+	}
+      fprintf(stdout, " normal form\n");
+      if(left_inverted == INVERTED)
+	{
+	  fprintf(stdout, "The left factor will be inverted.\n");
+	}
+      if(right_inverted == INVERTED)
+	{
+	  fprintf(stdout, "The right factor will be inverted.\n");
+	}
+      fprintf(stdout, "Matrix read from file %s\n", infile_name);
+    }
+
+  /*************************************************************/
+  /* We have all we need to get to the computational business: */
+  /*************************************************************/
+
+  /* Read the matrix */
+  A = gmp_matrix_read_coord(infile_name);
+  free(infile_name);
+
+  if(verbose_wanted == WANTED)
+    {
+      fprintf(stdout, "Input matrix:\n");
+      gmp_matrix_fprintf(stdout, A);
+    }
+
+  /* Compute the requested normal form */
+  switch(normal_form_requested)
+    {
+    case SMITH:
+      normal_form = create_gmp_Smith_normal_form(A, left_inverted, right_inverted);
+      break;
+
+    default:
+      normal_form = create_gmp_Hermite_normal_form(A, left_inverted, right_inverted);
+      break;
+    }
+
+
+  if(verbose_wanted == WANTED)
+    {
+      fprintf(stdout, "Left factor:\n");
+      gmp_matrix_fprintf(stdout, normal_form->left);
+      fprintf(stdout, "Canonical form:\n");
+      gmp_matrix_fprintf(stdout, normal_form->canonical);
+      fprintf(stdout, "Right factor:\n");
+      gmp_matrix_fprintf(stdout, normal_form->right);
+    }
+
+  /* Save the result to the output files */
+  outfile_name = append_suffix(file_base_name, "left");
+  if( gmp_matrix_write_coord(outfile_name, normal_form -> left) 
+      == EXIT_SUCCESS)
+    {
+      if(verbose_wanted == WANTED)
+	fprintf(stdout, "Left factor successfully writen to file     %s\n", outfile_name);
+    }
+
+  free(outfile_name);
+
+  outfile_name = append_suffix(file_base_name, "can");
+  if( gmp_matrix_write_coord(outfile_name, normal_form -> canonical)
+      == EXIT_SUCCESS)
+    {
+      if(verbose_wanted == WANTED)
+	fprintf(stdout, "Canonical form successfully writen to file  %s\n", outfile_name);
+    }
+  free(outfile_name);
+
+  outfile_name = append_suffix(file_base_name, "right");
+  if( gmp_matrix_write_coord(outfile_name, normal_form -> right)
+      == EXIT_SUCCESS)
+    {
+      if(verbose_wanted == WANTED)
+	fprintf(stdout, "Right factor successfully writen to file    %s\n", outfile_name);
+    }
+  free(outfile_name);
+
+  free(file_base_name);
+  destroy_gmp_normal_form(normal_form);
+  
+  return EXIT_SUCCESS;
+}
diff --git a/contrib/kbipack/gmp_blas.c b/contrib/kbipack/gmp_blas.c
new file mode 100755
index 0000000000..501c4eba54
--- /dev/null
+++ b/contrib/kbipack/gmp_blas.c
@@ -0,0 +1,227 @@
+/* 
+   Source file for integer-oriented elementary versions of some 
+   subroutines of BLAS. There routines are chosen to facilitate the 
+   computation of Hermite and Smith normal forms of matrices of 
+   modest size. 
+
+   Copyright (C) 28.10.2003 Saku Suuriniemi TUT/CEM
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   Saku Suuriniemi, TUT/Electromagetics
+   P.O.Box 692, FIN-33101 Tampere, Finland
+   saku.suuriniemi@tut.fi
+
+   $Id: gmp_blas.c,v 1.1 2009-03-30 14:10:57 matti Exp $
+*/
+
+/* #include<stdio.h> */
+#include"gmp_blas.h"
+
+void gmp_blas_swap(size_t n, mpz_t* x, size_t incx, mpz_t* y, size_t incy)
+{
+  mpz_t  elbow;
+  size_t ind;
+
+  mpz_init(elbow);
+
+  for(ind = 0; ind < n; ind++)
+    {
+      mpz_set(elbow      , x[ind*incx]);
+      mpz_set(x[ind*incx], y[ind*incy]);
+      mpz_set(y[ind*incy], elbow);
+    }
+  mpz_clear(elbow);
+
+  return;
+}
+
+void gmp_blas_scal(size_t n, mpz_t a, mpz_t* x, size_t incx)
+{
+  size_t ind;
+
+  for(ind = 0; ind < n; ind++)
+    {
+      mpz_mul (x[ind*incx], x[ind*incx], a);
+    }
+
+  return;
+}
+
+void gmp_blas_copy(size_t n, const mpz_t* x, size_t incx, mpz_t* y, size_t incy)
+{
+  size_t ind;
+
+  for(ind = 0; ind < n; ind++)
+    {
+      mpz_set(y[ind*incy], x[ind*incx]);
+    }
+
+  return;
+}
+
+void gmp_blas_axpy(size_t n, mpz_t a, const mpz_t* x, size_t incx, 
+		   mpz_t* y, size_t incy)
+{
+  size_t ind;
+
+  for(ind = 0; ind < n; ind++)
+    {
+      mpz_addmul (y[ind*incy], a, x[ind*incx]);
+    }
+
+  return;
+}
+
+void
+gmp_blas_dot(mpz_t * d, size_t n, 
+	     const mpz_t* x, size_t incx, 
+	     const mpz_t* y, size_t incy)
+{
+  size_t ind;
+
+  mpz_set_si(*d,0);
+
+  for(ind = 0; ind < n; ind++)
+    {
+      mpz_addmul (*d, x[ind*incx], y[ind*incy]);
+    }
+
+  return;  
+}
+
+/* x <- ax + by 
+   y <- cx + dy */
+void gmp_blas_rot(size_t n, 
+		  mpz_t a, mpz_t b, mpz_t* x, size_t incx, 
+		  mpz_t c, mpz_t d, mpz_t* y, size_t incy)
+{
+  mpz_t  ax;
+  mpz_t  by;
+  mpz_t  cx;
+  mpz_t  dy;
+
+  size_t ind;
+
+  mpz_init(ax);
+  mpz_init(by);
+  mpz_init(cx);
+  mpz_init(dy);
+
+  for(ind = 0; ind < n; ind++)
+    {
+      mpz_mul (ax, a, x[ind*incx]);
+      mpz_mul (by, b, y[ind*incy]);
+      mpz_mul (cx, c, x[ind*incx]);
+      mpz_mul (dy, d, y[ind*incy]);
+      mpz_add (x[ind*incx], ax, by);
+      mpz_add (y[ind*incy], cx, dy);
+    }
+
+  mpz_clear(ax);
+  mpz_clear(by);
+  mpz_clear(cx);
+  mpz_clear(dy);
+
+  return;
+}
+
+
+/* Returns k such that x[(k-1)*incx] != 0 holds for the first time. 
+   If none found, returns n+1. */
+size_t gmp_blas_inz  (size_t n, const mpz_t* x, size_t incx)
+{
+  size_t ind;
+
+  for(ind = 0; ind < n; ind++)
+    {
+      if(mpz_sgn (x[ind*incx]) != 0)
+	{  
+	  return ind+1; 
+	}
+    }
+
+  /* No nonzeros found */
+  return n+1; 
+}
+
+size_t gmp_blas_iamax(size_t n, const mpz_t* x, size_t incx)
+{
+  size_t ind;
+  size_t ind_so_far;
+  mpz_t  max_so_far;
+
+  mpz_init(max_so_far);
+  mpz_set(max_so_far, 0);
+  ind_so_far = 0;
+
+  for(ind = 0; ind < n; ind++)
+    {
+      if(mpz_cmpabs (x[ind*incx], max_so_far) > 0)
+	{  
+	  mpz_set(max_so_far, x[ind*incx]);
+	  ind_so_far = ind;
+	}
+    }
+
+  /* No nonzeros found */
+  if(mpz_sgn (max_so_far) == 0)
+    {
+      mpz_clear(max_so_far);
+      return n + 1; 
+    }
+
+  /* Nonzero maximal by modulus element found */
+  mpz_clear(max_so_far);
+  return ind_so_far + 1; 
+}
+
+
+size_t gmp_blas_iamin(size_t n, const mpz_t* x, size_t incx)
+{
+  size_t ind;
+  size_t ind_so_far;
+  mpz_t  min_so_far;
+
+  ind_so_far = gmp_blas_inz (n, x, incx);
+
+  /* No nonzeros found? */
+  if(ind_so_far == n+1)
+    {
+      return n+1;
+    }
+
+  /* OK, There is at leat one nonzero element */
+  mpz_init(min_so_far);
+  mpz_set(min_so_far, x[(ind_so_far-1)*incx]);
+
+  /* Scan through te rest of the elements to see if smaller  
+     elements by modulus are found */
+  for(ind = ind_so_far-1; ind < n; ind++)
+    {
+      if(mpz_sgn (x[ind*incx]) != 0)
+	{
+	  if(mpz_cmpabs (x[ind*incx], min_so_far) < 0)
+	    { 
+	      mpz_set(min_so_far, x[ind*incx]);
+	      ind_so_far = ind + 1;
+	    }
+	}
+    }
+
+  /* Nonzero minimal by modulus element found */
+  mpz_clear(min_so_far);
+  return ind_so_far;
+}
diff --git a/contrib/kbipack/gmp_blas.h b/contrib/kbipack/gmp_blas.h
new file mode 100755
index 0000000000..6c9c88b2f8
--- /dev/null
+++ b/contrib/kbipack/gmp_blas.h
@@ -0,0 +1,103 @@
+/* 
+   Header file for integer-oriented elementary versions of some 
+   subroutines of BLAS. There routines are chosen to facilitate the 
+   computation of Hermite and Smith normal forms of matrices of 
+   modest size. 
+
+   Copyright (C) 28.10.2003 Saku Suuriniemi TUT/CEM
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   Saku Suuriniemi, TUT/Electromagetics
+   P.O.Box 692, FIN-33101 Tampere, Finland
+   saku.suuriniemi@tut.fi
+
+   $Id: gmp_blas.h,v 1.1 2009-03-30 14:10:57 matti Exp $
+*/
+
+#ifndef __GMP_BLAS_H__
+#define __GMP_BLAS_H__
+
+#include <stdio.h>
+#include <gmp.h>
+
+/***********/
+/* Level 1 */
+/***********/
+
+/* x <-> y */
+void 
+gmp_blas_swap(size_t n,          mpz_t * x, size_t incx, mpz_t * y, size_t incy);
+
+/* x <- ax */
+void 
+gmp_blas_scal(size_t n, mpz_t a, mpz_t * x, size_t incx); 
+
+/* y <- x */
+void 
+gmp_blas_copy(size_t n, const mpz_t * x, size_t incx, mpz_t * y, size_t incy); 
+
+/* y <- ax + y */
+void 
+gmp_blas_axpy(size_t n, mpz_t a, 
+	      const mpz_t * x, size_t incx, 
+	      mpz_t * y,       size_t incy); 
+
+/* d <- x^T y The integer * d has to be initialized by e.g. mpz_init() ! */
+void
+gmp_blas_dot(mpz_t * d, size_t n, 
+	     const mpz_t * x, size_t incx, 
+	     const mpz_t * y, size_t incy); 
+
+
+/* Givens rotations are obviously impossible. However, the extended Euclid  
+   algorithm produces its close relative: 
+
+      g = gcd(a,b) & ka+lb = g 
+
+   E.g. in the computation of HNF, right-multipliations by the matrix 
+
+     [k -b/g]
+     [l  a/g]
+
+   occur frequently. Hence the name for the "Euclid rotation" function pair: */
+
+/* For a,b, find k,l,g such that ka+lb = g = gcd(a,b) holds */
+void gmp_blas_rotg(mpz_t g, mpz_t k, mpz_t l, mpz_t a, mpz_t b);
+#define gmp_blas_rotg mpz_gcdext
+
+
+/* x <- ax + by 
+   y <- cx + dy */
+void gmp_blas_rot(size_t n, 
+		  mpz_t a, mpz_t b, mpz_t * x, size_t incx, 
+		  mpz_t c, mpz_t d, mpz_t * y, size_t incy); 
+
+
+/* In integer computations, the first nonzero element, minimal nonzero element 
+   by modulus, and maximal nonzero element by modulus come often handy: */
+
+/* Returns k such that x[(k-1)*incx] != 0 holds for the first time. 
+   In FORTRAN, this is x((k-1)*incx + 1) .NE. 0.
+   If none found, returns n+1. */
+size_t gmp_blas_inz  (size_t n, const mpz_t * x, size_t incx);
+
+/* Similarly, x[(k-1)*incx] is maximal by modulus */
+size_t gmp_blas_iamax(size_t n, const mpz_t * x, size_t incx);
+
+/* Similarly, x[(k-1)*incx] is nonzero and minimal by modulus */
+size_t gmp_blas_iamin(size_t n, const mpz_t * x, size_t incx);
+
+#endif
diff --git a/contrib/kbipack/gmp_blas.o b/contrib/kbipack/gmp_blas.o
new file mode 100755
index 0000000000000000000000000000000000000000..02064581725915c72fb204793a3f9436e14c5cfc
GIT binary patch
literal 10724
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
Jk?&B-{}+rJd=dZv

literal 0
HcmV?d00001

diff --git a/contrib/kbipack/gmp_matrix.c b/contrib/kbipack/gmp_matrix.c
new file mode 100755
index 0000000000..23d648c89e
--- /dev/null
+++ b/contrib/kbipack/gmp_matrix.c
@@ -0,0 +1,673 @@
+/* 
+   Source file for integer-oriented matrix, relying on the arbitrary 
+   precision integers from the GNU gmp package. 
+
+   Copyright (C) 28.10.2003 Saku Suuriniemi TUT/CEM
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   Saku Suuriniemi, TUT/Electromagetics
+   P.O.Box 692, FIN-33101 Tampere, Finland
+   saku.suuriniemi@tut.fi
+
+   $Id: gmp_matrix.c,v 1.1 2009-03-30 14:10:57 matti Exp $
+*/
+
+
+#include<stdlib.h>
+#include"gmp_matrix.h"
+
+gmp_matrix * 
+create_gmp_matrix(size_t r, size_t c, 
+		  const mpz_t * e)
+{
+  gmp_matrix * new_matrix;
+  size_t       ind;
+
+  new_matrix = (gmp_matrix * ) malloc(sizeof(gmp_matrix));
+  if(new_matrix == NULL)
+    {
+      return NULL;
+    }
+
+  new_matrix -> storage = (mpz_t *) calloc(r*c, sizeof(mpz_t));
+  if(new_matrix -> storage == NULL)
+    {
+      free(new_matrix);
+      return NULL;
+    }
+
+  new_matrix -> rows = r;
+  new_matrix -> cols = c;
+
+  for(ind = 0; ind < r*c; ind ++)
+    {
+      mpz_init (new_matrix -> storage[ind]);
+      mpz_set  (new_matrix -> storage[ind], e[ind]);
+    }
+
+  return new_matrix;
+}
+
+
+gmp_matrix * 
+create_gmp_matrix_identity(size_t dim)
+{
+  gmp_matrix * new_matrix;
+  size_t       ind;
+
+  new_matrix = (gmp_matrix * ) malloc(sizeof(gmp_matrix));
+  if(new_matrix == NULL)
+    {
+      return NULL;
+    }
+
+  new_matrix -> storage = (mpz_t *) calloc(dim*dim, sizeof(mpz_t));
+  if(new_matrix -> storage == NULL)
+    {
+      free(new_matrix);
+      return NULL;
+    }
+
+  new_matrix -> rows = dim;
+  new_matrix -> cols = dim;
+
+  for(ind = 0; ind < dim*dim; ind ++)
+    {
+      mpz_init_set_si (new_matrix -> storage[ind], 0);
+    }
+
+  for(ind = 0; ind < dim; ind ++)
+    {
+      mpz_set_ui(new_matrix -> storage[ind*(dim+1)], 1);
+    }
+
+  return new_matrix;
+}
+
+
+gmp_matrix * 
+create_gmp_matrix_zero(size_t rows, size_t cols)
+{
+  gmp_matrix * new_matrix;
+  size_t       ind;
+
+  new_matrix = (gmp_matrix * ) malloc(sizeof(gmp_matrix));
+  if(new_matrix == NULL)
+    {
+      return NULL;
+    }
+
+  new_matrix -> storage = (mpz_t *) calloc(rows*cols, sizeof(mpz_t));
+  if(new_matrix -> storage == NULL)
+    {
+      free(new_matrix);
+      return NULL;
+    }
+
+  new_matrix -> rows = rows;
+  new_matrix -> cols = cols;
+
+  for(ind = 0; ind < rows*cols; ind ++)
+    {
+      mpz_init_set_si (new_matrix -> storage[ind], 0);
+    }
+
+  return new_matrix;
+}
+
+int 
+destroy_gmp_matrix(gmp_matrix * m)
+{
+  size_t       ind, nmb_storage;;
+
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  if(m -> storage == NULL)
+    {
+      free(m);
+      return EXIT_FAILURE;
+    }
+
+  nmb_storage = (m -> rows)*(m -> cols);
+
+  for(ind = 0; ind < nmb_storage; ind++)
+    {
+      mpz_clear(m -> storage[ind]);
+    }
+
+  free(m -> storage);
+  free(m);
+  return EXIT_SUCCESS;
+}
+
+
+size_t
+gmp_matrix_rows(const gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return 0;
+    }
+
+  return m -> rows;
+}
+
+
+size_t
+gmp_matrix_cols(const gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return 0;
+    }
+
+  return m -> cols;
+}
+
+/* elem <- (matrix(row, col)) */
+int
+gmp_matrix_get_elem(mpz_t elem, size_t row, size_t col,
+		    const gmp_matrix * m)
+{
+#ifdef DEBUG
+
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+#endif
+
+  mpz_set(elem, m -> storage[(col-1)*(m -> rows)+row-1]);
+  return EXIT_SUCCESS;
+}
+
+int  
+gmp_matrix_swap_rows(size_t row1, size_t row2, gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((row1 < 1) || (row1 > m->rows) || (row2 < 1) || (row2 > m->rows))
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* printf("Swapping rows %i %i\n", row1, row2); */
+  gmp_blas_swap(m -> cols,
+		&(m -> storage[row1-1]), m -> rows, 
+		&(m -> storage[row2-1]), m -> rows);
+
+  return EXIT_SUCCESS;
+}
+
+int
+gmp_matrix_swap_cols(size_t col1, size_t col2, gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((col1 < 1) || (col1 > m->cols) || (col2 < 1) || (col2 > m->cols))
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* printf("Swapping cols %i %i\n", col1, col2); */
+  gmp_blas_swap(m -> rows, 
+		&(m -> storage[(m->rows)*(col1-1)]), 1, 
+		&(m -> storage[(m->rows)*(col2-1)]), 1);
+
+  return EXIT_SUCCESS;
+}
+
+int
+gmp_matrix_negate_row(size_t row, gmp_matrix * m)
+{
+  mpz_t minus_one;
+
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((row < 1) || (row > m->rows))
+    {
+      return EXIT_FAILURE;
+    }
+
+  mpz_init(minus_one);
+  mpz_set_si(minus_one, -1);
+  gmp_blas_scal(m -> cols, minus_one, (&m -> storage[row-1]), m -> rows); 
+  mpz_clear(minus_one);
+  return EXIT_SUCCESS; 
+}
+
+int
+gmp_matrix_negate_col(size_t col, gmp_matrix * m)
+{
+  mpz_t minus_one;
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((col < 1) || (col > m->cols))
+    {
+      return EXIT_FAILURE;
+    }
+
+  mpz_init(minus_one);
+  mpz_set_si(minus_one, -1);
+  gmp_blas_scal(m -> rows, minus_one, 
+		&(m -> storage[(m->rows)*(col-1)]), 1); 
+  mpz_clear(minus_one);
+  return EXIT_SUCCESS;
+}
+
+/* row2 <- a*row1 + row2*/
+int
+gmp_matrix_add_row(mpz_t a, size_t row1, size_t row2,
+		   gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((row1 < 1) || (row1 > m->rows) || (row2 < 1) || (row2 > m->rows))
+    {
+      return EXIT_FAILURE;
+    }
+
+  gmp_blas_axpy(m->cols, a, 
+		(const mpz_t *) &(m->storage[row1-1]), m->rows, 
+		&(m->storage[row2-1]), m->rows); 
+
+  return EXIT_SUCCESS;
+}
+
+int
+gmp_matrix_add_col(mpz_t a, size_t col1, size_t col2,
+		   gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((col1 < 1) || (col1 > m->cols) || (col2 < 1) || (col2 > m->cols))
+    {
+      return EXIT_FAILURE;
+    }
+
+  gmp_blas_axpy(m->rows, a, 
+		(const mpz_t *) &(m -> storage[(m->rows)*(col1-1)]), 1, 
+		&(m -> storage[(m->rows)*(col2-1)]), 1); 
+
+  return EXIT_SUCCESS;
+}
+
+/* row1 <- a*row1 + b*row2
+   row2 <- c*row1 + d*row2 */
+int
+gmp_matrix_row_rot(mpz_t a, mpz_t b, size_t row1,
+		   mpz_t c, mpz_t d, size_t row2,
+		   gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((row1 < 1) || (row1 > m->rows) || (row2 < 1) || (row2 > m->rows))
+    {
+      return EXIT_FAILURE;
+    }
+
+  gmp_blas_rot(m->cols, 
+	       a, b, &(m->storage[row1-1]), m->rows, 
+	       c, d, &(m->storage[row2-1]), m->rows); 
+
+  return EXIT_SUCCESS;
+}
+int
+gmp_matrix_col_rot(mpz_t a, mpz_t b, size_t col1,
+		   mpz_t c, mpz_t d, size_t col2,
+		   gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((col1 < 1) || (col1 > m->cols) || (col2 < 1) || (col2 > m->cols))
+    {
+      return EXIT_FAILURE;
+    }
+
+/*   printf("a: %i b: %i c: %i d:%i col1: %i col2: %i\n", */
+/* 	 mpz_get_si(a), */
+/* 	 mpz_get_si(b), */
+/* 	 mpz_get_si(c), */
+/* 	 mpz_get_si(d), */
+/* 	 col1, col2); */
+
+  gmp_blas_rot(m->rows, 
+	       a, b, &(m -> storage[(m->rows)*(col1-1)]), 1, 
+	       c, d, &(m -> storage[(m->rows)*(col2-1)]), 1); 
+
+  return EXIT_SUCCESS;
+}
+
+size_t
+gmp_matrix_col_inz (size_t r1, size_t r2, size_t c, 
+		    gmp_matrix * m)
+{
+  size_t result;
+
+  if(m == NULL)
+    {
+      return 0;
+    }
+  if((r1 < 1) || (r1 > m->rows) || 
+     (r2 < 1) || (r2 > m->rows) ||
+     (r2 < r1) || (c < 1) || (c > m->cols))
+    {
+      return 0;
+    }
+
+  result = gmp_blas_inz(r2-r1+1, 
+			(const mpz_t *) &(m->storage[(c-1)*(m->rows)+r1-1]), 
+			1);
+
+  if(result > r2-r1+1)
+    {
+      return 0;
+    }
+
+  return result;
+}
+
+size_t
+gmp_matrix_row_inz (size_t r, size_t c1, size_t c2, 
+		    gmp_matrix * m)
+{
+  size_t result;
+
+  if(m == NULL)
+    {
+      return 0;
+    }
+  if((r  < 1) || (r  > m->rows) || 
+     (c1 < 1) || (c1 > m->cols) ||
+     (c2 < c1) || (c2 < 1) || (c2 > m->cols))
+    {
+      return 0;
+    }
+
+  result = gmp_blas_inz(c2-c1+1, 
+			(const mpz_t *) &(m->storage[(c1-1)*(m->rows)+r-1]), 
+			m->rows);
+
+  if(result > c2-c1+1)
+    {
+      return 0;
+    }
+
+  return result;
+}
+
+
+int
+gmp_matrix_is_diagonal(const gmp_matrix * M)
+{
+  size_t i,j;
+  size_t rows, cols;
+
+  if(M == NULL)
+    {
+      return 0;
+    }
+  
+  rows = M->rows;
+  cols = M->cols;
+
+  for(j = 1; j <= cols; j ++)
+    {
+      for(i = 1; i <= rows; i ++)
+	{
+	  if((mpz_cmp_si(M->storage[(i-1)+(j-1)*rows], 0) != 0) && 
+	     (i != j))
+	    {
+	      return 0;
+	    }
+	}
+    }
+
+  return 1;
+}
+
+
+int
+gmp_matrix_transp(gmp_matrix * M)
+{
+  mpz_t * new_storage;
+  size_t i,j;
+  size_t rows, cols;
+
+  if(M == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  
+  rows = M->rows;
+  cols = M->cols;
+
+  new_storage = (mpz_t *) calloc(rows*cols, sizeof(mpz_t));
+  if(new_storage == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  for(i = 1; i <= rows; i++)
+    {
+      for(j = 1; j <= cols; j++)
+	{
+	  mpz_init_set(new_storage[(j-1)+(i-1)*cols], 
+		       M-> storage[(i-1)+(j-1)*rows]);
+	  mpz_clear(M-> storage[(i-1)+(j-1)*rows]);
+	}
+    }
+  free(M->storage);
+
+  M -> storage = new_storage;
+  M -> rows = cols;
+  M -> cols = rows;
+
+  return EXIT_SUCCESS;
+}
+
+
+int
+gmp_matrix_right_mult(gmp_matrix * A, const gmp_matrix * B)
+{
+  mpz_t * new_storage;
+  size_t i,j;
+  size_t rows_A, cols_A, rows_B, cols_B;
+
+  if((A == NULL) || (B == NULL))
+    {
+      return EXIT_FAILURE;
+    }
+  
+  rows_A = A->rows;
+  cols_A = A->cols;
+  rows_B = B->rows;
+  cols_B = B->cols;
+
+  if(cols_A != rows_B)
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* Create new storage for the product */
+  new_storage = (mpz_t *) calloc(rows_A*cols_B, sizeof(mpz_t));
+  if(new_storage == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* Compute the product to the storage */
+  for(j = 1; j <= cols_B; j++)
+    {
+      for(i = 1; i <= rows_A; i++)
+	{
+	  mpz_init (new_storage[(i-1)+(j-1)*rows_A]);
+	  gmp_blas_dot(&(new_storage[(i-1)+(j-1)*rows_A]),
+		       cols_A,
+		       (const mpz_t *) &(A->storage[i-1]),          rows_A,
+		       (const mpz_t *) &(B->storage[(j-1)*rows_B]), 1);
+	}
+    }
+
+  /* Get rid of the old storage */
+  for(i = 1; i <= rows_A*cols_A; i++)
+    {
+      mpz_clear (A->storage[i-1]);
+    }
+  free(A->storage);
+
+  /* Update A */
+  A -> storage = new_storage;
+  A -> cols    = cols_B;
+
+  return EXIT_SUCCESS;
+}
+
+int
+gmp_matrix_left_mult(const gmp_matrix * A, gmp_matrix * B)
+{
+  mpz_t * new_storage;
+  size_t i,j;
+  size_t rows_A, cols_A, rows_B, cols_B;
+
+  if((A == NULL) || (B == NULL))
+    {
+      return EXIT_FAILURE;
+    }
+  
+  rows_A = A->rows;
+  cols_A = A->cols;
+  rows_B = B->rows;
+  cols_B = B->cols;
+
+  if(cols_A != rows_B)
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* Create new storage for the product */
+  new_storage = (mpz_t *) calloc(rows_A*cols_B, sizeof(mpz_t));
+  if(new_storage == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* Compute the product to the storage */
+  for(j = 1; j <= cols_B; j++)
+    {
+      for(i = 1; i <= rows_A; i++)
+	{
+	  mpz_init (new_storage[(i-1)+(j-1)*rows_A]);
+	  gmp_blas_dot(&(new_storage[(i-1)+(j-1)*rows_A]),
+		       cols_A,
+		       (const mpz_t *) &(A->storage[i-1]),          rows_A,
+		       (const mpz_t *) &(B->storage[(j-1)*rows_B]), 1);
+	}
+    }
+
+  /* Get rid of the old storage */
+  for(i = 1; i <= rows_B*cols_B; i++)
+    {
+      mpz_clear (B->storage[i-1]);
+    }
+  free(B->storage);
+
+  /* Update A */
+  B -> storage = new_storage;
+  B -> rows    = rows_A;
+
+  return EXIT_SUCCESS;
+}
+
+
+int gmp_matrix_printf(const gmp_matrix * m)
+{
+
+  mpz_t        outz;
+  size_t rows, cols, i, j;
+
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  rows = m->rows;
+  cols = m->cols;
+  mpz_init(outz);
+  for(i = 1; i <= rows ; i++)
+    {
+      for(j = 1; j <= cols ; j++)
+	{
+	  gmp_matrix_get_elem(outz, i, j, m);
+	  mpz_out_str (stdout, 10, outz);
+	  printf(" ");
+	}
+      printf("\n");
+    }
+
+  mpz_clear(outz);
+
+  return EXIT_SUCCESS;
+}
+
+int gmp_matrix_fprintf(FILE* stream, const gmp_matrix * m)
+{
+  mpz_t        outz;
+  size_t rows, cols, i, j;
+
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  rows = m->rows;
+  cols = m->cols;
+  mpz_init(outz);
+  for(i = 1; i <= rows ; i++)
+    {
+      for(j = 1; j <= cols ; j++)
+	{
+	  gmp_matrix_get_elem(outz, i, j, m);
+	  mpz_out_str (stream, 10, outz);
+	  printf(" ");
+	}
+      printf("\n");
+    }
+
+  mpz_clear(outz);
+
+  return EXIT_SUCCESS;
+}
diff --git a/contrib/kbipack/gmp_matrix.c~ b/contrib/kbipack/gmp_matrix.c~
new file mode 100755
index 0000000000..2afe8d1dd2
--- /dev/null
+++ b/contrib/kbipack/gmp_matrix.c~
@@ -0,0 +1,678 @@
+/* 
+   Source file for integer-oriented matrix, relying on the arbitrary 
+   precision integers from the GNU gmp package. 
+
+   Copyright (C) 28.10.2003 Saku Suuriniemi TUT/CEM
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   Saku Suuriniemi, TUT/Electromagetics
+   P.O.Box 692, FIN-33101 Tampere, Finland
+   saku.suuriniemi@tut.fi
+
+   $Id: gmp_matrix.c~,v 1.1 2009-03-30 14:10:57 matti Exp $
+*/
+
+
+#include<stdlib.h>
+#include"gmp_matrix.h"
+
+int gmp_kaka(int laalaa){
+  printf("kissa %d", laalaa);
+  return laalaa;
+}
+
+gmp_matrix * 
+create_gmp_matrix(size_t r, size_t c, 
+		  const mpz_t * e)
+{
+  gmp_matrix * new_matrix;
+  size_t       ind;
+
+  new_matrix = (gmp_matrix * ) malloc(sizeof(gmp_matrix));
+  if(new_matrix == NULL)
+    {
+      return NULL;
+    }
+
+  new_matrix -> storage = (mpz_t *) calloc(r*c, sizeof(mpz_t));
+  if(new_matrix -> storage == NULL)
+    {
+      free(new_matrix);
+      return NULL;
+    }
+
+  new_matrix -> rows = r;
+  new_matrix -> cols = c;
+
+  for(ind = 0; ind < r*c; ind ++)
+    {
+      mpz_init (new_matrix -> storage[ind]);
+      mpz_set  (new_matrix -> storage[ind], e[ind]);
+    }
+
+  return new_matrix;
+}
+
+
+gmp_matrix * 
+create_gmp_matrix_identity(size_t dim)
+{
+  gmp_matrix * new_matrix;
+  size_t       ind;
+
+  new_matrix = (gmp_matrix * ) malloc(sizeof(gmp_matrix));
+  if(new_matrix == NULL)
+    {
+      return NULL;
+    }
+
+  new_matrix -> storage = (mpz_t *) calloc(dim*dim, sizeof(mpz_t));
+  if(new_matrix -> storage == NULL)
+    {
+      free(new_matrix);
+      return NULL;
+    }
+
+  new_matrix -> rows = dim;
+  new_matrix -> cols = dim;
+
+  for(ind = 0; ind < dim*dim; ind ++)
+    {
+      mpz_init_set_si (new_matrix -> storage[ind], 0);
+    }
+
+  for(ind = 0; ind < dim; ind ++)
+    {
+      mpz_set_ui(new_matrix -> storage[ind*(dim+1)], 1);
+    }
+
+  return new_matrix;
+}
+
+
+gmp_matrix * 
+create_gmp_matrix_zero(size_t rows, size_t cols)
+{
+  gmp_matrix * new_matrix;
+  size_t       ind;
+
+  new_matrix = (gmp_matrix * ) malloc(sizeof(gmp_matrix));
+  if(new_matrix == NULL)
+    {
+      return NULL;
+    }
+
+  new_matrix -> storage = (mpz_t *) calloc(rows*cols, sizeof(mpz_t));
+  if(new_matrix -> storage == NULL)
+    {
+      free(new_matrix);
+      return NULL;
+    }
+
+  new_matrix -> rows = rows;
+  new_matrix -> cols = cols;
+
+  for(ind = 0; ind < rows*cols; ind ++)
+    {
+      mpz_init_set_si (new_matrix -> storage[ind], 0);
+    }
+
+  return new_matrix;
+}
+
+int 
+destroy_gmp_matrix(gmp_matrix * m)
+{
+  size_t       ind, nmb_storage;;
+
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  if(m -> storage == NULL)
+    {
+      free(m);
+      return EXIT_FAILURE;
+    }
+
+  nmb_storage = (m -> rows)*(m -> cols);
+
+  for(ind = 0; ind < nmb_storage; ind++)
+    {
+      mpz_clear(m -> storage[ind]);
+    }
+
+  free(m -> storage);
+  free(m);
+  return EXIT_SUCCESS;
+}
+
+
+size_t
+gmp_matrix_rows(const gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return 0;
+    }
+
+  return m -> rows;
+}
+
+
+size_t
+gmp_matrix_cols(const gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return 0;
+    }
+
+  return m -> cols;
+}
+
+/* elem <- (matrix(row, col)) */
+int
+gmp_matrix_get_elem(mpz_t elem, size_t row, size_t col,
+		    const gmp_matrix * m)
+{
+#ifdef DEBUG
+
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+#endif
+
+  mpz_set(elem, m -> storage[(col-1)*(m -> rows)+row-1]);
+  return EXIT_SUCCESS;
+}
+
+int  
+gmp_matrix_swap_rows(size_t row1, size_t row2, gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((row1 < 1) || (row1 > m->rows) || (row2 < 1) || (row2 > m->rows))
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* printf("Swapping rows %i %i\n", row1, row2); */
+  gmp_blas_swap(m -> cols,
+		&(m -> storage[row1-1]), m -> rows, 
+		&(m -> storage[row2-1]), m -> rows);
+
+  return EXIT_SUCCESS;
+}
+
+int
+gmp_matrix_swap_cols(size_t col1, size_t col2, gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((col1 < 1) || (col1 > m->cols) || (col2 < 1) || (col2 > m->cols))
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* printf("Swapping cols %i %i\n", col1, col2); */
+  gmp_blas_swap(m -> rows, 
+		&(m -> storage[(m->rows)*(col1-1)]), 1, 
+		&(m -> storage[(m->rows)*(col2-1)]), 1);
+
+  return EXIT_SUCCESS;
+}
+
+int
+gmp_matrix_negate_row(size_t row, gmp_matrix * m)
+{
+  mpz_t minus_one;
+
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((row < 1) || (row > m->rows))
+    {
+      return EXIT_FAILURE;
+    }
+
+  mpz_init(minus_one);
+  mpz_set_si(minus_one, -1);
+  gmp_blas_scal(m -> cols, minus_one, (&m -> storage[row-1]), m -> rows); 
+  mpz_clear(minus_one);
+  return EXIT_SUCCESS; 
+}
+
+int
+gmp_matrix_negate_col(size_t col, gmp_matrix * m)
+{
+  mpz_t minus_one;
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((col < 1) || (col > m->cols))
+    {
+      return EXIT_FAILURE;
+    }
+
+  mpz_init(minus_one);
+  mpz_set_si(minus_one, -1);
+  gmp_blas_scal(m -> rows, minus_one, 
+		&(m -> storage[(m->rows)*(col-1)]), 1); 
+  mpz_clear(minus_one);
+  return EXIT_SUCCESS;
+}
+
+/* row2 <- a*row1 + row2*/
+int
+gmp_matrix_add_row(mpz_t a, size_t row1, size_t row2,
+		   gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((row1 < 1) || (row1 > m->rows) || (row2 < 1) || (row2 > m->rows))
+    {
+      return EXIT_FAILURE;
+    }
+
+  gmp_blas_axpy(m->cols, a, 
+		(const mpz_t *) &(m->storage[row1-1]), m->rows, 
+		&(m->storage[row2-1]), m->rows); 
+
+  return EXIT_SUCCESS;
+}
+
+int
+gmp_matrix_add_col(mpz_t a, size_t col1, size_t col2,
+		   gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((col1 < 1) || (col1 > m->cols) || (col2 < 1) || (col2 > m->cols))
+    {
+      return EXIT_FAILURE;
+    }
+
+  gmp_blas_axpy(m->rows, a, 
+		(const mpz_t *) &(m -> storage[(m->rows)*(col1-1)]), 1, 
+		&(m -> storage[(m->rows)*(col2-1)]), 1); 
+
+  return EXIT_SUCCESS;
+}
+
+/* row1 <- a*row1 + b*row2
+   row2 <- c*row1 + d*row2 */
+int
+gmp_matrix_row_rot(mpz_t a, mpz_t b, size_t row1,
+		   mpz_t c, mpz_t d, size_t row2,
+		   gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((row1 < 1) || (row1 > m->rows) || (row2 < 1) || (row2 > m->rows))
+    {
+      return EXIT_FAILURE;
+    }
+
+  gmp_blas_rot(m->cols, 
+	       a, b, &(m->storage[row1-1]), m->rows, 
+	       c, d, &(m->storage[row2-1]), m->rows); 
+
+  return EXIT_SUCCESS;
+}
+int
+gmp_matrix_col_rot(mpz_t a, mpz_t b, size_t col1,
+		   mpz_t c, mpz_t d, size_t col2,
+		   gmp_matrix * m)
+{
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  if((col1 < 1) || (col1 > m->cols) || (col2 < 1) || (col2 > m->cols))
+    {
+      return EXIT_FAILURE;
+    }
+
+/*   printf("a: %i b: %i c: %i d:%i col1: %i col2: %i\n", */
+/* 	 mpz_get_si(a), */
+/* 	 mpz_get_si(b), */
+/* 	 mpz_get_si(c), */
+/* 	 mpz_get_si(d), */
+/* 	 col1, col2); */
+
+  gmp_blas_rot(m->rows, 
+	       a, b, &(m -> storage[(m->rows)*(col1-1)]), 1, 
+	       c, d, &(m -> storage[(m->rows)*(col2-1)]), 1); 
+
+  return EXIT_SUCCESS;
+}
+
+size_t
+gmp_matrix_col_inz (size_t r1, size_t r2, size_t c, 
+		    gmp_matrix * m)
+{
+  size_t result;
+
+  if(m == NULL)
+    {
+      return 0;
+    }
+  if((r1 < 1) || (r1 > m->rows) || 
+     (r2 < 1) || (r2 > m->rows) ||
+     (r2 < r1) || (c < 1) || (c > m->cols))
+    {
+      return 0;
+    }
+
+  result = gmp_blas_inz(r2-r1+1, 
+			(const mpz_t *) &(m->storage[(c-1)*(m->rows)+r1-1]), 
+			1);
+
+  if(result > r2-r1+1)
+    {
+      return 0;
+    }
+
+  return result;
+}
+
+size_t
+gmp_matrix_row_inz (size_t r, size_t c1, size_t c2, 
+		    gmp_matrix * m)
+{
+  size_t result;
+
+  if(m == NULL)
+    {
+      return 0;
+    }
+  if((r  < 1) || (r  > m->rows) || 
+     (c1 < 1) || (c1 > m->cols) ||
+     (c2 < c1) || (c2 < 1) || (c2 > m->cols))
+    {
+      return 0;
+    }
+
+  result = gmp_blas_inz(c2-c1+1, 
+			(const mpz_t *) &(m->storage[(c1-1)*(m->rows)+r-1]), 
+			m->rows);
+
+  if(result > c2-c1+1)
+    {
+      return 0;
+    }
+
+  return result;
+}
+
+
+int
+gmp_matrix_is_diagonal(const gmp_matrix * M)
+{
+  size_t i,j;
+  size_t rows, cols;
+
+  if(M == NULL)
+    {
+      return 0;
+    }
+  
+  rows = M->rows;
+  cols = M->cols;
+
+  for(j = 1; j <= cols; j ++)
+    {
+      for(i = 1; i <= rows; i ++)
+	{
+	  if((mpz_cmp_si(M->storage[(i-1)+(j-1)*rows], 0) != 0) && 
+	     (i != j))
+	    {
+	      return 0;
+	    }
+	}
+    }
+
+  return 1;
+}
+
+
+int
+gmp_matrix_transp(gmp_matrix * M)
+{
+  mpz_t * new_storage;
+  size_t i,j;
+  size_t rows, cols;
+
+  if(M == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+  
+  rows = M->rows;
+  cols = M->cols;
+
+  new_storage = (mpz_t *) calloc(rows*cols, sizeof(mpz_t));
+  if(new_storage == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  for(i = 1; i <= rows; i++)
+    {
+      for(j = 1; j <= cols; j++)
+	{
+	  mpz_init_set(new_storage[(j-1)+(i-1)*cols], 
+		       M-> storage[(i-1)+(j-1)*rows]);
+	  mpz_clear(M-> storage[(i-1)+(j-1)*rows]);
+	}
+    }
+  free(M->storage);
+
+  M -> storage = new_storage;
+  M -> rows = cols;
+  M -> cols = rows;
+
+  return EXIT_SUCCESS;
+}
+
+
+int
+gmp_matrix_right_mult(gmp_matrix * A, const gmp_matrix * B)
+{
+  mpz_t * new_storage;
+  size_t i,j;
+  size_t rows_A, cols_A, rows_B, cols_B;
+
+  if((A == NULL) || (B == NULL))
+    {
+      return EXIT_FAILURE;
+    }
+  
+  rows_A = A->rows;
+  cols_A = A->cols;
+  rows_B = B->rows;
+  cols_B = B->cols;
+
+  if(cols_A != rows_B)
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* Create new storage for the product */
+  new_storage = (mpz_t *) calloc(rows_A*cols_B, sizeof(mpz_t));
+  if(new_storage == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* Compute the product to the storage */
+  for(j = 1; j <= cols_B; j++)
+    {
+      for(i = 1; i <= rows_A; i++)
+	{
+	  mpz_init (new_storage[(i-1)+(j-1)*rows_A]);
+	  gmp_blas_dot(&(new_storage[(i-1)+(j-1)*rows_A]),
+		       cols_A,
+		       (const mpz_t *) &(A->storage[i-1]),          rows_A,
+		       (const mpz_t *) &(B->storage[(j-1)*rows_B]), 1);
+	}
+    }
+
+  /* Get rid of the old storage */
+  for(i = 1; i <= rows_A*cols_A; i++)
+    {
+      mpz_clear (A->storage[i-1]);
+    }
+  free(A->storage);
+
+  /* Update A */
+  A -> storage = new_storage;
+  A -> cols    = cols_B;
+
+  return EXIT_SUCCESS;
+}
+
+int
+gmp_matrix_left_mult(const gmp_matrix * A, gmp_matrix * B)
+{
+  mpz_t * new_storage;
+  size_t i,j;
+  size_t rows_A, cols_A, rows_B, cols_B;
+
+  if((A == NULL) || (B == NULL))
+    {
+      return EXIT_FAILURE;
+    }
+  
+  rows_A = A->rows;
+  cols_A = A->cols;
+  rows_B = B->rows;
+  cols_B = B->cols;
+
+  if(cols_A != rows_B)
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* Create new storage for the product */
+  new_storage = (mpz_t *) calloc(rows_A*cols_B, sizeof(mpz_t));
+  if(new_storage == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* Compute the product to the storage */
+  for(j = 1; j <= cols_B; j++)
+    {
+      for(i = 1; i <= rows_A; i++)
+	{
+	  mpz_init (new_storage[(i-1)+(j-1)*rows_A]);
+	  gmp_blas_dot(&(new_storage[(i-1)+(j-1)*rows_A]),
+		       cols_A,
+		       (const mpz_t *) &(A->storage[i-1]),          rows_A,
+		       (const mpz_t *) &(B->storage[(j-1)*rows_B]), 1);
+	}
+    }
+
+  /* Get rid of the old storage */
+  for(i = 1; i <= rows_B*cols_B; i++)
+    {
+      mpz_clear (B->storage[i-1]);
+    }
+  free(B->storage);
+
+  /* Update A */
+  B -> storage = new_storage;
+  B -> rows    = rows_A;
+
+  return EXIT_SUCCESS;
+}
+
+
+int gmp_matrix_printf(const gmp_matrix * m)
+{
+
+  mpz_t        outz;
+  size_t rows, cols, i, j;
+
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  rows = m->rows;
+  cols = m->cols;
+  mpz_init(outz);
+  for(i = 1; i <= rows ; i++)
+    {
+      for(j = 1; j <= cols ; j++)
+	{
+	  gmp_matrix_get_elem(outz, i, j, m);
+	  mpz_out_str (stdout, 10, outz);
+	  printf(" ");
+	}
+      printf("\n");
+    }
+
+  mpz_clear(outz);
+
+  return EXIT_SUCCESS;
+}
+
+int gmp_matrix_fprintf(FILE* stream, const gmp_matrix * m)
+{
+  mpz_t        outz;
+  size_t rows, cols, i, j;
+
+  if(m == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  rows = m->rows;
+  cols = m->cols;
+  mpz_init(outz);
+  for(i = 1; i <= rows ; i++)
+    {
+      for(j = 1; j <= cols ; j++)
+	{
+	  gmp_matrix_get_elem(outz, i, j, m);
+	  mpz_out_str (stream, 10, outz);
+	  printf(" ");
+	}
+      printf("\n");
+    }
+
+  mpz_clear(outz);
+
+  return EXIT_SUCCESS;
+}
diff --git a/contrib/kbipack/gmp_matrix.h b/contrib/kbipack/gmp_matrix.h
new file mode 100755
index 0000000000..6e32583db3
--- /dev/null
+++ b/contrib/kbipack/gmp_matrix.h
@@ -0,0 +1,142 @@
+/* 
+   Header file for integer-oriented matrix, relying on the arbitrary 
+   precision integers from the GNU gmp package. 
+
+   Copyright (C) 28.10.2003 Saku Suuriniemi TUT/CEM
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   Saku Suuriniemi, TUT/Electromagetics
+   P.O.Box 692, FIN-33101 Tampere, Finland
+   saku.suuriniemi@tut.fi
+
+   $Id: gmp_matrix.h,v 1.1 2009-03-30 14:10:57 matti Exp $
+*/
+
+#ifndef __GMP_MATRIX_H__
+#define __GMP_MATRIX_H__
+
+#include"gmp_blas.h"
+
+typedef struct
+{
+  size_t  rows;
+  size_t  cols;
+  mpz_t * storage;
+} gmp_matrix;
+
+/* Sets the values of "elems" column by column. The user is 
+   responsible for sufficient supply. */
+gmp_matrix * 
+create_gmp_matrix(size_t rows, size_t cols, 
+		  const mpz_t * elems);
+
+gmp_matrix * 
+create_gmp_matrix_identity(size_t dim);
+
+gmp_matrix * 
+create_gmp_matrix_zero(size_t rows, size_t cols);
+
+int 
+destroy_gmp_matrix(gmp_matrix *);
+
+size_t
+gmp_matrix_rows(const gmp_matrix *);
+
+size_t
+gmp_matrix_cols(const gmp_matrix *);
+
+/* elem <- (matrix(row, col)) */
+int
+gmp_matrix_get_elem(mpz_t elem, size_t row, size_t col,
+		    const gmp_matrix *);
+
+int
+gmp_matrix_swap_rows(size_t row1, size_t row2, gmp_matrix *);
+
+int
+gmp_matrix_swap_cols(size_t col1, size_t col2, gmp_matrix *);
+
+int  
+gmp_matrix_negate_row(size_t row, gmp_matrix *);
+
+int 
+gmp_matrix_negate_col(size_t col, gmp_matrix *); 
+
+/* row2 <- a*row1 + row2*/
+int
+gmp_matrix_add_row(mpz_t a, size_t row1, size_t row2,
+		   gmp_matrix *);
+int
+gmp_matrix_add_col(mpz_t a, size_t col1, size_t col2,
+		   gmp_matrix *);
+
+/* row1 <- a*row1 + b*row2
+   row2 <- c*row1 + d*row2 */
+int
+gmp_matrix_row_rot(mpz_t a, mpz_t b, size_t row1,
+		   mpz_t c, mpz_t d, size_t row2,
+		   gmp_matrix *);
+int
+gmp_matrix_col_rot(mpz_t a, mpz_t b, size_t col1,
+		   mpz_t c, mpz_t d, size_t col2,
+		   gmp_matrix *);
+
+/* 0 for no, 1 for yes */
+int
+gmp_matrix_is_diagonal(const gmp_matrix * M);
+
+/* Finds a nonzero in a subcolumn M(r1:r2,c). */
+/* Returns zero if no nonzeros found. */
+size_t
+gmp_matrix_col_inz (size_t r1, size_t r2, size_t c, 
+		    gmp_matrix * M);
+
+/* Finds a nonzero in a subrow M(r,c1:c2). */
+/* Returns zero if no nonzeros found. */
+size_t
+gmp_matrix_row_inz (size_t r, size_t c1, size_t c2, 
+		    gmp_matrix * M);
+
+int
+gmp_matrix_transp(gmp_matrix * M);
+
+/* A <- A*B */
+int
+gmp_matrix_right_mult(gmp_matrix * A, const gmp_matrix * B);
+
+/* B <- A*B */
+int
+gmp_matrix_left_mult(const gmp_matrix * A, gmp_matrix * B);
+
+/* (TBD ?)Implement the BLAS style GEMM? Place it here at all? */
+/*         (T)   (T) */
+/*   A <- B   * C    */
+/* Give 1 if the transpose of the matrix is to be used, else 0 */
+/* int */
+/* gmp_matrix_mult(gmp_matrix * A,  */
+/* 		const gmp_matrix * B, int transpose_B,  */
+/* 		const gmp_matrix * C, int transpose_C);  */
+
+/* 
+   Mainly for diagnostics ...        
+   --------------------------
+*/
+
+int gmp_matrix_printf(const gmp_matrix *);
+int gmp_matrix_fprintf(FILE*, const gmp_matrix *);
+
+
+#endif
diff --git a/contrib/kbipack/gmp_matrix.h~ b/contrib/kbipack/gmp_matrix.h~
new file mode 100755
index 0000000000..217b5fe7e1
--- /dev/null
+++ b/contrib/kbipack/gmp_matrix.h~
@@ -0,0 +1,144 @@
+/* 
+   Header file for integer-oriented matrix, relying on the arbitrary 
+   precision integers from the GNU gmp package. 
+
+   Copyright (C) 28.10.2003 Saku Suuriniemi TUT/CEM
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   Saku Suuriniemi, TUT/Electromagetics
+   P.O.Box 692, FIN-33101 Tampere, Finland
+   saku.suuriniemi@tut.fi
+
+   $Id: gmp_matrix.h~,v 1.1 2009-03-30 14:10:57 matti Exp $
+*/
+
+#ifndef __GMP_MATRIX_H__
+#define __GMP_MATRIX_H__
+
+#include"gmp_blas.h"
+
+typedef struct
+{
+  size_t  rows;
+  size_t  cols;
+  mpz_t * storage;
+} gmp_matrix;
+
+int gmp_kaka(int laalaa);
+
+/* Sets the values of "elems" column by column. The user is 
+   responsible for sufficient supply. */
+gmp_matrix * 
+create_gmp_matrix(size_t rows, size_t cols, 
+		  const mpz_t * elems);
+
+gmp_matrix * 
+create_gmp_matrix_identity(size_t dim);
+
+gmp_matrix * 
+create_gmp_matrix_zero(size_t rows, size_t cols);
+
+int 
+destroy_gmp_matrix(gmp_matrix *);
+
+size_t
+gmp_matrix_rows(const gmp_matrix *);
+
+size_t
+gmp_matrix_cols(const gmp_matrix *);
+
+/* elem <- (matrix(row, col)) */
+int
+gmp_matrix_get_elem(mpz_t elem, size_t row, size_t col,
+		    const gmp_matrix *);
+
+int
+gmp_matrix_swap_rows(size_t row1, size_t row2, gmp_matrix *);
+
+int
+gmp_matrix_swap_cols(size_t col1, size_t col2, gmp_matrix *);
+
+int  
+gmp_matrix_negate_row(size_t row, gmp_matrix *);
+
+int 
+gmp_matrix_negate_col(size_t col, gmp_matrix *); 
+
+/* row2 <- a*row1 + row2*/
+int
+gmp_matrix_add_row(mpz_t a, size_t row1, size_t row2,
+		   gmp_matrix *);
+int
+gmp_matrix_add_col(mpz_t a, size_t col1, size_t col2,
+		   gmp_matrix *);
+
+/* row1 <- a*row1 + b*row2
+   row2 <- c*row1 + d*row2 */
+int
+gmp_matrix_row_rot(mpz_t a, mpz_t b, size_t row1,
+		   mpz_t c, mpz_t d, size_t row2,
+		   gmp_matrix *);
+int
+gmp_matrix_col_rot(mpz_t a, mpz_t b, size_t col1,
+		   mpz_t c, mpz_t d, size_t col2,
+		   gmp_matrix *);
+
+/* 0 for no, 1 for yes */
+int
+gmp_matrix_is_diagonal(const gmp_matrix * M);
+
+/* Finds a nonzero in a subcolumn M(r1:r2,c). */
+/* Returns zero if no nonzeros found. */
+size_t
+gmp_matrix_col_inz (size_t r1, size_t r2, size_t c, 
+		    gmp_matrix * M);
+
+/* Finds a nonzero in a subrow M(r,c1:c2). */
+/* Returns zero if no nonzeros found. */
+size_t
+gmp_matrix_row_inz (size_t r, size_t c1, size_t c2, 
+		    gmp_matrix * M);
+
+int
+gmp_matrix_transp(gmp_matrix * M);
+
+/* A <- A*B */
+int
+gmp_matrix_right_mult(gmp_matrix * A, const gmp_matrix * B);
+
+/* B <- A*B */
+int
+gmp_matrix_left_mult(const gmp_matrix * A, gmp_matrix * B);
+
+/* (TBD ?)Implement the BLAS style GEMM? Place it here at all? */
+/*         (T)   (T) */
+/*   A <- B   * C    */
+/* Give 1 if the transpose of the matrix is to be used, else 0 */
+/* int */
+/* gmp_matrix_mult(gmp_matrix * A,  */
+/* 		const gmp_matrix * B, int transpose_B,  */
+/* 		const gmp_matrix * C, int transpose_C);  */
+
+/* 
+   Mainly for diagnostics ...        
+   --------------------------
+*/
+
+int gmp_matrix_printf(const gmp_matrix *);
+int gmp_matrix_fprintf(FILE*, const gmp_matrix *);
+
+
+#endif
diff --git a/contrib/kbipack/gmp_matrix.o b/contrib/kbipack/gmp_matrix.o
new file mode 100755
index 0000000000000000000000000000000000000000..b151210c86fa837e700afce35afff9326bbec789
GIT binary patch
literal 20660
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

literal 0
HcmV?d00001

diff --git a/contrib/kbipack/gmp_matrix_io.c b/contrib/kbipack/gmp_matrix_io.c
new file mode 100755
index 0000000000..f85639304a
--- /dev/null
+++ b/contrib/kbipack/gmp_matrix_io.c
@@ -0,0 +1,135 @@
+/*
+  IO-routines for gmp integer matrices in coordinate format.
+
+  Copyright (C) 19.11.2003 Saku Suuriniemi TUT/CEM
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  any later version.
+   
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  Saku Suuriniemi, TUT/Electromagetics
+  P.O.Box 692, FIN-33101 Tampere, Finland
+  saku.suuriniemi@tut.fi
+
+  $Id: gmp_matrix_io.c,v 1.1 2009-03-30 14:10:57 matti Exp $
+ */
+
+#include<stdio.h>
+#include<stdlib.h>
+#include<string.h>
+#include "gmp_matrix_io.h"
+
+gmp_matrix * gmp_matrix_read_coord(char* filename)
+{
+  FILE *  p_file;
+  size_t  read_values;
+  size_t  row, col, nrows, ncols, dummy;
+  int     val;
+  char    buffer[1000];
+  gmp_matrix * new_matrix;
+  
+  p_file = fopen(filename, "r");
+  if(p_file == NULL)
+    return NULL;
+
+  /* Matlab and Octave typically include comments on ascii files. */
+  /* They start with # */
+  fgets(buffer, 999, p_file);
+  while(strncmp(buffer, "#",1) == 0)
+    {
+      fgets(buffer, 999, p_file);
+    }
+
+  /* First read the size of the matrix */
+  read_values = sscanf(buffer, "%u %u %u", &nrows, &ncols, &dummy);
+
+  /* Create the matrix */
+  new_matrix = create_gmp_matrix_zero(nrows, ncols);
+  if(new_matrix == NULL)
+    {
+      fclose(p_file);
+      return NULL;
+    }
+
+  /* Read the values to the matrix */
+  while(read_values != EOF)
+    {
+      read_values = fscanf(p_file, "%u %u %i\n", &row, &col, &val);
+      if( (row <= nrows) && (row > 0) && (col <= ncols) && (col > 0) )
+	{
+	  mpz_set_si ( new_matrix->storage[(row-1)+(col-1)*nrows], val );
+	}
+    }
+  fclose(p_file);
+
+  return new_matrix;
+}
+
+int gmp_matrix_write_coord(char* filename, const gmp_matrix * M)
+{
+
+  FILE *  p_file;
+  size_t  written_values;
+  size_t  row, col, nrows, ncols, nnz;
+  mpz_t   outz;
+
+  if(M == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  nrows = M->rows;
+  ncols = M->cols;
+
+  p_file = fopen(filename, "w");
+  if(p_file == NULL)
+    return 0;
+
+  nnz = 0;
+  /* Compute the number of nonzeros for coordinate format */
+  mpz_init(outz);
+  for(col = 1; col <= ncols ; col++)
+    {
+      for(row = 1; row <= nrows ; row++)
+	{
+	  gmp_matrix_get_elem(outz, row, col, M);
+	  if( mpz_cmp_si (outz, 0) != 0)
+	    {
+	      nnz++;
+	    }
+	}
+    }
+
+  /* First write the size of the matrix */
+  written_values = fprintf(p_file, "%u %u %u\n", nrows, ncols, nnz);
+
+  /* Write the values to the matrix */
+  for(col = 1; col <= ncols ; col++)
+    {
+      for(row = 1; row <= nrows ; row++)
+	{
+	  gmp_matrix_get_elem(outz, row, col, M);
+	  if( mpz_cmp_si (outz, 0) != 0)
+	    {
+	      fprintf(p_file, "%u %u ", row, col);
+	      mpz_out_str (p_file, 10, outz);
+	      fprintf(p_file, "\n");
+	    }
+	}
+    }
+  mpz_clear(outz);  
+
+  fclose(p_file);
+
+  return EXIT_SUCCESS;
+}
diff --git a/contrib/kbipack/gmp_matrix_io.h b/contrib/kbipack/gmp_matrix_io.h
new file mode 100755
index 0000000000..74df3f2ad1
--- /dev/null
+++ b/contrib/kbipack/gmp_matrix_io.h
@@ -0,0 +1,37 @@
+/*
+  IO-routines for gmp integer matrices in coordinate format.
+
+  Copyright (C) 19.11.2003 Saku Suuriniemi TUT/CEM
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  any later version.
+   
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  Saku Suuriniemi, TUT/Electromagetics
+  P.O.Box 692, FIN-33101 Tampere, Finland
+  saku.suuriniemi@tut.fi
+
+  $Id: gmp_matrix_io.h,v 1.1 2009-03-30 14:10:57 matti Exp $
+ */
+
+#ifndef _GMP_MATRIX_IO_
+#define _GMP_MATRIX_IO_
+
+
+#include "gmp_matrix.h"
+
+gmp_matrix * gmp_matrix_read_coord(char* filename);
+int          gmp_matrix_write_coord(char* filename, const gmp_matrix * M);
+
+
+#endif
diff --git a/contrib/kbipack/gmp_matrix_io.o b/contrib/kbipack/gmp_matrix_io.o
new file mode 100755
index 0000000000000000000000000000000000000000..c7231192ab4007c70d68c083fb81cab8a3fddd0f
GIT binary patch
literal 8060
zcmb7JeQ;dWb-!=lTWQz3mSjuvm;AA|Wn&1^TC$<G5ny8rTfsm8e*uQ|O8ZvYct6DM
zKFfCatfXSiqK+Ltno00vOv6l}q=bZI5=cNP3?2^zC_@S@Ok&cekl?gV0&ZHM>hE{o
zdD>?u{iApE-tYd-IrrRi&%N)yyH9s@Z@tnm4CNI=nX1Z^iZ;2L)f!f)xoVbYs&V`G
zX+8GKojYzAKGjvZBRV=+KXM;rmF{Rojdj0ETyGuU)N0Xwtovl8J2Ymse(>q!<m6cQ
z+ZE^Sj)~DT^&`8`X1x1@jx$g`PoLVNa?8olcj`wrQu#ePs9gQ-v(%2BD7TH5m$RF(
z-L2Nx?X97)O|4OQ@SG3dampC}+2rsG#@bunExk2-+*o_Z$<p(c?vrEX)~4s^1W|<o
zE?VU9&`{}qmyRmkZ+ngoyZuPN6He2w4#9T(>E*a9*M0E-<0vBr#&UKXmF=fS-zdLD
zhr3&&u!mg(dyb+x^P+o}j4*zvU`)+4Sa?xTWVLDuG&(sP*S*6p8Ec)B^}{zKpT5!i
z;_sLkezEK3TNAgufU&`?o>5=};|J;#$e7uyKb{Yt_;C7Rf7!05`kqr4Vmj?(bT!s}
zx?=*9%BGFJfRB%!{A6;HeT+_w9j_m$h2Qa`A<R2RNbC1aw4U!Sh5GM}QnfDFj0R5Z
zFemhnZA@2Res6sABRGenhVjuqxV7l`=%=jBZ}t4ohAW+@u@@?yl~r-AB;N>4hgnWx
zV#<m1;qeDg)5$hERtRYPL5mS@bC(w*ioxqxmm76OYb!_D@!EeLdueD6rn;#z&X$*q
zGq9nW(GyN*`Y@)+`Rl1>+fUo8nwh+c?%i2`&x=f`o+Mf4qdE@sH@;!A{>c}N*DDV)
zpX1j0aclG=OtyK&)N}OVF+EIUhOw-!F=E!W)Xgwv8Vxl{1!gI;Ze8S(NDrpQY*mrS
zWzZ{SNj0QY(5)&}QxCOLwFH`n8V$295@BltG(6pP73^tTgJn3)^Q=@vG-yRl)G&G)
z*<$)))zcV<%ev*LC{^ERRNXbS8<$7j*v^agG{$rvGlITwXU;)_8X*aJ186$C4F|ct
zhCMY`+XXP{S=C0QvsX`f4IsWbRjSln5b;mbc?*n=#_ct`A~))IT7s$r?&nvfN-d~1
zXN!eC2XMZKrVPw;5%UX_TC8&tRBB1p&E?T9?gh>8#@z_BV|<3YaRRpqSEBC2v%(UX
zkDK|AD|xqSm*bf-jZhp$W}x<qWLE7aEv)$>O#;D7p|h-DCSS%tgS#mV)pi2{|Ad>h
zCdn!;Wnsayeef71Ut_bsET9^+YHO@o>k%rAn!6wm?8PnE@iZi%nW#PxY%2r8k3&$?
zVB7=<ELCBwEmhNOJglh|et>kA&`|hMw5XXaG#dUq##b{(XhV1h#!xd?XjAyVNaqQi
z6JA4CEkfsqpCX+vG#0*t#tVhEhA*P$MM77Ezr%iGLN5$2CtYHE8xUB!GR)nlrq%eq
zrmMsINSDiq)`TCW=L>{(g?~lug~mZZVCnksk4RS<mub2wd>uV^7+W>HGQ5=9PUAW4
zd2{&dbak=us-|1QA5(j|ai2Ec8a~6eTa2%2x-I+}X^+rdVT&XBywKg@2<cTq_k?GX
zZZ($awyE$p+0iu;^FVkJwO=sa)ct1Fv|74;I@AoSXw5TBL*zk<o^>;#A}O~2p_>pZ
z(n8&YJHsJWYkdj4*5XQtK7t#EGyShnp_FSvOaIlAo(Ji#LFyiXL0~g#wSUj0SRbu@
zxeb9uDipmU3n=msx~_fIWmY6c-9OMQu#{c@2YBFLX!6E?vs%yU+da@nSK)ReE(xVR
z8d}2VC(?$#LQCDus7N<Np;nh!kriaiXy&@VjI}**99m9S4aO??4<vOW&Et^FxP*c|
zx_X)c?BO_PBC^NOO@r|rs?X?!W*XSs8xx6=3Vh7R0o6RJ9GUCoV|r0d#w%!JT&w#4
zSMPzcyo2V=r>VP!%`e0?>uM0nQy{Z{ftzt1odld+a|RKIaXkk%w^$fb#tp2FkoFa_
zLODsZ=c0kD4LG}JcS7W`J#+K<gTh{>=}MYz(@8n9XWmhUi((XCL<lX<fVMnI*?ekZ
z)ZFfqA}8;`uI|fFHW_zPz1Vm^s1(P<lz#*fxjV=h2N{EEfgaZqj%z*WoC(|vJ%0=J
z!1Uyq&dC0SdSF8D-tP`f=%+*O$b|l3Xm*%xTzcfx(87I5BxM;V!*os$%^qR)mj4FP
zm`C8MLgVuooavk#TJ#gd(hiF)w2B{xxaDJrT7J#j&v8>Rn(Tr)OOwuX)yM_YpdXC?
zLG#6&iT6#lgaZy9;e%!vOMXD|WtXT?kbrs<c82ju-E`dbft`WHol-HL$@gWQlpXKQ
zluJrWvYFm^e_vlbvwF?NZP`rT+27WmcjBwtSG9L~=B}l*m1c#$WOhog`g4PcT(Vru
z>`!D0?S1H9mCC71p|gEJp>xzzb{chrTs|;pm!`z99>DJig)qD-d_jgqP<0BfOAzgm
z-B*!j^T4jLr-ldazU-}^T-tW&&KpBb>uwqe-Cwxd3fyHGW^>@r3@c#n?P7gbXkX|E
z*g%=A?8#d~PlS$`2SV*T%<0ek-OCW){O2%u{;t4A<C&*J@gKw?`l*qI?5R6Lhe2L5
ze&>>|ZwFR3hn%+li}nTD8-E%Pjqe#X4+fSRf%u$25NRJ$%bb}0n<h0?R7<CXUL}Kb
z01wwVq)i%l5<*eSI)sGH*TZ#he6%;zg+>FLrw6Yb4*rF8uQ{dXQ}M8;zhZi@NAzq7
zG4hQe1NnkQJw&9xfO1%!Dp#WxP)8JzuP*9|d?ip${8R1by6r5Y=y^WTg{O+g(9Tdd
zh(z(p;k(aJI|+|#?GD1<>grCf9~gLTuAgeNV;n}(rfRsuGh{A)i;H6>{@$&Jh<da3
zjhVK5HaRxpxq80qSKu<#7p}+Zq}mtKMsx`!%3K`8xMNqBYGSCEDcgy@LZO&a|8};j
zXeU#mVPj6R>jq|0*ppXySor{#H`WhPKkX@j=VK5naq)F?EiSJl5`}a+QC5kbYZJQ5
z!E#Y0`UaAjyeef5+EkSW3dM4-lUCRrODc~|v83%GO7td6w#pXr{V{!W@}*3F-cH4M
zFszEnL;=_6Af3tD`GP+czVS}QV&_w?6ShyJv&sHy$J8bIoFa0Rb#i&XE4`~E61O?X
zU|9|7&Qzk1O>wLx8?FR5U$Z^7F{ZaKm2z^q11dLoP?C{L7Wdjky))(2S;I;X77<Tc
z;l*1<)HyfAvrISwcW!k$OH7{5V(gF1O38h;o5j36B>SRDV2fn-RU%vH+bcuwgt(Bh
zRl48ChKy~p;FOh48(uYzUGk^EzpxVUc95|;ePyK+pUlDYRERp0VC+f;e`U|sZj~4;
zAl--riFUJ5swP5DqdO!{4y>}7TrZpND<^xiwwL1SVprNJ11sD4goLc!AUkQKnq4Kc
zh(aau_I`MxkBe7JNP3`pwJ~y?H3lBf*{Mu2zG=g@4e@No?ziLpxza%A%9U$%CEi!a
z<4w~W-`krROfr>;bdIhElc^3DkXEu)F06LpB9+&RJ>le?lAY>Ax(mf*zpaq-gYM#o
zH)qhDxv3|@O}ma#ZQi(XU2MhnUMF96+%?wL;b_*;dA=+<)m}P~L%x9JqKgAwt!QW4
z%gjT2Dp^jd_TEwnRlC>ePb7PLi}pU%P7_y>$)^i`j+e6yUSL!D?r87utJ6g;0cp#O
z`&<n=y?JQSS=D{An9TQ6<o0$pgL~E9SIFgTEQI!ap=`I~!D=g&neYEcSp-||OxPxM
zS&zGai_;&LAZx)D#^se$^W3d(HUw<X4U#XcWx~B(@)fTFIg7WEehziod+Ptojz5ll
zJEx+Z{-g*wi>JH7SEsy!;~4jTuXuL+Mdu&Kn;6&td~zH#_r}4On%4$Z^^FL=6!`>R
z^&05>{<@G*j*I??p1)gBXB!^OS=y#6b%9&ecj|cECa8tD?SkDlj{v>=<xuzcy9xcq
zAY;D>b*f)|KnM5A6YzH{JCfp6$HBciRnzK~+AKB%PdA3Tzu%N^y!aM1)$cK18&uVG
z@E2o6is$brxZhtE{<!8i4x*R8Z=vqBK~>!bfBbad6;JmZ1gvx2usjAk{^s)vb-Ydp
zdh!514RP&y#q;+D1YVuG^I+#Mo^A$&zHYSLFMNic9nn2fxXO<(&;|(jeT+UhUXGhN
z@%yvzy!{d~uXz4E?xA09sozd|a^l(l4IuaTK<=$Bx0R{z5c!HZwM%$<uJCFx6%Nb3
zV(OJx6_^T(&sR*v6;myy9=V^-tP<2rb$0Si#ZNrdVyY&cLrQqoor#%SQB`a2?dR}y
z=WssHUg13K_6U&Nzqyk0dG!i8pEs|NkK*!v12CSic}gJl$Ijt@a}Iz09RAWdoWCT!
zLVy1Ah(3~4&jM#1eA9(NJCD~c)CUupe5UMgh_g<@&e}QX<=lnCDPQB-8gFABY*c#C
zDdX7S3j4A)j+p7bYyn48U!T4crW~P4eaU=U^+ADCxX<#ST`Z`ys}-j>HsVBvF7#`S
zF(lH-OqRps`@_KUTMG<encPgo@_2~Yq|}c^|8sIIlZ99hl=DLm+g(KtxlNp<)P5qK
z#RrIZ82L>vfFDgS0%`vaIqd83tdh?HvfoWayhHefkn#cHw+Vk(@EGOzJ@G7Yfl|K{
zx&GX>P^k_)itO(mBKE*T#F+a|GVmDkN1K7ig?j{%E1$^Ubq0LFrN+g?TBVkYew85C
zIqf$HZV|jzkU!F>PYH6JlMf1hN$`-MeqTWUsPM-Hzbp7-!Pf;j5A^r%f}aUSkypxF
z1TPR=E4W2)r(jyp5gZY`U+@vZrvxViUlaV9;BN#!6^w8`5XU^h^99!k@&hOJI|OY(
zNAPyRLxK+penXI771`dzyb@;!@*6JsYQf6|dj$st?-aaW@aux#5`0$hHNi82Cbo3?
zuOlLcnS!&3*sd3e{(RvV3tl4f9>H5ho)avJ{BFUoi2SJVCyChZPY8Y>^4}8?$A1cb
zLPQ)E*0_$72#)_1X}&=C65&?~-Xxe2yjSoc!N-W`=bM7B2)-^jNkm?vIP?+U<9dWW
c-@^u8zWnJ=&NBvimvEjD4ct8Bo=dy`1z>ra3jhEB

literal 0
HcmV?d00001

diff --git a/contrib/kbipack/gmp_normal_form.c b/contrib/kbipack/gmp_normal_form.c
new file mode 100755
index 0000000000..42785337e7
--- /dev/null
+++ b/contrib/kbipack/gmp_normal_form.c
@@ -0,0 +1,783 @@
+/* 
+   Implementation for integer computation of Hermite and Smith normal 
+   forms of matrices of modest size. 
+
+   Implementation: Dense matrix with GMP-library's mpz_t elements to 
+                   hold huge integers. 
+
+   Algorithm: Kannan - Bachem algorithm with improvement by
+              Chou and Collins. Expects a large number of unit invariant 
+	      factors and takes advantage of them as they appear. 
+
+   References: 
+    [1] Ravindran Kannan, Achim Bachem: 
+        "Polynomial algorithms for computing the Smith and Hermite normal 
+	forms of an integer matrix", 
+	SIAM J. Comput., vol. 8, no. 5, pp. 499-507, 1979.
+    [2] Tsu-Wu J.Chou, George E. Collins: 
+        "Algorithms for the solution of systems of linear Diophantine 
+	equations", 
+	SIAM J. Comput., vol. 11, no. 4, pp. 687-708, 1982.
+    [3] GMP homepage http://www.swox.com/gmp/
+    [4] GNU gmp page http://www.gnu.org/software/gmp/
+
+   Copyright (C) 30.10.2003 Saku Suuriniemi TUT/CEM
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   Saku Suuriniemi, TUT/Electromagetics
+   P.O.Box 692, FIN-33101 Tampere, Finland
+   saku.suuriniemi@tut.fi
+
+   $Id: gmp_normal_form.c,v 1.1 2009-03-30 14:10:57 matti Exp $
+*/
+
+#include<stdlib.h>
+#include"gmp_blas.h"
+#include"gmp_matrix.h"
+#include"gmp_normal_form.h"
+
+
+/* The unaltered matrix and identity left and right factors */
+static gmp_normal_form *
+create_gmp_trivial_normal_form(gmp_matrix * A, 
+			       inverted_flag left_inverted,
+			       inverted_flag right_inverted)
+{
+  size_t rows, cols;
+
+  gmp_normal_form * new_nf;
+  if(A == NULL)
+    {
+      return NULL;
+    }
+
+  new_nf = (gmp_normal_form *) malloc(sizeof(gmp_normal_form));
+  if(new_nf == NULL)
+    {
+      destroy_gmp_matrix(A);
+      return NULL;
+    }
+
+  rows = A -> rows;
+  cols = A -> cols;
+
+  if((rows == 0) || (cols == 0))
+    {
+      destroy_gmp_matrix(A);
+      return NULL;
+    }
+
+  new_nf->left = create_gmp_matrix_identity(rows);
+  if(new_nf->left == NULL)
+    {
+      destroy_gmp_matrix(A);
+      free(new_nf);
+      return NULL;
+    }
+
+  new_nf->right = create_gmp_matrix_identity(cols);
+  if(new_nf->right == NULL)
+    {
+      destroy_gmp_matrix(A);
+      destroy_gmp_matrix(new_nf->left);
+      free(new_nf);
+      return NULL;
+    }
+
+  new_nf -> canonical = A;
+  new_nf -> left_inverted  = left_inverted;
+  new_nf -> right_inverted = right_inverted;
+
+  return new_nf;
+}
+
+static int
+gmp_Hermite_eliminate_step(gmp_matrix * L, gmp_matrix * U, 
+			   size_t col, inverted_flag right_inverted)
+{
+  size_t ind, row_limit;
+  mpz_t  pivot, elem;
+  mpz_t  bez1, bez2, gc_div;
+  mpz_t  cff1, cff2;
+
+  mpz_init(pivot);
+  mpz_init(elem);
+  mpz_init(bez1);
+  mpz_init(bez2);
+  mpz_init(cff1);
+  mpz_init(cff2);
+  mpz_init(gc_div);
+
+  row_limit = (L->rows >= col) ? 
+    col-1 : 
+    L->rows;
+
+  for(ind = 1; ind <= row_limit; ind++)
+    {
+      gmp_matrix_get_elem(elem, ind, col, L);
+
+      /* Eliminate only if nonzero */
+      if(mpz_sgn (elem) != 0)
+	{
+	  gmp_matrix_get_elem(pivot, ind, ind, L);
+
+	  /* Extended Euclid's: 
+	     bez1*pivot+bez2*elem = gc_div */
+	  gmp_blas_rotg(gc_div, bez1, bez2, pivot, elem);
+
+	  /* Make cff1 = -elem/gc_div */
+	  mpz_divexact(cff1, elem,  gc_div);
+	  mpz_neg     (cff1, cff1);
+	  /* Make cff2 = pivot/gc_div */
+	  mpz_divexact(cff2, pivot, gc_div);
+
+	  /* Update the HNF canonical matrix */
+	  gmp_matrix_col_rot(bez1, bez2, ind,
+			     cff1, cff2, col,
+			     L);
+
+	  /* Update the unimodular factor matrix */
+	  if(right_inverted == INVERTED)
+	    {
+	      gmp_matrix_col_rot(bez1, bez2, ind,
+				 cff1, cff2, col,
+				 U);
+	    }
+	  else
+	    {
+
+	      /* [bez1, -elem/gc_div] [pivot/gc_div, elem/gc_div]
+		 [bez2, pivot/gc_div] [-bez2,        bez1       ]  = I_2 */
+	      mpz_neg(cff1, cff1);
+	      mpz_neg(bez2, bez2);
+	      gmp_matrix_row_rot(cff2, cff1, ind,
+				 bez2, bez1, col,
+				 U);
+	    }
+	}
+    }
+  mpz_clear(pivot);
+  mpz_clear(elem);
+  mpz_clear(bez1);
+  mpz_clear(bez2);
+  mpz_clear(cff1);
+  mpz_clear(cff2);
+  mpz_clear(gc_div);
+
+  return EXIT_SUCCESS;
+}
+
+
+
+static int
+gmp_Hermite_reduce_step(gmp_matrix * L,  gmp_matrix * U, 
+			size_t col, inverted_flag right_inverted)
+{
+
+  size_t i, j;
+  mpz_t  pivot, elem;
+  mpz_t  cff;
+
+  mpz_init(pivot);
+  mpz_init(elem);
+  mpz_init(cff);
+
+  if(col > (L->rows))
+    {
+      return EXIT_FAILURE;
+    }
+
+  /*  printf("Col = %i\n", col);
+      printf("L before\n");
+      gmp_matrix_printf(L);*/
+
+  for(j = col-1; j >= 1; j--)
+    {
+      for(i = j+1; i <= col; i++)
+	{
+	  gmp_matrix_get_elem(pivot, i, i, L);
+	  gmp_matrix_get_elem(elem,  i, j, L);
+	  /* printf("   i %i j %i\n",i,j);  */
+
+	  if((mpz_cmpabs(elem, pivot) >= 0) || (mpz_sgn(elem) < 0))
+	    {
+	      /* The objective:
+		  0           <= elem + k*pivot < pivot
+		  -elem       <= k*pivot        < pivot - elem
+		  -elem/pivot <= k              < - elem/pivot + 1
+
+		  Solution:
+		  k = ceil(-elem/pivot)
+	      */
+
+	      /* Make cff = -elem */
+	      mpz_neg    (cff, elem);
+	      mpz_cdiv_q (cff, cff, pivot);
+
+	      /* printf("col %i j %i\n", i, j); 
+		 printf("elem %i k %i pivot %i\n", 
+		 mpz_get_si(elem), 
+		 mpz_get_si(cff), 
+		 mpz_get_si(pivot));*/
+
+
+	      gmp_matrix_add_col(cff, i, j, L);
+
+	      /* Update the unimodular factor matrix */
+	      if(right_inverted == INVERTED)
+		{
+		  gmp_matrix_add_col(cff, i, j, U);
+		}
+
+	      /* [1   0] [ 1   0] = [1 0]
+		 [cff 1] [-cff 1]   [0 1] */
+	      else
+		{
+		  mpz_neg (cff, cff);
+		  gmp_matrix_add_row(cff, j, i, U);
+		}
+
+	      /* printf("\n");
+		 gmp_matrix_printf(L);*/
+
+	    }
+	}
+    }
+
+/*  printf("L after\n"); */
+/*   gmp_matrix_printf(L); */
+
+  mpz_clear(pivot);
+  mpz_clear(elem);
+  mpz_clear(cff);
+  return EXIT_SUCCESS;
+}
+
+
+static int
+gmp_normal_form_make_Hermite(gmp_normal_form * nf)
+{
+  size_t rows, cols;
+  size_t pivot_ind;
+  size_t schur, colind;
+  mpz_t  pivot;
+  gmp_matrix * canonical;
+  gmp_matrix * left;
+  gmp_matrix * right;
+
+  if(nf == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* OK, it's safe to get to business */
+  canonical = nf->canonical;
+  left      = nf->left;
+  right     = nf->right;
+  rows      = canonical -> rows;
+  cols      = canonical -> cols;
+
+  mpz_init(pivot);
+
+  /* "schur" denotes where the present Schur complement starts */
+  schur = 1;
+
+  while((schur <= rows) && (schur <= cols))
+    {
+      /* Eliminate a column */
+      if (schur > 1)
+	{
+	  gmp_Hermite_eliminate_step(canonical, right, 
+				     schur, nf->right_inverted);
+	}
+
+      /* Find a pivot */
+      pivot_ind = gmp_matrix_col_inz(schur, rows, schur, canonical);
+
+
+      /* If no nonzeros was found, the column is all zero, hence 
+	 settled with. Permute it to the end and decrement cols. */
+      if(pivot_ind == 0)
+	{
+	  gmp_matrix_swap_cols(schur, cols, canonical);
+
+	  if(nf -> right_inverted == INVERTED)
+	    {
+	      gmp_matrix_swap_cols(schur, cols, right);
+	    }
+	  else
+	    {
+	      gmp_matrix_swap_rows(schur, cols, right);
+	    }
+
+	  cols--;
+
+	  /* When the whole column was zeroed, the diagonal 
+	     elements may have got reduced. Reduce the sub-
+	     diagonals as well*/
+
+	  if(schur > 1)
+	    {
+	      gmp_Hermite_reduce_step (canonical, right, schur-1, 
+				       nf -> right_inverted);
+	    }
+	}
+
+      /* A nonzero pivot was found. Permute it to the diagonal position, 
+	 make it positive, and reduce the off-diagonals. 
+	 The schur complement now starts from the next diagonal. */
+      else
+	{
+	  pivot_ind = schur+pivot_ind-1;
+	  gmp_matrix_swap_rows(schur, pivot_ind, canonical);
+
+	  if(nf->left_inverted == INVERTED)
+	    {
+	      gmp_matrix_swap_rows(schur, pivot_ind, left);
+	    }
+	  else
+	    {
+	      gmp_matrix_swap_cols(schur, pivot_ind, left);
+	    }
+
+	  /* Make the pivot positive */
+	  gmp_matrix_get_elem(pivot, schur, schur, canonical);
+
+	  if(mpz_cmp_si(pivot, 0) < 0)
+	    {
+	      gmp_matrix_negate_col(schur, canonical); 
+
+	      if(nf->right_inverted == INVERTED)
+		{
+		  gmp_matrix_negate_col(schur, right); 
+		}
+	      else
+		{
+		  gmp_matrix_negate_row(schur, right); 
+		}
+	    }
+
+	  /*  Reduce off-diagonals */
+	  gmp_Hermite_reduce_step (canonical, right, schur, nf -> right_inverted);
+
+	  schur++;
+	}
+    }
+
+  /* The Schur complement is now empty. There may still be uneliminated 
+     columns left (in case of a wide matrix) */
+
+  colind = schur;
+  while (colind <= cols)
+    {
+      gmp_Hermite_eliminate_step (canonical, right, colind,  nf->right_inverted);
+      gmp_Hermite_reduce_step    (canonical, right, schur-1, nf->right_inverted);
+      colind++;
+    }
+
+  mpz_clear(pivot);
+
+  return EXIT_SUCCESS;
+}
+
+
+
+gmp_normal_form * 
+create_gmp_Hermite_normal_form(gmp_matrix * A, 
+			       inverted_flag left_inverted,
+			       inverted_flag right_inverted)
+{
+  gmp_normal_form * new_nf;
+
+  if(A == NULL)
+    {
+      return NULL;
+    }
+
+  new_nf = 
+    create_gmp_trivial_normal_form(A, left_inverted, right_inverted);
+
+  if(new_nf == NULL)
+    {
+      /* A has been destroyed already */
+      return NULL;
+    }
+
+  if(gmp_normal_form_make_Hermite(new_nf) != EXIT_SUCCESS)
+    {
+      destroy_gmp_normal_form(new_nf);
+      return NULL;
+    }
+
+  return new_nf;
+}
+
+
+gmp_normal_form *
+create_gmp_Smith_normal_form(gmp_matrix * A,
+			     inverted_flag left_inverted,
+			     inverted_flag right_inverted)
+{
+
+  gmp_normal_form * new_nf;
+  gmp_matrix * canonical;
+  gmp_matrix * elbow;
+  inverted_flag elbow_flag;
+  size_t rows, cols;
+  size_t i, j;
+  size_t subd_ind, row_undivisible;
+  size_t last_ready_row, first_ready_col, ind;
+  mpz_t  pivot;
+  mpz_t  remainder;
+
+  if(A == NULL)
+    {
+      return NULL;
+    }
+
+  new_nf = 
+    create_gmp_trivial_normal_form(A, left_inverted, right_inverted);
+
+  if(new_nf == NULL)
+    {
+      /* A has been destroyed already */
+      return NULL;
+    }
+
+  canonical = new_nf -> canonical;
+  mpz_init(pivot);
+  mpz_init(remainder);
+  rows = canonical->rows;
+  cols = canonical->cols;
+  last_ready_row  = 0;
+  first_ready_col = (cols < rows) ? (cols+1) : (rows+1);
+
+  while(first_ready_col > last_ready_row + 1)
+    {
+      /*******/
+      /* HNF */
+      /*******/
+
+      if(gmp_normal_form_make_Hermite(new_nf) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+
+#ifdef DEBUG
+      printf("HNF\n");
+      gmp_matrix_printf (new_nf -> left);
+      gmp_matrix_printf (new_nf -> canonical);
+      gmp_matrix_printf (new_nf -> right);
+#endif
+
+      /**********************/
+      /* Find ready columns */
+      /**********************/
+
+      /* If a diagonal entry is zero, so is the corresponding 
+	 column. The zero diagonals always reside in the end. 
+	 Seek until zero diagonal encountered, but stay within the matrix! */
+      ind = 1;
+      while ( (ind < first_ready_col) && 
+	      (mpz_cmp_si(canonical -> storage[(ind-1)+(ind-1)*rows], 0) != 0) 
+	      )	     
+	{
+	  ind ++;
+	}
+      first_ready_col = ind;
+
+      /* Note: The number of ready cols is settled after the first HNF, 
+	 but the check is cheap. */
+
+      /**********************************************/
+      /* Permute unit diagonals such that they lead */
+      /**********************************************/
+
+      /* If the recently computed HNF has ones on the diagonal, their 
+	 corresponding rows are all zero (except the diagonal). 
+	 They are then settled, because the next LHNF kills the elements
+	 on their columns. */
+
+      ind = last_ready_row+1;
+  
+      /* Stay within the nonzero cols of the matrix */
+      while (ind < first_ready_col)
+	{
+	  /* Unit diagonal encountered */
+	  if(mpz_cmp_si ( canonical->storage[(ind-1) + (ind-1)*rows], 
+			  1) == 0)
+	    {
+	      /* If not in the beginning, permute to extend the leading minor 
+		 with unit diagonals */
+	      if(ind != last_ready_row+1)
+		{
+		  gmp_matrix_swap_rows(last_ready_row+1,     ind, 
+				       new_nf -> canonical);
+		  
+		  if(left_inverted == INVERTED)
+		    {
+		      gmp_matrix_swap_rows(last_ready_row+1, ind, 
+					   new_nf -> left);
+		    }
+		  else
+		    {
+		      gmp_matrix_swap_cols(last_ready_row+1, ind, 
+					   new_nf -> left);
+		    }
+		  
+		  gmp_matrix_swap_cols(last_ready_row+1,     ind, 
+				       new_nf -> canonical);
+		  
+		  if(right_inverted == INVERTED)
+		    {
+		      gmp_matrix_swap_cols(last_ready_row+1, ind, 
+					   new_nf -> right);
+		    }
+		  else
+		    {
+		      gmp_matrix_swap_rows(last_ready_row+1, ind, 
+					   new_nf -> right);
+		    }
+		}
+	      last_ready_row ++;
+	    }
+	  ind++;
+	}
+      
+#ifdef DEBUG
+      printf("Leading units\n");
+      gmp_matrix_printf (new_nf -> left);
+      gmp_matrix_printf (new_nf -> canonical);
+      gmp_matrix_printf (new_nf -> right);
+#endif
+
+      /********/
+      /* LHNF */
+      /********/
+
+      /* Extravagant strategy: Compute HNF of an explicit tranpose. */
+
+      /* 1. Transpose everything in the normal form */
+      if(gmp_matrix_transp(canonical) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+      if(gmp_matrix_transp(new_nf->left) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+      if(gmp_matrix_transp(new_nf->right) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+
+      elbow          = new_nf ->left;
+      new_nf ->left  = new_nf ->right;
+      new_nf ->right = elbow;
+      elbow_flag              = new_nf ->left_inverted;
+      new_nf ->left_inverted  = new_nf ->right_inverted;
+      new_nf ->right_inverted = elbow_flag;
+
+      /* 2. Compute HNF of the transpose */
+      if(gmp_normal_form_make_Hermite(new_nf) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+
+      /* 3. Transpose everything back */
+      if(gmp_matrix_transp(canonical) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+      if(gmp_matrix_transp(new_nf->left) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+      if(gmp_matrix_transp(new_nf->right) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+      elbow          = new_nf ->left;
+      new_nf ->left  = new_nf ->right;
+      new_nf ->right = elbow;
+      elbow_flag              = new_nf ->left_inverted;
+      new_nf ->left_inverted  = new_nf ->right_inverted;
+      new_nf ->right_inverted = elbow_flag;
+
+
+#ifdef DEBUG
+      printf("LHNF\n");
+      gmp_matrix_printf (new_nf -> left);
+      gmp_matrix_printf (new_nf -> canonical);
+      gmp_matrix_printf (new_nf -> right);
+#endif
+
+
+      /*****************************************************/
+      /* Check if more of the leading normal form is ready */
+      /*****************************************************/
+
+      /* The matrix is in LHNF, i.e. it is upper triangular. 
+	 If the row trailing the top left diagonal element is 
+	 zero, the diagonal element may be an invariant factor 
+	 on its final position, and the stage may be ready.
+
+	 The stage may not still be ready: The leading diagonal element 
+	 of D may not divide the rest of the Schur complement. */
+
+      subd_ind = 0;
+      row_undivisible = 0;
+
+      /* Explanation of loop conditions:
+	 1.) No relative primes found from Schur complement
+	 2.) Stay within the Schur complement
+	 3.) If a nonzero is found from the trailing row, the stage is 
+	     definitely not ready */
+      while (row_undivisible == 0 &&   
+	     last_ready_row + 1 < first_ready_col && 
+	     subd_ind == 0)        
+	{
+	  subd_ind = 
+	    gmp_matrix_row_inz(last_ready_row+1,
+			       last_ready_row+2, cols, 
+			       canonical);
+
+	  /* printf("subd_ind %i\n", subd_ind);
+	     printf("last_ready_row %i\n", last_ready_row); */
+      
+	  /* No nonzeros found, the stage may be ready */
+	  if (subd_ind == 0)
+	    {
+	      mpz_set (pivot, 
+		       canonical->storage[(last_ready_row)+
+					  (last_ready_row)*rows]);
+
+	      /* Check whether the pivot divides all elements in the Schur 
+		 complement */
+	      row_undivisible = 0;
+	      for(j = last_ready_row+2; j < first_ready_col; j++)
+		{
+		  for(i = last_ready_row+2; i <= j; i++)
+		    {
+		      mpz_tdiv_r (remainder, 
+				  canonical->storage[(i-1)+
+						     (j-1)*rows], 
+				  pivot);
+		  
+		      if(mpz_cmp_si(remainder, 0) !=0)
+			{
+			  row_undivisible = i;
+			  /* col_undivisible = j; */
+			}
+		    }
+		}
+
+	      /* printf("Row undivisible %i\n", row_undivisible); */
+
+	      /* If a relative prime was found from the Schur complement,  
+		 add that row to the first row of the Schur complement */
+	      if(row_undivisible != 0)
+		{
+		  mpz_set_si (remainder, 1);
+		  gmp_matrix_add_row(remainder, 
+				     row_undivisible, last_ready_row+1, 
+				     canonical);
+
+		  /* [ 1 0] [1 0] = [1 0]
+		     [-1 1] [1 1]   [0 1] */
+
+		  if(left_inverted == INVERTED)
+		    {
+		      gmp_matrix_add_row(remainder, 
+					 row_undivisible, last_ready_row+1, 
+					 new_nf->left);
+		    }
+		  else
+		    {
+		      mpz_neg (remainder, remainder);
+		      gmp_matrix_add_col(remainder, 
+					 last_ready_row+1, row_undivisible,
+					 new_nf->left);
+		    }
+		}
+	      /* The Schur complement is smaller by one again */
+	      else
+		{
+		  last_ready_row++;
+		}
+	    }
+	} 
+    }  /* The main loop ends here */
+  
+  mpz_clear(pivot);
+  mpz_clear(remainder);
+  return new_nf;
+}
+
+int destroy_gmp_normal_form(gmp_normal_form * nf)
+{
+  int status;
+
+  if(nf == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  status = EXIT_SUCCESS;
+  if(destroy_gmp_matrix(nf -> left) == EXIT_FAILURE)
+    {
+      status = EXIT_FAILURE;
+    }
+
+  if(destroy_gmp_matrix(nf -> canonical) == EXIT_FAILURE)
+    {
+      status = EXIT_FAILURE;
+    }
+
+  if(destroy_gmp_matrix(nf -> right) == EXIT_FAILURE)
+    {
+      status = EXIT_FAILURE;      
+    }
+  free(nf);
+
+  return status;
+}
+
+
diff --git a/contrib/kbipack/gmp_normal_form.c~ b/contrib/kbipack/gmp_normal_form.c~
new file mode 100755
index 0000000000..5f695a53d0
--- /dev/null
+++ b/contrib/kbipack/gmp_normal_form.c~
@@ -0,0 +1,784 @@
+/* 
+   Implementation for integer computation of Hermite and Smith normal 
+   forms of matrices of modest size. 
+
+   Implementation: Dense matrix with GMP-library's mpz_t elements to 
+                   hold huge integers. 
+
+   Algorithm: Kannan - Bachem algorithm with improvement by
+              Chou and Collins. Expects a large number of unit invariant 
+	      factors and takes advantage of them as they appear. 
+
+   References: 
+    [1] Ravindran Kannan, Achim Bachem: 
+        "Polynomial algorithms for computing the Smith and Hermite normal 
+	forms of an integer matrix", 
+	SIAM J. Comput., vol. 8, no. 5, pp. 499-507, 1979.
+    [2] Tsu-Wu J.Chou, George E. Collins: 
+        "Algorithms for the solution of systems of linear Diophantine 
+	equations", 
+	SIAM J. Comput., vol. 11, no. 4, pp. 687-708, 1982.
+    [3] GMP homepage http://www.swox.com/gmp/
+    [4] GNU gmp page http://www.gnu.org/software/gmp/
+
+   Copyright (C) 30.10.2003 Saku Suuriniemi TUT/CEM
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   Saku Suuriniemi, TUT/Electromagetics
+   P.O.Box 692, FIN-33101 Tampere, Finland
+   saku.suuriniemi@tut.fi
+
+   $Id: gmp_normal_form.c~,v 1.1 2009-03-30 14:10:57 matti Exp $
+*/
+
+#include<stdlib.h>
+#include"gmp_blas.h"
+#include"gmp_matrix.h"
+#include"gmp_normal_form.h"
+
+
+/* The unaltered matrix and identity left and right factors */
+static gmp_normal_form *
+create_gmp_trivial_normal_form(gmp_matrix * A, 
+			       inverted_flag left_inverted,
+			       inverted_flag right_inverted)
+{
+  size_t rows, cols;
+
+  gmp_normal_form * new_nf;
+  if(A == NULL)
+    {
+      return NULL;
+    }
+
+  new_nf = (gmp_normal_form *) malloc(sizeof(gmp_normal_form));
+  if(new_nf == NULL)
+    {
+      destroy_gmp_matrix(A);
+      return NULL;
+    }
+
+  rows = A -> rows;
+  cols = A -> cols;
+
+  if((rows == 0) || (cols == 0))
+    {
+      destroy_gmp_matrix(A);
+      return NULL;
+    }
+
+  new_nf->left = create_gmp_matrix_identity(rows);
+  if(new_nf->left == NULL)
+    {
+      destroy_gmp_matrix(A);
+      free(new_nf);
+      return NULL;
+    }
+
+  new_nf->right = create_gmp_matrix_identity(cols);
+  if(new_nf->right == NULL)
+    {
+      destroy_gmp_matrix(A);
+      destroy_gmp_matrix(new_nf->left);
+      free(new_nf);
+      return NULL;
+    }
+
+  new_nf -> canonical = A;
+  new_nf -> left_inverted  = left_inverted;
+  new_nf -> right_inverted = right_inverted;
+
+  return new_nf;
+}
+
+static int
+gmp_Hermite_eliminate_step(gmp_matrix * L, gmp_matrix * U, 
+			   size_t col, inverted_flag right_inverted)
+{
+  size_t ind, row_limit;
+  mpz_t  pivot, elem;
+  mpz_t  bez1, bez2, gc_div;
+  mpz_t  cff1, cff2;
+
+  mpz_init(pivot);
+  mpz_init(elem);
+  mpz_init(bez1);
+  mpz_init(bez2);
+  mpz_init(cff1);
+  mpz_init(cff2);
+  mpz_init(gc_div);
+
+  row_limit = (L->rows >= col) ? 
+    col-1 : 
+    L->rows;
+
+  for(ind = 1; ind <= row_limit; ind++)
+    {
+      gmp_matrix_get_elem(elem, ind, col, L);
+
+      /* Eliminate only if nonzero */
+      if(mpz_sgn (elem) != 0)
+	{
+	  gmp_matrix_get_elem(pivot, ind, ind, L);
+
+	  /* Extended Euclid's: 
+	     bez1*pivot+bez2*elem = gc_div */
+	  gmp_blas_rotg(gc_div, bez1, bez2, pivot, elem);
+
+	  /* Make cff1 = -elem/gc_div */
+	  mpz_divexact(cff1, elem,  gc_div);
+	  mpz_neg     (cff1, cff1);
+	  /* Make cff2 = pivot/gc_div */
+	  mpz_divexact(cff2, pivot, gc_div);
+
+	  /* Update the HNF canonical matrix */
+	  gmp_matrix_col_rot(bez1, bez2, ind,
+			     cff1, cff2, col,
+			     L);
+
+	  /* Update the unimodular factor matrix */
+	  if(right_inverted == INVERTED)
+	    {
+	      gmp_matrix_col_rot(bez1, bez2, ind,
+				 cff1, cff2, col,
+				 U);
+	    }
+	  else
+	    {
+
+	      /* [bez1, -elem/gc_div] [pivot/gc_div, elem/gc_div]
+		 [bez2, pivot/gc_div] [-bez2,        bez1       ]  = I_2 */
+	      mpz_neg(cff1, cff1);
+	      mpz_neg(bez2, bez2);
+	      gmp_matrix_row_rot(cff2, cff1, ind,
+				 bez2, bez1, col,
+				 U);
+	    }
+	}
+    }
+  mpz_clear(pivot);
+  mpz_clear(elem);
+  mpz_clear(bez1);
+  mpz_clear(bez2);
+  mpz_clear(cff1);
+  mpz_clear(cff2);
+  mpz_clear(gc_div);
+
+  return EXIT_SUCCESS;
+}
+
+
+
+static int
+gmp_Hermite_reduce_step(gmp_matrix * L,  gmp_matrix * U, 
+			size_t col, inverted_flag right_inverted)
+{
+
+  size_t i, j;
+  mpz_t  pivot, elem;
+  mpz_t  cff;
+
+  mpz_init(pivot);
+  mpz_init(elem);
+  mpz_init(cff);
+
+  if(col > (L->rows))
+    {
+      return EXIT_FAILURE;
+    }
+
+  /*  printf("Col = %i\n", col);
+      printf("L before\n");
+      gmp_matrix_printf(L);*/
+
+  for(j = col-1; j >= 1; j--)
+    {
+      for(i = j+1; i <= col; i++)
+	{
+	  gmp_matrix_get_elem(pivot, i, i, L);
+	  gmp_matrix_get_elem(elem,  i, j, L);
+	  /* printf("   i %i j %i\n",i,j);  */
+
+	  if((mpz_cmpabs(elem, pivot) >= 0) || (mpz_sgn(elem) < 0))
+	    {
+	      /* The objective:
+		  0           <= elem + k*pivot < pivot
+		  -elem       <= k*pivot        < pivot - elem
+		  -elem/pivot <= k              < - elem/pivot + 1
+
+		  Solution:
+		  k = ceil(-elem/pivot)
+	      */
+
+	      /* Make cff = -elem */
+	      mpz_neg    (cff, elem);
+	      mpz_cdiv_q (cff, cff, pivot);
+
+	      /* printf("col %i j %i\n", i, j); 
+		 printf("elem %i k %i pivot %i\n", 
+		 mpz_get_si(elem), 
+		 mpz_get_si(cff), 
+		 mpz_get_si(pivot));*/
+
+
+	      gmp_matrix_add_col(cff, i, j, L);
+
+	      /* Update the unimodular factor matrix */
+	      if(right_inverted == INVERTED)
+		{
+		  gmp_matrix_add_col(cff, i, j, U);
+		}
+
+	      /* [1   0] [ 1   0] = [1 0]
+		 [cff 1] [-cff 1]   [0 1] */
+	      else
+		{
+		  mpz_neg (cff, cff);
+		  gmp_matrix_add_row(cff, j, i, U);
+		}
+
+	      /* printf("\n");
+		 gmp_matrix_printf(L);*/
+
+	    }
+	}
+    }
+
+/*  printf("L after\n"); */
+/*   gmp_matrix_printf(L); */
+
+  mpz_clear(pivot);
+  mpz_clear(elem);
+  mpz_clear(cff);
+  return EXIT_SUCCESS;
+}
+
+
+static int
+gmp_normal_form_make_Hermite(gmp_normal_form * nf)
+{
+  size_t rows, cols;
+  size_t pivot_ind;
+  size_t schur, colind;
+  mpz_t  pivot;
+  gmp_matrix * canonical;
+  gmp_matrix * left;
+  gmp_matrix * right;
+
+  if(nf == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  /* OK, it's safe to get to business */
+  canonical = nf->canonical;
+  left      = nf->left;
+  right     = nf->right;
+  rows      = canonical -> rows;
+  cols      = canonical -> cols;
+
+  mpz_init(pivot);
+
+  /* "schur" denotes where the present Schur complement starts */
+  schur = 1;
+
+  while((schur <= rows) && (schur <= cols))
+    {
+      /* Eliminate a column */
+      if (schur > 1)
+	{
+	  gmp_Hermite_eliminate_step(canonical, right, 
+				     schur, nf->right_inverted);
+	}
+
+      /* Find a pivot */
+      pivot_ind = gmp_matrix_col_inz(schur, rows, schur, canonical);
+
+
+      /* If no nonzeros was found, the column is all zero, hence 
+	 settled with. Permute it to the end and decrement cols. */
+      if(pivot_ind == 0)
+	{
+	  gmp_matrix_swap_cols(schur, cols, canonical);
+
+	  if(nf -> right_inverted == INVERTED)
+	    {
+	      gmp_matrix_swap_cols(schur, cols, right);
+	    }
+	  else
+	    {
+	      gmp_matrix_swap_rows(schur, cols, right);
+	    }
+
+	  cols--;
+
+	  /* When the whole column was zeroed, the diagonal 
+	     elements may have got reduced. Reduce the sub-
+	     diagonals as well*/
+
+	  if(schur > 1)
+	    {
+	      gmp_Hermite_reduce_step (canonical, right, schur-1, 
+				       nf -> right_inverted);
+	    }
+	}
+
+      /* A nonzero pivot was found. Permute it to the diagonal position, 
+	 make it positive, and reduce the off-diagonals. 
+	 The schur complement now starts from the next diagonal. */
+      else
+	{
+	  pivot_ind = schur+pivot_ind-1;
+	  gmp_matrix_swap_rows(schur, pivot_ind, canonical);
+
+	  if(nf->left_inverted == INVERTED)
+	    {
+	      gmp_matrix_swap_rows(schur, pivot_ind, left);
+	    }
+	  else
+	    {
+	      gmp_matrix_swap_cols(schur, pivot_ind, left);
+	    }
+
+	  /* Make the pivot positive */
+	  gmp_matrix_get_elem(pivot, schur, schur, canonical);
+
+	  if(mpz_cmp_si(pivot, 0) < 0)
+	    {
+	      gmp_matrix_negate_col(schur, canonical); 
+
+	      if(nf->right_inverted == INVERTED)
+		{
+		  gmp_matrix_negate_col(schur, right); 
+		}
+	      else
+		{
+		  gmp_matrix_negate_row(schur, right); 
+		}
+	    }
+
+	  /*  Reduce off-diagonals */
+	  gmp_Hermite_reduce_step (canonical, right, schur, nf -> right_inverted);
+
+	  schur++;
+	}
+    }
+
+  /* The Schur complement is now empty. There may still be uneliminated 
+     columns left (in case of a wide matrix) */
+
+  colind = schur;
+  while (colind <= cols)
+    {
+      gmp_Hermite_eliminate_step (canonical, right, colind,  nf->right_inverted);
+      gmp_Hermite_reduce_step    (canonical, right, schur-1, nf->right_inverted);
+      colind++;
+    }
+
+  mpz_clear(pivot);
+
+  return EXIT_SUCCESS;
+}
+
+
+
+gmp_normal_form * 
+create_gmp_Hermite_normal_form(gmp_matrix * A, 
+			       inverted_flag left_inverted,
+			       inverted_flag right_inverted)
+{
+  gmp_normal_form * new_nf;
+
+  if(A == NULL)
+    {
+      return NULL;
+    }
+
+  new_nf = 
+    create_gmp_trivial_normal_form(A, left_inverted, right_inverted);
+
+  if(new_nf == NULL)
+    {
+      /* A has been destroyed already */
+      return NULL;
+    }
+
+  if(gmp_normal_form_make_Hermite(new_nf) != EXIT_SUCCESS)
+    {
+      destroy_gmp_normal_form(new_nf);
+      return NULL;
+    }
+
+  return new_nf;
+}
+
+
+gmp_normal_form *
+create_gmp_Smith_normal_form(gmp_matrix * A,
+			     inverted_flag left_inverted,
+			     inverted_flag right_inverted)
+{
+
+  gmp_normal_form * new_nf;
+  gmp_matrix * canonical;
+  gmp_matrix * elbow;
+  inverted_flag elbow_flag;
+  size_t rows, cols;
+  size_t i, j;
+  size_t subd_ind, row_undivisible;
+  size_t last_ready_row, first_ready_col, ind;
+  mpz_t  pivot;
+  mpz_t  remainder;
+
+  if(A == NULL)
+    {
+      return NULL;
+    }
+
+  new_nf = 
+    create_gmp_trivial_normal_form(A, left_inverted, right_inverted);
+
+  if(new_nf == NULL)
+    {
+      /* A has been destroyed already */
+      return NULL;
+    }
+
+  canonical = new_nf -> canonical;
+  mpz_init(pivot);
+  mpz_init(remainder);
+  rows = canonical->rows;
+  cols = canonical->cols;
+  last_ready_row  = 0;
+  first_ready_col = (cols < rows) ? (cols+1) : (rows+1);
+
+  while(first_ready_col > last_ready_row + 1)
+    {
+      /*******/
+      /* HNF */
+      /*******/
+
+      if(gmp_normal_form_make_Hermite(new_nf) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+
+#ifdef DEBUG
+      printf("HNF\n");
+      gmp_matrix_printf (new_nf -> left);
+      gmp_matrix_printf (new_nf -> canonical);
+      gmp_matrix_printf (new_nf -> right);
+#endif
+
+      /**********************/
+      /* Find ready columns */
+      /**********************/
+
+      /* If a diagonal entry is zero, so is the corresponding 
+	 column. The zero diagonals always reside in the end. 
+	 Seek until zero diagonal encountered, but stay within the matrix! */
+      ind = 1;
+      while ( (mpz_cmp_si(canonical -> storage[(ind-1)+(ind-1)*rows], 0) 
+	       != 0) 
+	      && 
+	      (ind < first_ready_col) )
+	{
+	  ind ++;
+	}
+      first_ready_col = ind;
+
+      /* Note: The number of ready cols is settled after the first HNF, 
+	 but the check is cheap. */
+
+      /**********************************************/
+      /* Permute unit diagonals such that they lead */
+      /**********************************************/
+
+      /* If the recently computed HNF has ones on the diagonal, their 
+	 corresponding rows are all zero (except the diagonal). 
+	 They are then settled, because the next LHNF kills the elements
+	 on their columns. */
+
+      ind = last_ready_row+1;
+  
+      /* Stay within the nonzero cols of the matrix */
+      while (ind < first_ready_col)
+	{
+	  /* Unit diagonal encountered */
+	  if(mpz_cmp_si ( canonical->storage[(ind-1) + (ind-1)*rows], 
+			  1) == 0)
+	    {
+	      /* If not in the beginning, permute to extend the leading minor 
+		 with unit diagonals */
+	      if(ind != last_ready_row+1)
+		{
+		  gmp_matrix_swap_rows(last_ready_row+1,     ind, 
+				       new_nf -> canonical);
+		  
+		  if(left_inverted == INVERTED)
+		    {
+		      gmp_matrix_swap_rows(last_ready_row+1, ind, 
+					   new_nf -> left);
+		    }
+		  else
+		    {
+		      gmp_matrix_swap_cols(last_ready_row+1, ind, 
+					   new_nf -> left);
+		    }
+		  
+		  gmp_matrix_swap_cols(last_ready_row+1,     ind, 
+				       new_nf -> canonical);
+		  
+		  if(right_inverted == INVERTED)
+		    {
+		      gmp_matrix_swap_cols(last_ready_row+1, ind, 
+					   new_nf -> right);
+		    }
+		  else
+		    {
+		      gmp_matrix_swap_rows(last_ready_row+1, ind, 
+					   new_nf -> right);
+		    }
+		}
+	      last_ready_row ++;
+	    }
+	  ind++;
+	}
+      
+#ifdef DEBUG
+      printf("Leading units\n");
+      gmp_matrix_printf (new_nf -> left);
+      gmp_matrix_printf (new_nf -> canonical);
+      gmp_matrix_printf (new_nf -> right);
+#endif
+
+      /********/
+      /* LHNF */
+      /********/
+
+      /* Extravagant strategy: Compute HNF of an explicit tranpose. */
+
+      /* 1. Transpose everything in the normal form */
+      if(gmp_matrix_transp(canonical) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+      if(gmp_matrix_transp(new_nf->left) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+      if(gmp_matrix_transp(new_nf->right) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+
+      elbow          = new_nf ->left;
+      new_nf ->left  = new_nf ->right;
+      new_nf ->right = elbow;
+      elbow_flag              = new_nf ->left_inverted;
+      new_nf ->left_inverted  = new_nf ->right_inverted;
+      new_nf ->right_inverted = elbow_flag;
+
+      /* 2. Compute HNF of the transpose */
+      if(gmp_normal_form_make_Hermite(new_nf) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+
+      /* 3. Transpose everything back */
+      if(gmp_matrix_transp(canonical) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+      if(gmp_matrix_transp(new_nf->left) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+      if(gmp_matrix_transp(new_nf->right) != EXIT_SUCCESS)
+	{
+	  destroy_gmp_normal_form(new_nf);
+	  mpz_clear(pivot);
+	  mpz_clear(remainder);
+	  return NULL;
+	}
+      elbow          = new_nf ->left;
+      new_nf ->left  = new_nf ->right;
+      new_nf ->right = elbow;
+      elbow_flag              = new_nf ->left_inverted;
+      new_nf ->left_inverted  = new_nf ->right_inverted;
+      new_nf ->right_inverted = elbow_flag;
+
+
+#ifdef DEBUG
+      printf("LHNF\n");
+      gmp_matrix_printf (new_nf -> left);
+      gmp_matrix_printf (new_nf -> canonical);
+      gmp_matrix_printf (new_nf -> right);
+#endif
+
+
+      /*****************************************************/
+      /* Check if more of the leading normal form is ready */
+      /*****************************************************/
+
+      /* The matrix is in LHNF, i.e. it is upper triangular. 
+	 If the row trailing the top left diagonal element is 
+	 zero, the diagonal element may be an invariant factor 
+	 on its final position, and the stage may be ready.
+
+	 The stage may not still be ready: The leading diagonal element 
+	 of D may not divide the rest of the Schur complement. */
+
+      subd_ind = 0;
+      row_undivisible = 0;
+
+      /* Explanation of loop conditions:
+	 1.) No relative primes found from Schur complement
+	 2.) Stay within the Schur complement
+	 3.) If a nonzero is found from the trailing row, the stage is 
+	     definitely not ready */
+      while (row_undivisible == 0 &&   
+	     last_ready_row + 1 < first_ready_col && 
+	     subd_ind == 0)        
+	{
+	  subd_ind = 
+	    gmp_matrix_row_inz(last_ready_row+1,
+			       last_ready_row+2, cols, 
+			       canonical);
+
+	  /* printf("subd_ind %i\n", subd_ind);
+	     printf("last_ready_row %i\n", last_ready_row); */
+      
+	  /* No nonzeros found, the stage may be ready */
+	  if (subd_ind == 0)
+	    {
+	      mpz_set (pivot, 
+		       canonical->storage[(last_ready_row)+
+					  (last_ready_row)*rows]);
+
+	      /* Check whether the pivot divides all elements in the Schur 
+		 complement */
+	      row_undivisible = 0;
+	      for(j = last_ready_row+2; j < first_ready_col; j++)
+		{
+		  for(i = last_ready_row+2; i <= j; i++)
+		    {
+		      mpz_tdiv_r (remainder, 
+				  canonical->storage[(i-1)+
+						     (j-1)*rows], 
+				  pivot);
+		  
+		      if(mpz_cmp_si(remainder, 0) !=0)
+			{
+			  row_undivisible = i;
+			  /* col_undivisible = j; */
+			}
+		    }
+		}
+
+	      /* printf("Row undivisible %i\n", row_undivisible); */
+
+	      /* If a relative prime was found from the Schur complement,  
+		 add that row to the first row of the Schur complement */
+	      if(row_undivisible != 0)
+		{
+		  mpz_set_si (remainder, 1);
+		  gmp_matrix_add_row(remainder, 
+				     row_undivisible, last_ready_row+1, 
+				     canonical);
+
+		  /* [ 1 0] [1 0] = [1 0]
+		     [-1 1] [1 1]   [0 1] */
+
+		  if(left_inverted == INVERTED)
+		    {
+		      gmp_matrix_add_row(remainder, 
+					 row_undivisible, last_ready_row+1, 
+					 new_nf->left);
+		    }
+		  else
+		    {
+		      mpz_neg (remainder, remainder);
+		      gmp_matrix_add_col(remainder, 
+					 last_ready_row+1, row_undivisible,
+					 new_nf->left);
+		    }
+		}
+	      /* The Schur complement is smaller by one again */
+	      else
+		{
+		  last_ready_row++;
+		}
+	    }
+	} 
+    }  /* The main loop ends here */
+  
+  mpz_clear(pivot);
+  mpz_clear(remainder);
+  return new_nf;
+}
+
+int destroy_gmp_normal_form(gmp_normal_form * nf)
+{
+  int status;
+
+  if(nf == NULL)
+    {
+      return EXIT_FAILURE;
+    }
+
+  status = EXIT_SUCCESS;
+  if(destroy_gmp_matrix(nf -> left) == EXIT_FAILURE)
+    {
+      status = EXIT_FAILURE;
+    }
+
+  if(destroy_gmp_matrix(nf -> canonical) == EXIT_FAILURE)
+    {
+      status = EXIT_FAILURE;
+    }
+
+  if(destroy_gmp_matrix(nf -> right) == EXIT_FAILURE)
+    {
+      status = EXIT_FAILURE;      
+    }
+  free(nf);
+
+  return status;
+}
+
+
diff --git a/contrib/kbipack/gmp_normal_form.h b/contrib/kbipack/gmp_normal_form.h
new file mode 100755
index 0000000000..3250646a65
--- /dev/null
+++ b/contrib/kbipack/gmp_normal_form.h
@@ -0,0 +1,154 @@
+/* 
+   Header file for integer computation of Hermite and Smith normal 
+   forms of matrices of modest size. 
+
+   Implementation: Dense matrix with GMP-library's mpz_t elements to 
+                   hold huge integers. 
+
+   Algorithm: Kannan - Bachem algorithm with improvement by
+              Chou and Collins. Expects a large number of unit invariant 
+	      factors and takes advantage of them as they appear. 
+
+   References: 
+    [1] Ravindran Kannan, Achim Bachem: 
+        "Polynomial algorithms for computing the Smith and Hermite normal 
+	forms of an integer matrix", 
+	SIAM J. Comput., vol. 8, no. 5, pp. 499-507, 1979.
+    [2] Tsu-Wu J.Chou, George E. Collins: 
+        "Algorithms for the solution of systems of linear Diophantine 
+	equations", 
+	SIAM J. Comput., vol. 11, no. 4, pp. 687-708, 1982.
+    [3] GMP homepage http://www.swox.com/gmp/
+    [4] GNU gmp page http://www.gnu.org/software/gmp/
+
+
+   Copyright (C) 30.10.2003Saku Suuriniemi  TUT/CEM
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   Saku Suuriniemi, TUT/Electromagetics
+   P.O.Box 692, FIN-33101 Tampere, Finland
+   saku.suuriniemi@tut.fi
+
+   $Id: gmp_normal_form.h,v 1.1 2009-03-30 14:10:57 matti Exp $
+*/
+
+#ifndef _GMP_NORMAL_FORM_
+#define _GMP_NORMAL_FORM_
+
+#include"gmp_blas.h"
+#include"gmp_matrix.h"
+
+typedef enum {NOT_INVERTED, INVERTED} inverted_flag;
+
+typedef struct
+{
+  gmp_matrix * left;
+  gmp_matrix * canonical;
+  gmp_matrix * right;
+  inverted_flag left_inverted;
+  inverted_flag right_inverted;
+} gmp_normal_form;
+
+/* For efficiency, the routines assume responsibility for the input matrices. 
+   Do *not* destroy them yourself! */
+
+/***********************/
+/* Hermite normal form */
+/***********************/
+/* 
+   PA = LU, 
+
+   left:
+   o P permutation matrix 
+     - Use "left_inverted = INVERTED" for left = P, 
+       and "left_inverted = NOT_INVERTED"  for left = P^T, i.e. for the 
+       decomposition 
+                      A = P^T L U.
+   canonical:
+   o L lower triangular, 
+     - diagonal entries positive, 
+     - subdiagonal entries positive 
+     - subdiagonal entries smaller than the diagonal entry of their row 
+
+   right:
+   o U unimodular,
+     - Use "right_inverted = NOT_INVERTED" for right = U, i.e. for the 
+       decomposition 
+                      P A = L U, 
+       and "right_inverted = INVERTED" for right = inv(U), i.e. for the 
+       decompositions 
+                      P A inv(U) = L and  A inv(U) = P^T L. 
+
+   Algorithm: Kannan-Bachem algorithm with improved (by Chou & Collins) 
+              reduction phase.
+
+   References: 
+    [1] Ravindran Kannan, Achim Bachem: 
+        "Polynomial algorithms for computing the Smith and Hermite normal 
+	forms of an integer matrix", 
+	SIAM J. Comput., vol. 8, no. 5, pp. 499-507, 1979.
+    [2] Tsu-Wu J.Chou, George E. Collins: 
+        "Algorithms for the solution of systems of linear Diophantine 
+	equations", 
+	SIAM J. Comput., vol. 11, no. 4, pp. 687-708, 1982.
+*/
+
+gmp_normal_form * 
+create_gmp_Hermite_normal_form(gmp_matrix * A, 
+			       inverted_flag left_inverted,
+			       inverted_flag right_inverted);
+
+
+
+/*********************/
+/* Smith normal form */
+/*********************/
+/* 
+   A = USV, 
+
+   left:
+   o U unimodular factor matrix 
+     - Use "left_inverted = NOT_INVERTED"  for left = U and 
+       "left_inverted = INVERTED" for left = inv(U), i.e. for 
+       the decomposition 
+                          inv(U) A = S V.
+
+   canonical:
+   o S diagonal, 
+     - first k diagonal entries positive (invariant factors of A), 
+       rest zero 
+     - each positive diagonal entry divisible by the previous one
+
+   right:
+   o V unimodular,
+     - Use "right_inverted = NOT_INVERTED" for right = V, 
+       and "right_inverted = INVERTED" for right = inv(V), i.e. for the 
+       decompositions 
+                      A inv(V) = U S and  inv(U) A inv(V) = S. 
+
+   Algorithm: Successive Hermite normal forms by Kannan-Bachem algorithm 
+              with improved (by Chou & Collins) reduction phase. See 
+	      reference [1].
+*/
+
+gmp_normal_form *
+create_gmp_Smith_normal_form(gmp_matrix * A,
+			     inverted_flag left_inverted,
+			     inverted_flag right_inverted);
+
+int destroy_gmp_normal_form(gmp_normal_form*);
+
+#endif
diff --git a/contrib/kbipack/gmp_normal_form.o b/contrib/kbipack/gmp_normal_form.o
new file mode 100755
index 0000000000000000000000000000000000000000..f165a904233fc099325f1bb789f948c5207669de
GIT binary patch
literal 16308
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
SLZ1~H<){o?T;w|r{r)Eq1>N}o

literal 0
HcmV?d00001

diff --git a/contrib/kbipack/kbihnf.m b/contrib/kbipack/kbihnf.m
new file mode 100755
index 0000000000..f04977ca95
--- /dev/null
+++ b/contrib/kbipack/kbihnf.m
@@ -0,0 +1,58 @@
+function [P, L, U] = kbihnf(A)
+
+% [P, L, U] = kbihnf(A)
+%
+% Computes the integer Hermite normal form L such that 
+% 
+%   P*A = L*U
+% 
+% Uses external Kannan-Bachem implementation
+
+%    Copyright (C) 3.11.2003 Saku Suuriniemi TUT/CEM
+%
+%    This program is free software; you can redistribute it and/or modify
+%    it under the terms of the GNU General Public License as published by
+%    the Free Software Foundation; either version 2 of the License, or
+%    any later version.
+%
+%    This program is distributed in the hope that it will be useful,
+%    but WITHOUT ANY WARRANTY; without even the implied warranty of
+%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%    GNU General Public License for more details.
+%
+%    You should have received a copy of the GNU General Public License
+%    along with this program; if not, write to the Free Software
+%    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+%   Saku Suuriniemi, TUT/Electromagetics
+%   P.O.Box 692, FIN-33101 Tampere, Finland
+%   saku.suuriniemi@tut.fi
+
+  [i,j,v] = find(A);
+
+  fid = fopen('hnf_temp.crd', 'w');
+  fprintf(fid, '%g %g %g\n', size(A,1), size(A,2), length(i));
+  if ~isempty(v)
+    fprintf(fid, '%g %g %g\n', [i'; j'; v']);
+  end
+  fclose(fid);
+
+  system ("./compute_normal_form -Hl hnf_temp.crd");
+
+  load hnf_temp.left;
+  P = zeros(hnf_temp(1,1), hnf_temp(1,2));
+  for i = 2:size(hnf_temp,1)
+    P(hnf_temp(i,1), hnf_temp(i,2)) = hnf_temp(i,3);
+  end
+
+  load hnf_temp.can;
+  L = zeros(hnf_temp(1,1), hnf_temp(1,2));
+  for i = 2:size(hnf_temp,1)
+    L(hnf_temp(i,1), hnf_temp(i,2)) = hnf_temp(i,3);
+  end
+
+  load hnf_temp.right;
+  U = zeros(hnf_temp(1,1), hnf_temp(1,2));
+  for i = 2:size(hnf_temp,1)
+    U(hnf_temp(i,1), hnf_temp(i,2)) = hnf_temp(i,3);
+  end
diff --git a/contrib/kbipack/kbisnf.m b/contrib/kbipack/kbisnf.m
new file mode 100755
index 0000000000..066509dceb
--- /dev/null
+++ b/contrib/kbipack/kbisnf.m
@@ -0,0 +1,58 @@
+function [U, S, V] = kbisnf(A)
+
+% [U, S, V] = kbisnf(A)
+%
+% Computes the integer Smith normal form S such that 
+% 
+%   A = U*S*V
+% 
+% Uses external Kannan-Bachem implementation
+
+%    Copyright (C) 4.11.2003 Saku Suuriniemi TUT/CEM
+%
+%    This program is free software; you can redistribute it and/or modify
+%    it under the terms of the GNU General Public License as published by
+%    the Free Software Foundation; either version 2 of the License, or
+%    any later version.
+%
+%    This program is distributed in the hope that it will be useful,
+%    but WITHOUT ANY WARRANTY; without even the implied warranty of
+%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%    GNU General Public License for more details.
+%
+%    You should have received a copy of the GNU General Public License
+%    along with this program; if not, write to the Free Software
+%    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+%   Saku Suuriniemi, TUT/Electromagetics
+%   P.O.Box 692, FIN-33101 Tampere, Finland
+%   saku.suuriniemi@tut.fi
+
+  [i,j,v] = find(A);
+
+  fid = fopen('snf_temp.crd', 'w');
+  fprintf(fid, '%g %g %g\n', size(A,1), size(A,2), length(i));
+  if ~isempty(v)
+    fprintf(fid, '%g %g %g\n', [i'; j'; v']);
+  end
+  fclose(fid);
+
+  system ('./compute_normal_form -S snf_temp.crd');
+
+  load snf_temp.left;
+  U = zeros(snf_temp(1,1), snf_temp(1,2));
+  for i = 2:size(snf_temp,1)
+     U(snf_temp(i,1), snf_temp(i,2)) = snf_temp(i,3);
+  end
+
+  load snf_temp.can;
+  S = zeros(snf_temp(1,1), snf_temp(1,2));
+  for i = 2:size(snf_temp,1)
+    S(snf_temp(i,1), snf_temp(i,2)) = snf_temp(i,3);
+  end
+
+  load snf_temp.right;
+  V = zeros(snf_temp(1,1), snf_temp(1,2));
+  for i = 2:size(snf_temp,1)
+    V(snf_temp(i,1), snf_temp(i,2)) = snf_temp(i,3);
+  end
diff --git a/contrib/kbipack/licence b/contrib/kbipack/licence
new file mode 100755
index 0000000000..5b6e7c66c2
--- /dev/null
+++ b/contrib/kbipack/licence
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/contrib/kbipack/readme b/contrib/kbipack/readme
new file mode 100755
index 0000000000..3fbf20be8b
--- /dev/null
+++ b/contrib/kbipack/readme
@@ -0,0 +1,106 @@
+-----------
+KBIPack 1.0
+-----------
+
+This is an ANSI C-implementation of Kannan-Bachem algorithms for Hermite 
+and Smith normal forms for integer matrices. I wrote it to be the core of 
+an integer homology group solver. You are welcome to improve this package, 
+but I am unfortunately unable to invest much more time to it myself. The 
+license is GNU GPL (see below). 
+
+The algorithms are Kannan - Bachem algorithms with improvement by Chou and 
+Collins. The Smith normal form routine expects a large number of unit 
+invariant factors (this occurs in my application) and takes advantage of 
+them as they appear. 
+
+References: 
+-----------
+[1] Ravindran Kannan, Achim Bachem: 
+    "Polynomial algorithms for computing the Smith and Hermite normal 
+    forms of an integer matrix", 
+    SIAM J. Comput., vol. 8, no. 5, pp. 499-507, 1979.
+[2] Tsu-Wu J.Chou, George E. Collins: 
+    "Algorithms for the solution of systems of linear Diophantine equations", 
+     SIAM J. Comput., vol. 11, no. 4, pp. 687-708, 1982.
+[3] GMP homepage http://www.swox.com/gmp/
+[4] GNU gmp page http://www.gnu.org/software/gmp/
+
+Technicalities:
+---------------
+The package relies on GNU multiple precision library gmp to represent large 
+integers. You must have gmp installed before you compile. 
+
+It should run smoothly on UNIX systems with working standard I/O, and 
+in Cygwin (http://www.cygwin.com/). 
+
+You can use this package in at least three ways: 
+1) You can call the functions bkihnf and bkisnf in respective .m-files 
+   from MatLab or Octave. Note that the numbers in the results may be too 
+   large to be exactly read back by them. 
+2) You can use the executable "compute normal form" just like any command. 
+   Use the -h flag to get info. 
+3) You can use just the core routines from your own application. 
+
+Contents: 
+---------
+bkihnf.m                 Integer Hermite normal form for Octave/MatLab 
+bkisnf.m                 Integer Smith normal form for Octave/MatLab 
+compute_normal_form.c    A main program for a comand-line version 
+gmp_blas.c               Low-level routines for integer arrays 
+gmp_blas.h
+gmp_matrix.c             Integer matrix type
+gmp_matrix.h
+gmp_matrix_io.c          I/O for integer matrices
+gmp_matrix_io.h
+gmp_normal_form.c        The core computation routine
+gmp_normal_form.h        
+LICENCE                  The GNU General Public License version 2
+Makefile                 A rudimentary Makefile (just type "make")
+README                   This file
+
+Installation: 
+-------------
+First install gmp (http://www.swox.com/gmp/ or a binary package).
+If you use gcc, just type "make". If not, alter the Makefile as necessary. 
+
+Licensing:
+----------
+Copyright (C) 2005 Saku Suuriniemi
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Saku Suuriniemi, TUT/Electromagetics
+P.O.Box 692, FIN-33101 Tampere, Finland
+saku.suuriniemi@tut.fi
+http://www.em.tut.fi/Eng/
+
+Background:
+-----------
+The package is a part of my PhD project, so if you find this package useful 
+and wish to refer to my thesis (also available on my homepage), here is the 
+BiBTeX entry: 
+
+@phdthesis{Suuriniemi:PhD2004,
+author="Saku Suuriniemi",
+school="Tampere University of Technology",
+title="Homological computations in electromagnetic modeling",
+address="Tampere",
+year=2004,
+isbn="952-15-1237-7"
+}
+
+Thanks: 
+-------
+Thanks are due to Chistophe Geuzaine for testing and comments. 
diff --git a/utils/misc/celldriver.cpp b/utils/misc/celldriver.cpp
new file mode 100755
index 0000000000..c8b91e03a9
--- /dev/null
+++ b/utils/misc/celldriver.cpp
@@ -0,0 +1,63 @@
+// configure, compile and install Gmsh as a library with
+//
+//   ./configure --disable-gui --disable-netgen --disable-chaco 
+//        --disable-metis --disable-tetgen --prefix=/usr/local/
+//   make install-lib
+//
+// Then compile this driver with "g++ kaka.cpp -lGmsh -llapack -lblas -lgmp"
+
+#include <stdio.h>
+#include <gmsh/Gmsh.h>
+#include <gmsh/GModel.h>
+#include <gmsh/MElement.h>
+#include <gmsh/CellComplex.h>
+
+
+int main(int argc, char **argv)
+{
+  GmshInitialize(argc, argv);
+  GModel *m = new GModel();
+  m->readGEO("transformer.geo");
+  m->mesh(3);
+  m->writeMSH("transformer.msh");
+  
+  printf("This model has: %d GRegions, %d GFaces, %d GEdges and %d GVertices. \n" , m->getNumRegions(), m->getNumFaces(), m->getNumEdges(), m->getNumVertices());
+  
+  std::vector<GEntity*> domain;
+  std::vector<GEntity*> subdomain;
+  
+  // whole model
+  for(GModel::riter rit = m->firstRegion(); rit != m->lastRegion(); rit++){
+    GEntity *r = *rit;
+    domain.push_back(r);
+  }
+
+  // the ports
+  GModel::fiter sub = m->firstFace();
+  GEntity *s = *sub;
+  subdomain.push_back(s);
+  s = *(++sub);
+  subdomain.push_back(s);
+  s =*(++sub);
+  subdomain.push_back(s);
+  s= *(++sub);
+  subdomain.push_back(s);
+  
+  CellComplex complex = CellComplex(domain, subdomain);
+
+  printf("Cell complex of this model has: %d volumes, %d faces, %d edges and %d vertices\n",
+         complex.getSize(3), complex.getSize(2), complex.getSize(1), complex.getSize(0));  
+  
+  complex.reduceComplex();
+  complex.writeComplexMSH("reduced_complex.msh");
+  complex.coreduceComplex();
+  complex.writeComplexMSH("coreduced_complex.msh");
+    
+  delete m;
+  GmshFinalize();
+  
+}
+
+
+
+
diff --git a/utils/misc/transformer.geo b/utils/misc/transformer.geo
new file mode 100755
index 0000000000..9021d10d66
--- /dev/null
+++ b/utils/misc/transformer.geo
@@ -0,0 +1,99 @@
+// Gmsh project created on Thu Mar 26 10:01:45 2009
+
+m=0.5;
+
+Point(newp) = {0, 0, 0, m};
+Point(newp) = {10, 0, 0, m};
+Point(newp) = {10, 10, 0, m};
+Point(newp) = {0, 10, 0, m};
+Point(newp) = {4, 4, 0, m};
+Point(newp) = {6, 4, 0, m};
+Point(newp) = {6, 6, 0, m};
+Point(newp) = {4, 6, 0, m};
+
+Point(newp) = {2, 0, 0, m};
+Point(newp) = {8, 0, 0, m};
+Point(newp) = {2, 10, 0, m};
+Point(newp) = {8, 10, 0, m};
+
+Point(newp) = {0, 0, 1, m};
+Point(newp) = {10, 0, 1, m};
+Point(newp) = {10, 10, 1, m};
+Point(newp) = {0, 10, 1, m};
+Point(newp) = {4, 4, 1, m};
+Point(newp) = {6, 4, 1, m};
+Point(newp) = {6, 6, 1, m};
+Point(newp) = {4, 6, 1, m};
+
+Point(newp) = {2, 0, 1, m};
+Point(newp) = {8, 0, 1, m};
+Point(newp) = {2, 10, 1, m};
+Point(newp) = {8, 10, 1, m};
+Line(1) = {16, 23};
+Line(2) = {23, 11};
+Line(3) = {11, 4};
+Line(4) = {4, 16};
+Line(5) = {24, 12};
+Line(6) = {12, 3};
+Line(7) = {3, 15};
+Line(8) = {15, 24};
+Line(9) = {10, 2};
+Line(10) = {2, 14};
+Line(11) = {14, 22};
+Line(12) = {22, 10};
+Line(13) = {21, 9};
+Line(14) = {9, 1};
+Line(15) = {1, 13};
+Line(16) = {13, 21};
+Line Loop(17) = {3, 4, 1, 2};
+Ruled Surface(18) = {17};
+Line Loop(19) = {6, 7, 8, 5};
+Ruled Surface(20) = {19};
+Line Loop(21) = {9, 10, 11, 12};
+Ruled Surface(22) = {21};
+Line Loop(23) = {14, 15, 16, 13};
+Ruled Surface(24) = {23};
+Line(25) = {16, 13};
+Line(26) = {1, 4};
+Line(27) = {11, 12};
+Line(28) = {24, 23};
+Line(29) = {21, 22};
+Line(30) = {10, 9};
+Line(31) = {2, 3};
+Line(32) = {15, 14};
+Line(33) = {20, 19};
+Line(34) = {19, 18};
+Line(35) = {18, 17};
+Line(36) = {17, 20};
+Line(37) = {8, 7};
+Line(38) = {7, 6};
+Line(39) = {6, 18};
+Line(40) = {5, 6};
+Line(41) = {5, 8};
+Line(42) = {20, 8};
+Line(43) = {17, 5};
+Line(44) = {19, 7};
+Line Loop(45) = {27, -5, 28, 2};
+Ruled Surface(46) = {45};
+Line Loop(47) = {25, -15, 26, 4};
+Ruled Surface(48) = {47};
+Line Loop(49) = {29, 12, 30, -13};
+Ruled Surface(50) = {49};
+Line Loop(51) = {32, -10, 31, 7};
+Ruled Surface(52) = {51};
+Line Loop(53) = {41, -42, -36, 43};
+Ruled Surface(54) = {53};
+Line Loop(55) = {35, 43, 40, 39};
+Ruled Surface(56) = {55};
+Line Loop(57) = {34, -39, -38, -44};
+Ruled Surface(58) = {57};
+Line Loop(59) = {33, 44, -37, -42};
+Ruled Surface(60) = {59};
+Line Loop(61) = {28, -1, 25, 16, 29, -11, -32, 8};
+Line Loop(62) = {33, 34, 35, 36};
+Ruled Surface(63) = {61, 62};
+Line Loop(64) = {3, -26, -14, -30, 9, 31, -6, -27};
+Line Loop(65) = {37, 38, -40, 41};
+Ruled Surface(66) = {64, 65};
+Surface Loop(67) = {63, 46, 66, 18, 48, 24, 50, 22, 52, 20, 54, 60, 58, 56};
+Volume(68) = {67};
-- 
GitLab