diff --git a/Geo/CellComplex.cpp b/Geo/CellComplex.cpp
index 3befc1ef002de272d24932a7998af410d0a5bb40..5d17436d71154629fd7a492d10a75a2dddd3888f 100644
--- a/Geo/CellComplex.cpp
+++ b/Geo/CellComplex.cpp
@@ -403,13 +403,13 @@ void CellComplex::coreduceComplex(int generatorDim, std::set<Cell*, Less_Cell>&
   return;
   
 }
-void CellComplex::coreduceComplex(){
+void CellComplex::coreduceComplex(int generatorDim){
   
   std::set<Cell*, Less_Cell> generatorCells[4];
   
   printf("Cell complex before coreduction: %d volumes, %d faces, %d edges and %d vertices.\n",
          getSize(3), getSize(2), getSize(1), getSize(0));
-  for(int i = 0; i < 4; i++){
+  for(int i = 0; i < 4; i++){    
     while (getSize(i) != 0){
       citer cit = firstCell(i);
       Cell* cell = *cit;
@@ -423,6 +423,7 @@ void CellComplex::coreduceComplex(){
       coreduction(1);
       coreduction(2);
     }
+    if(generatorDim == i) break;
     
   }
   for(int i = 0; i < 4; i++){
@@ -436,65 +437,6 @@ void CellComplex::coreduceComplex(){
   
   return;
 }
-  
-
-#if defined(HAVE_KBIPACK)
-
-std::vector<gmp_matrix*> CellComplex::constructHMatrices(){
-  
-  // h_dim: C_dim -> C_(dim-1)
-  
-  std::vector<gmp_matrix*> HMatrix;
-
-  HMatrix.push_back(NULL);
-
-  for(int dim = 1; dim < 4; dim++){
-    unsigned int cols = _cells[dim].size();
-    unsigned int rows =  _cells[dim-1].size();
-    
-    for(citer cit = firstCell(dim); cit != lastCell(dim); cit++){
-      Cell* cell = *cit;
-      if(cell->inSubdomain()) cols--;
-    }
-    for(citer cit = firstCell(dim-1); cit != lastCell(dim-1); cit++){
-      Cell* cell = *cit;
-      if(cell->inSubdomain()) rows--;
-    }
-    
-    
-    if( cols == 0 ){
-      HMatrix.push_back(NULL);
-    }
-    else if( rows == 0){
-      HMatrix.push_back(create_gmp_matrix_zero(1, cols));
-    }
-    else{
-      long int elems[rows*cols];
-      
-      citer high = firstCell(dim);
-      citer low = firstCell(dim-1);
-
-      unsigned int i = 0;
-      while(i < rows*cols){
-        while(low != lastCell(dim-1)){      
-          Cell* lowcell = *low;
-          Cell* highcell = *high;
-          if(!(highcell->inSubdomain() || lowcell->inSubdomain())){
-            elems[i] = kappa(*high, *low);
-            i++;
-          }
-          low++;
-        }
-        low = firstCell(dim-1);
-        high++;
-      }
-      HMatrix.push_back(create_gmp_matrix_int(rows, cols, elems));
-      
-    }
-  }
-  return HMatrix; 
-}
-#endif
 
 void CellComplex::getDomainVertices(std::set<MVertex*, Less_MVertex>& domainVertices, bool subdomain){
 
@@ -553,6 +495,7 @@ 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());
+    printf("Unable to open file.");
     return 0;
   }
   
@@ -617,242 +560,5 @@ void CellComplex::printComplex(int dim, bool subcomplex){
   }
 }
 
-#if defined(HAVE_KBIPACK)
-
-void ChainComplex::KerCod(int dim){
-  
-  if(dim < 1 || dim > 3 || _HMatrix[dim] == NULL) return;
-  
-  gmp_matrix* HMatrix = copy_gmp_matrix(_HMatrix[dim], 1, 1, 
-                                        gmp_matrix_rows(_HMatrix[dim]), gmp_matrix_cols(_HMatrix[dim]));
-  
-  gmp_normal_form* normalForm = create_gmp_Hermite_normal_form(HMatrix, NOT_INVERTED, INVERTED);
-  //printMatrix(normalForm->left);
-  //printMatrix(normalForm->canonical);
-  //printMatrix(normalForm->right);
-  
-  int minRowCol = std::min(gmp_matrix_rows(normalForm->canonical), gmp_matrix_cols(normalForm->canonical));
-  int rank = 0;
-  mpz_t elem;
-  mpz_init(elem);
-  
-  while(rank < minRowCol){
-    gmp_matrix_get_elem(elem, rank+1, rank+1, normalForm->canonical);
-    if(mpz_cmp_si(elem,0) == 0) break;
-    rank++;
-  }
-  
-  if(rank != gmp_matrix_cols(normalForm->canonical)){
-    _kerH[dim] = copy_gmp_matrix(normalForm->right, 1, rank+1, 
-                                 gmp_matrix_rows(normalForm->right),  gmp_matrix_cols(normalForm->right));
-  }
-  
-  if(rank > 0){
-     _codH[dim] = copy_gmp_matrix(normalForm->canonical, 1, 1,
-                                  gmp_matrix_rows(normalForm->canonical), rank);
-    gmp_matrix_left_mult(normalForm->left, _codH[dim]);
-  }
-  
-  mpz_clear(elem);
-  destroy_gmp_normal_form(normalForm);
-  
-  return;
-}
-
-//j:B_(k+1)->Z_k
-void ChainComplex::Inclusion(int dim){
-  
-  if(dim < 1 || dim > 2 || _kerH[dim] == NULL || _codH[dim+1] == NULL) return;
-  
-  gmp_matrix* Zbasis = copy_gmp_matrix(_kerH[dim], 1, 1,
-                                       gmp_matrix_rows(_kerH[dim]), gmp_matrix_cols(_kerH[dim]));
-  gmp_matrix* Bbasis = copy_gmp_matrix(_codH[dim+1], 1, 1,
-                                       gmp_matrix_rows(_codH[dim+1]), gmp_matrix_cols(_codH[dim+1]));
-  
-  int rows = gmp_matrix_rows(Zbasis);
-  int cols = gmp_matrix_cols(Zbasis);
-  if(rows < cols) return;
-  
-  rows = gmp_matrix_rows(Bbasis);
-  cols = gmp_matrix_cols(Bbasis);
-  if(rows < cols) return;
-  
-  // A*inv(V) = U*S
-  gmp_normal_form* normalForm = create_gmp_Smith_normal_form(Zbasis, INVERTED, INVERTED);
-  
-  mpz_t elem;
-  mpz_init(elem);
-  
-  for(int i = 1; i <= cols; i++){
-    gmp_matrix_get_elem(elem, i, i, normalForm->canonical);
-    if(mpz_cmp_si(elem,0) == 0){
-      destroy_gmp_normal_form(normalForm);
-      return;
-    }
-  }
-  
-  gmp_matrix_left_mult(normalForm->left, Bbasis);
-  
-  
-  
-  gmp_matrix* LB = copy_gmp_matrix(Bbasis, 1, 1, gmp_matrix_cols(Zbasis), gmp_matrix_cols(Bbasis));
-  destroy_gmp_matrix(Bbasis);
-  
-  rows = gmp_matrix_rows(LB);
-  cols = gmp_matrix_cols(LB);
-  
-  mpz_t divisor;
-  mpz_init(divisor);
-  mpz_t remainder;
-  mpz_init(remainder);
-  mpz_t result;
-  mpz_init(result);
-  
-  for(int i = 1; i <= rows; i++){
-    gmp_matrix_get_elem(divisor, i, i, normalForm->canonical);
-    for(int j = 1; j <= cols; j++){
-      gmp_matrix_get_elem(elem, i, j, LB);
-      mpz_cdiv_qr(result, remainder, elem, divisor);
-      if(mpz_cmp_si(remainder, 0) == 0){
-        gmp_matrix_set_elem(result, i, j, LB);
-      }
-      else return;
-    }
-  }
-  
-  gmp_matrix_left_mult(normalForm->right, LB);
-  
-  _JMatrix[dim] = LB;
-  
-  mpz_clear(elem);
-  mpz_clear(divisor);
-  mpz_clear(result);
-  destroy_gmp_normal_form(normalForm);
-  
-  return;
-  
-}
-
-void ChainComplex::Quotient(int dim){
-  
-  if(dim < 1 || dim > 3 || _JMatrix[dim] == NULL) return;
-  
-  gmp_matrix* JMatrix = copy_gmp_matrix(_JMatrix[dim], 1, 1,
-                                        gmp_matrix_rows(_JMatrix[dim]), gmp_matrix_cols(_JMatrix[dim]));
-  int rows = gmp_matrix_rows(JMatrix);
-  int cols = gmp_matrix_cols(JMatrix);
-  
-  gmp_normal_form* normalForm = create_gmp_Smith_normal_form(JMatrix, NOT_INVERTED, NOT_INVERTED);
-  
-  mpz_t elem;
-  mpz_init(elem);
-  
-  for(int i = 1; i <= cols; i++){
-    gmp_matrix_get_elem(elem, i, i, normalForm->canonical);
-    if(mpz_cmp_si(elem,0) == 0){
-      destroy_gmp_normal_form(normalForm);
-      return;
-    }
-    if(mpz_cmp_si(elem,1) > 0){
-      _torsion[dim].push_back(mpz_get_si(elem));
-    }
-  }
-  
-  int rank = cols - _torsion[dim].size();
-  if(rows - rank > 0){
-    gmp_matrix* Hbasis = copy_gmp_matrix(normalForm->left, 1, rank+1, rows, rows);
-    _QMatrix[dim] = Hbasis;
-  }
-  
-  mpz_clear(elem);
-  destroy_gmp_normal_form(normalForm);
-  return; 
-}
 
-void ChainComplex::computeHomology(){
   
-  for(int i=0; i < 4; i++){
-    
-    KerCod(i+1);
-    
-    // 1) no edges, but zero cells
-    if(i == 0 &&  gmp_matrix_cols(getHMatrix(0)) > 0 && getHMatrix(1) == NULL) {
-      _Hbasis[i] = create_gmp_matrix_identity(gmp_matrix_cols(getHMatrix(0)));
-    }
-    
-    // 2) this dimension is empty
-    else if(getHMatrix(i) == NULL && getHMatrix(i+1) == NULL){
-      _Hbasis[i] = NULL;
-    }
-    // 3) No higher dimension cells -> none of the cycles are boundaries
-    else if(getHMatrix(i+1) == NULL){
-      _Hbasis[i] = copy_gmp_matrix(getKerHMatrix(i), 1, 1,
-                                   gmp_matrix_rows(getKerHMatrix(i)), gmp_matrix_cols(getKerHMatrix(i)));
-    }
-   
-    // 5) General case:
-    //   1) Find the bases of boundaries B and cycles Z 
-    //   2) find j: B -> Z and
-    //   3) find quotient Z/j(B) 
-    else {
-      
-      // 4) No lower dimension cells -> all chains are cycles
-      if(getHMatrix(i) == NULL){
-        _kerH[i] = create_gmp_matrix_identity(gmp_matrix_rows(getHMatrix(i+1))); 
-      }
-      
-      Inclusion(i);
-      Quotient(i);
-      
-      if(getCodHMatrix(i+1) == NULL){
-        _Hbasis[i] = getKerHMatrix(i);
-      }
-      else if(getJMatrix(i) == NULL || getQMatrix(i) == NULL){
-        _Hbasis[i] = NULL;
-      }
-      else{
-        _Hbasis[i] = copy_gmp_matrix(getKerHMatrix(i), 1, 1, 
-                                     gmp_matrix_rows(getKerHMatrix(i)), gmp_matrix_cols(getKerHMatrix(i)));
-        gmp_matrix_right_mult(_Hbasis[i], getQMatrix(i));
-      }
-    }
-    
-    destroy_gmp_matrix(_kerH[i]);
-    destroy_gmp_matrix(_codH[i]);
-    destroy_gmp_matrix(_JMatrix[i]);
-    destroy_gmp_matrix(_QMatrix[i]);
-    
-    _kerH[i] = NULL;
-    _codH[i] = NULL;
-    _JMatrix[i] = NULL;
-    _QMatrix[i] = NULL;
-   
-  }
-  
-  
-  return;
-}
-
-
-void ChainComplex::matrixTest(){
-  
-  int rows = 3;
-  int cols = 6;
-  
-  long int elems[rows*cols];
-  for(int i = 1; i<=rows*cols; i++) elems[i-1] = i;
-  
-  gmp_matrix* matrix = create_gmp_matrix_int(rows, cols, elems);
-  
-  gmp_matrix* copymatrix = copy_gmp_matrix(matrix, 3, 2, 3, 5);
-  
-  printMatrix(matrix);
-  printMatrix(copymatrix);
-  
-  return; 
-}
-
-
-#endif
-
-
diff --git a/Geo/CellComplex.h b/Geo/CellComplex.h
index dbcfc66cf42026a2564130f55f203c45601c8f77..9cf34a2a59f32cb51129f360572d3596efdd0a93 100644
--- a/Geo/CellComplex.h
+++ b/Geo/CellComplex.h
@@ -21,15 +21,6 @@
 #include "GFace.h"
 #include "GVertex.h"
 
-#if defined(HAVE_KBIPACK)
-
-#include "gmp.h"
-extern "C" {
-  #include "gmp_normal_form.h" // perhaps make c++ headers instead?
-}
-
-#endif
-
 // Abstract class representing a cell of a cell complex.
 class Cell
 {  
@@ -340,11 +331,7 @@ class CellComplex
    // one for each dimension
    std::set<Cell*, Less_Cell>  _cells[4];
    
-   // boundary operator matrices for this chain complex
-   // h_k: C_k -> C_(k-1)
-   //gmp_matrix* _HMatrix[4];
-   
- public:
+  public:
    // iterator for the cells of same dimension
    typedef std::set<Cell*, Less_Cell>::iterator citer;
    
@@ -380,6 +367,8 @@ class CellComplex
    // get the number of certain dimensional cells
    virtual int getSize(int dim){ return _cells[dim].size(); }
    
+   virtual std::set<Cell*, Less_Cell> getCells(int dim){ return _cells[dim]; }
+   
    // 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(); }
@@ -421,7 +410,8 @@ class CellComplex
    
    // useful functions for (co)reduction of cell complex
    virtual void reduceComplex();
-   virtual void coreduceComplex();
+   // coreduction up to generators of dimension generatorDim
+   virtual void coreduceComplex(int generatorDim=3);
    
    // stores cells removed after removal of generatos of dimension generatorDim
    virtual void coreduceComplex(int generatorDim, std::set<Cell*, Less_Cell>& removedCells);
@@ -437,97 +427,7 @@ class CellComplex
    // write this cell complex in legacy .msh format
    virtual int writeComplexMSH(const std::string &name); 
    
-    // 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]; }
-  
-   
-#if defined(HAVE_KBIPACK)   
-   // construct boundary operator matrices of this cell complex
-   // used to construct a chain complex
-   virtual std::vector<gmp_matrix*> constructHMatrices();
-#endif
-   
-};
-
-#if defined(HAVE_KBIPACK)
-// A class representing a chain complex of a cell complex.
-// This should only be constructed for a reduced cell complex because of
-// dense matrix representations and great computational complexity in its methods.
-class ChainComplex{
-  private:
-   // boundary operator matrices for this chain complex
-   // h_k: C_k -> C_(k-1)
-   gmp_matrix* _HMatrix[4];
-   
-   // Basis matrices for the kernels and codomains of the boundary operator matrices
-   gmp_matrix* _kerH[4];
-   gmp_matrix* _codH[4];
-   
-   gmp_matrix* _JMatrix[4];
-   gmp_matrix* _QMatrix[4];
-   std::vector<long int> _torsion[4];
-   
-   // bases for homology groups
-   gmp_matrix* _Hbasis[4];
-   
-  public:
    
-   ChainComplex( std::vector<gmp_matrix*> HMatrix ){
-     for(int i = 0; i < HMatrix.size(); i++){
-       _HMatrix[i] = HMatrix.at(i);
-     }
-     for(int i = 0; i < 4; i++){
-       _kerH[i] = NULL;
-       _codH[i] = NULL;
-       _JMatrix[i] = NULL;
-       _QMatrix[i] = NULL;
-       _Hbasis[i] = NULL;
-     }
-     
-   }
-   ChainComplex(){
-     for(int i = 0; i < 4; i++){
-       _HMatrix[i] = create_gmp_matrix_zero(1,1);
-       _kerH[i] = NULL;
-       _codH[i] = NULL;
-       _JMatrix[i] = NULL;
-       _QMatrix[i] = NULL;
-       _Hbasis[i] = NULL;
-     }
-   }
-   virtual ~ChainComplex(){}
-   
-   // get the boundary operator matrix dim->dim-1
-   virtual gmp_matrix* getHMatrix(int dim) { if(dim > -1 || dim < 4) return _HMatrix[dim]; else return NULL;}
-   virtual gmp_matrix* getKerHMatrix(int dim) { if(dim > -1 || dim < 4) return _kerH[dim]; else return NULL;}
-   virtual gmp_matrix* getCodHMatrix(int dim) { if(dim > -1 || dim < 4) return _codH[dim]; else return NULL;}
-   virtual gmp_matrix* getJMatrix(int dim) { if(dim > -1 || dim < 4) return _JMatrix[dim]; else return NULL;}
-   virtual gmp_matrix* getQMatrix(int dim) { if(dim > -1 || dim < 4) return _QMatrix[dim]; else return NULL;}
-   virtual gmp_matrix* getHbasis(int dim) { if(dim > -1 || dim < 4) return _Hbasis[dim]; else return NULL;}
-   
-   // Compute basis for kernel and codomain of boundary operator matrix of dimension dim (ie. ker(h_dim) and cod(h_dim) )
-   virtual void KerCod(int dim);
-   // Compute matrix representation J for inclusion relation from dim-cells who are boundary of dim+1-cells 
-   // to cycles of dim-cells (ie. j: cod(h_(dim+1)) -> ker(h_dim) )
-   virtual void Inclusion(int dim);
-   // Compute quotient problem for given inclusion relation j to find representatives of homology groups
-   // and possible torsion coeffcients
-   virtual void Quotient(int dim);
-   
-   // Compute bases for the homology groups of this chain complex 
-   virtual void computeHomology();
-   
-   virtual int printMatrix(gmp_matrix* matrix){ 
-     printf("%d rows and %d columns\n", gmp_matrix_rows(matrix), gmp_matrix_cols(matrix)); 
-     return gmp_matrix_printf(matrix); } 
-     
-   // debugging aid
-   virtual void matrixTest();
 };
 
-
-#endif
-
 #endif
diff --git a/Geo/ChainComplex.cpp b/Geo/ChainComplex.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5bdcd8f5170c35654bbbdf9f15146b66f3731dae
--- /dev/null
+++ b/Geo/ChainComplex.cpp
@@ -0,0 +1,307 @@
+// 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.
+
+#include "ChainComplex.h"
+
+#if defined(HAVE_KBIPACK)
+
+ChainComplex::ChainComplex(CellComplex* cellComplex){
+  
+  _HMatrix[0] = NULL;
+  
+  for(int dim = 1; dim < 4; dim++){
+    unsigned int cols = cellComplex->getSize(dim);
+    unsigned int rows =  cellComplex->getSize(dim-1);
+    
+    for(std::set<Cell*, Less_Cell>::iterator cit = cellComplex->firstCell(dim); cit != cellComplex->lastCell(dim); cit++){
+      Cell* cell = *cit;
+      if(cell->inSubdomain()) cols--;
+    }
+    for(std::set<Cell*, Less_Cell>::iterator cit = cellComplex->firstCell(dim-1); cit != cellComplex->lastCell(dim-1); cit++){
+      Cell* cell = *cit;
+      if(cell->inSubdomain()) rows--;
+    }
+    
+    
+    if( cols == 0 ){
+      _HMatrix[dim] = NULL;
+    }
+    else if( rows == 0){
+      _HMatrix[dim] = create_gmp_matrix_zero(1, cols);
+    }
+    else{
+      
+      long int elems[rows*cols];
+      
+      std::set<Cell*, Less_Cell>::iterator high = cellComplex->firstCell(dim);
+      std::set<Cell*, Less_Cell>::iterator low = cellComplex->firstCell(dim-1);
+      
+      unsigned int i = 0;
+      while(i < rows*cols){
+        while(low != cellComplex->lastCell(dim-1)){
+          Cell* lowcell = *low;
+          Cell* highcell = *high;
+          if(!(highcell->inSubdomain() || lowcell->inSubdomain())){
+            elems[i] = cellComplex->kappa(*high, *low);
+            i++;
+          }
+          low++;
+        }
+        low = cellComplex->firstCell(dim-1);
+        high++;
+      }
+      _HMatrix[dim] = create_gmp_matrix_int(rows, cols, elems);      
+    }
+   
+    _kerH[dim] = NULL;
+    _codH[dim] = NULL;
+    _JMatrix[dim] = NULL;
+    _QMatrix[dim] = NULL;
+    _Hbasis[dim] = NULL; 
+  }
+  return;
+}
+
+
+void ChainComplex::KerCod(int dim){
+  
+  if(dim < 1 || dim > 3 || _HMatrix[dim] == NULL) return;
+  
+  gmp_matrix* HMatrix = copy_gmp_matrix(_HMatrix[dim], 1, 1, 
+                                        gmp_matrix_rows(_HMatrix[dim]), gmp_matrix_cols(_HMatrix[dim]));
+  
+  gmp_normal_form* normalForm = create_gmp_Hermite_normal_form(HMatrix, NOT_INVERTED, INVERTED);
+  //printMatrix(normalForm->left);
+  //printMatrix(normalForm->canonical);
+  //printMatrix(normalForm->right);
+  
+  int minRowCol = std::min(gmp_matrix_rows(normalForm->canonical), gmp_matrix_cols(normalForm->canonical));
+  int rank = 0;
+  mpz_t elem;
+  mpz_init(elem);
+  
+  while(rank < minRowCol){
+    gmp_matrix_get_elem(elem, rank+1, rank+1, normalForm->canonical);
+    if(mpz_cmp_si(elem,0) == 0) break;
+    rank++;
+  }
+  
+  if(rank != gmp_matrix_cols(normalForm->canonical)){
+    _kerH[dim] = copy_gmp_matrix(normalForm->right, 1, rank+1, 
+                                 gmp_matrix_rows(normalForm->right),  gmp_matrix_cols(normalForm->right));
+  }
+  
+  if(rank > 0){
+     _codH[dim] = copy_gmp_matrix(normalForm->canonical, 1, 1,
+                                  gmp_matrix_rows(normalForm->canonical), rank);
+    gmp_matrix_left_mult(normalForm->left, _codH[dim]);
+  }
+  
+  mpz_clear(elem);
+  destroy_gmp_normal_form(normalForm);
+  
+  return;
+}
+
+//j:B_(k+1)->Z_k
+void ChainComplex::Inclusion(int dim){
+  
+  if(dim < 1 || dim > 2 || _kerH[dim] == NULL || _codH[dim+1] == NULL) return;
+  
+  gmp_matrix* Zbasis = copy_gmp_matrix(_kerH[dim], 1, 1,
+                                       gmp_matrix_rows(_kerH[dim]), gmp_matrix_cols(_kerH[dim]));
+  gmp_matrix* Bbasis = copy_gmp_matrix(_codH[dim+1], 1, 1,
+                                       gmp_matrix_rows(_codH[dim+1]), gmp_matrix_cols(_codH[dim+1]));
+  
+  int rows = gmp_matrix_rows(Zbasis);
+  int cols = gmp_matrix_cols(Zbasis);
+  if(rows < cols) return;
+  
+  rows = gmp_matrix_rows(Bbasis);
+  cols = gmp_matrix_cols(Bbasis);
+  if(rows < cols) return;
+  
+  // A*inv(V) = U*S
+  gmp_normal_form* normalForm = create_gmp_Smith_normal_form(Zbasis, INVERTED, INVERTED);
+  
+  mpz_t elem;
+  mpz_init(elem);
+  
+  for(int i = 1; i <= cols; i++){
+    gmp_matrix_get_elem(elem, i, i, normalForm->canonical);
+    if(mpz_cmp_si(elem,0) == 0){
+      destroy_gmp_normal_form(normalForm);
+      return;
+    }
+  }
+  
+  gmp_matrix_left_mult(normalForm->left, Bbasis);
+  
+  
+  
+  gmp_matrix* LB = copy_gmp_matrix(Bbasis, 1, 1, gmp_matrix_cols(Zbasis), gmp_matrix_cols(Bbasis));
+  destroy_gmp_matrix(Bbasis);
+  
+  rows = gmp_matrix_rows(LB);
+  cols = gmp_matrix_cols(LB);
+  
+  mpz_t divisor;
+  mpz_init(divisor);
+  mpz_t remainder;
+  mpz_init(remainder);
+  mpz_t result;
+  mpz_init(result);
+  
+  for(int i = 1; i <= rows; i++){
+    gmp_matrix_get_elem(divisor, i, i, normalForm->canonical);
+    for(int j = 1; j <= cols; j++){
+      gmp_matrix_get_elem(elem, i, j, LB);
+      mpz_cdiv_qr(result, remainder, elem, divisor);
+      if(mpz_cmp_si(remainder, 0) == 0){
+        gmp_matrix_set_elem(result, i, j, LB);
+      }
+      else return;
+    }
+  }
+  
+  gmp_matrix_left_mult(normalForm->right, LB);
+  
+  _JMatrix[dim] = LB;
+  
+  mpz_clear(elem);
+  mpz_clear(divisor);
+  mpz_clear(result);
+  destroy_gmp_normal_form(normalForm);
+  
+  return;
+  
+}
+
+void ChainComplex::Quotient(int dim){
+  
+  if(dim < 1 || dim > 3 || _JMatrix[dim] == NULL) return;
+  
+  gmp_matrix* JMatrix = copy_gmp_matrix(_JMatrix[dim], 1, 1,
+                                        gmp_matrix_rows(_JMatrix[dim]), gmp_matrix_cols(_JMatrix[dim]));
+  int rows = gmp_matrix_rows(JMatrix);
+  int cols = gmp_matrix_cols(JMatrix);
+  
+  gmp_normal_form* normalForm = create_gmp_Smith_normal_form(JMatrix, NOT_INVERTED, NOT_INVERTED);
+  
+  mpz_t elem;
+  mpz_init(elem);
+  
+  for(int i = 1; i <= cols; i++){
+    gmp_matrix_get_elem(elem, i, i, normalForm->canonical);
+    if(mpz_cmp_si(elem,0) == 0){
+      destroy_gmp_normal_form(normalForm);
+      return;
+    }
+    if(mpz_cmp_si(elem,1) > 0){
+      _torsion[dim].push_back(mpz_get_si(elem));
+    }
+  }
+  
+  int rank = cols - _torsion[dim].size();
+  if(rows - rank > 0){
+    gmp_matrix* Hbasis = copy_gmp_matrix(normalForm->left, 1, rank+1, rows, rows);
+    _QMatrix[dim] = Hbasis;
+  }
+  
+  mpz_clear(elem);
+  destroy_gmp_normal_form(normalForm);
+  return; 
+}
+
+void ChainComplex::computeHomology(){
+  
+  for(int i=0; i < 4; i++){
+   
+    KerCod(i+1);
+    
+    // 1) no edges, but zero cells
+    if(i == 0 &&  gmp_matrix_cols(getHMatrix(0)) > 0 && getHMatrix(1) == NULL) {
+      _Hbasis[i] = create_gmp_matrix_identity(gmp_matrix_cols(getHMatrix(0)));
+    }
+    
+    // 2) this dimension is empty
+    else if(getHMatrix(i) == NULL && getHMatrix(i+1) == NULL){
+      _Hbasis[i] = NULL;
+    }
+    // 3) No higher dimension cells -> none of the cycles are boundaries
+    else if(getHMatrix(i+1) == NULL){
+      _Hbasis[i] = copy_gmp_matrix(getKerHMatrix(i), 1, 1,
+                                   gmp_matrix_rows(getKerHMatrix(i)), gmp_matrix_cols(getKerHMatrix(i)));
+    }
+    
+    
+    // 5) General case:
+    //   1) Find the bases of boundaries B and cycles Z 
+    //   2) find j: B -> Z and
+    //   3) find quotient Z/j(B) 
+    else {
+      
+      // 4) No lower dimension cells -> all chains are cycles
+      if(getHMatrix(i) == NULL){
+        _kerH[i] = create_gmp_matrix_identity(gmp_matrix_rows(getHMatrix(i+1)));
+      }
+      
+      Inclusion(i);
+      Quotient(i);
+      
+      if(getCodHMatrix(i+1) == NULL){
+        _Hbasis[i] = copy_gmp_matrix(getKerHMatrix(i), 1, 1,
+                                     gmp_matrix_rows(getKerHMatrix(i)), gmp_matrix_cols(getKerHMatrix(i)));
+      }
+      else if(getJMatrix(i) == NULL || getQMatrix(i) == NULL){
+        _Hbasis[i] = NULL;
+      }
+      else{
+        _Hbasis[i] = copy_gmp_matrix(getKerHMatrix(i), 1, 1, 
+                                     gmp_matrix_rows(getKerHMatrix(i)), gmp_matrix_cols(getKerHMatrix(i)));
+        gmp_matrix_right_mult(_Hbasis[i], getQMatrix(i));
+      }  
+    } 
+    
+    
+    destroy_gmp_matrix(_kerH[i]);
+    destroy_gmp_matrix(_codH[i]);
+    destroy_gmp_matrix(_JMatrix[i]);
+    destroy_gmp_matrix(_QMatrix[i]);
+    
+    _kerH[i] = NULL;
+    _codH[i] = NULL;
+    _JMatrix[i] = NULL;
+    _QMatrix[i] = NULL;
+   
+  }
+  
+  
+  return;
+}
+
+
+void ChainComplex::matrixTest(){
+  
+  int rows = 3;
+  int cols = 6;
+  
+  long int elems[rows*cols];
+  for(int i = 1; i<=rows*cols; i++) elems[i-1] = i;
+  
+  gmp_matrix* matrix = create_gmp_matrix_int(rows, cols, elems);
+  
+  gmp_matrix* copymatrix = copy_gmp_matrix(matrix, 3, 2, 3, 5);
+  
+  printMatrix(matrix);
+  printMatrix(copymatrix);
+  
+  return; 
+}
+
+
+#endif
diff --git a/Geo/ChainComplex.h b/Geo/ChainComplex.h
new file mode 100644
index 0000000000000000000000000000000000000000..89b505a900b7722385bdcd131a310c47dd597f34
--- /dev/null
+++ b/Geo/ChainComplex.h
@@ -0,0 +1,99 @@
+// 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.
+
+#ifndef _CHAINCOMPLEX_H_
+#define _CHAINCOMPLEX_H_
+
+#include <stdio.h>
+#include <string>
+#include <algorithm>
+#include <set>
+#include <queue>
+#include "GmshConfig.h"
+#include "MElement.h"
+#include "GModel.h"
+#include "GEntity.h"
+#include "GRegion.h"
+#include "GFace.h"
+#include "GVertex.h"
+#include "CellComplex.h"
+
+#if defined(HAVE_KBIPACK)
+
+#include "gmp.h"
+extern "C" {
+  #include "gmp_normal_form.h" // perhaps make c++ headers instead?
+}
+
+// A class representing a chain complex of a cell complex.
+// This should only be constructed for a reduced cell complex because of
+// dense matrix representations and great computational complexity in its methods.
+class ChainComplex{
+  private:
+   // boundary operator matrices for this chain complex
+   // h_k: C_k -> C_(k-1)
+   gmp_matrix* _HMatrix[4];
+   
+   // Basis matrices for the kernels and codomains of the boundary operator matrices
+   gmp_matrix* _kerH[4];
+   gmp_matrix* _codH[4];
+   
+   gmp_matrix* _JMatrix[4];
+   gmp_matrix* _QMatrix[4];
+   std::vector<long int> _torsion[4];
+   
+   // bases for homology groups
+   gmp_matrix* _Hbasis[4];
+   
+  public:
+   
+   ChainComplex(CellComplex* cellComplex);
+      
+   ChainComplex(){
+     for(int i = 0; i < 4; i++){
+       _HMatrix[i] = create_gmp_matrix_zero(1,1);
+       _kerH[i] = NULL;
+       _codH[i] = NULL;
+       _JMatrix[i] = NULL;
+       _QMatrix[i] = NULL;
+       _Hbasis[i] = NULL;
+     }
+   }
+   virtual ~ChainComplex(){}
+   
+   // get the boundary operator matrix dim->dim-1
+   virtual gmp_matrix* getHMatrix(int dim) { if(dim > -1 || dim < 4) return _HMatrix[dim]; else return NULL;}
+   virtual gmp_matrix* getKerHMatrix(int dim) { if(dim > -1 || dim < 4) return _kerH[dim]; else return NULL;}
+   virtual gmp_matrix* getCodHMatrix(int dim) { if(dim > -1 || dim < 4) return _codH[dim]; else return NULL;}
+   virtual gmp_matrix* getJMatrix(int dim) { if(dim > -1 || dim < 4) return _JMatrix[dim]; else return NULL;}
+   virtual gmp_matrix* getQMatrix(int dim) { if(dim > -1 || dim < 4) return _QMatrix[dim]; else return NULL;}
+   virtual gmp_matrix* getHbasis(int dim) { if(dim > -1 || dim < 4) return _Hbasis[dim]; else return NULL;}
+   
+   // Compute basis for kernel and codomain of boundary operator matrix of dimension dim (ie. ker(h_dim) and cod(h_dim) )
+   virtual void KerCod(int dim);
+   // Compute matrix representation J for inclusion relation from dim-cells who are boundary of dim+1-cells 
+   // to cycles of dim-cells (ie. j: cod(h_(dim+1)) -> ker(h_dim) )
+   virtual void Inclusion(int dim);
+   // Compute quotient problem for given inclusion relation j to find representatives of homology groups
+   // and possible torsion coeffcients
+   virtual void Quotient(int dim);
+   
+   // Compute bases for the homology groups of this chain complex 
+   virtual void computeHomology();
+   
+   virtual int printMatrix(gmp_matrix* matrix){ 
+     printf("%d rows and %d columns\n", gmp_matrix_rows(matrix), gmp_matrix_cols(matrix)); 
+     return gmp_matrix_printf(matrix); } 
+     
+   // debugging aid
+   virtual void matrixTest();
+};
+
+
+#endif
+
+#endif
diff --git a/Geo/Makefile b/Geo/Makefile
index 33438837597c590354d4661b209464cd5c8947b9..fcb2a22ac12dbb1f4c49ebae596c16a6e712d9ac 100644
--- a/Geo/Makefile
+++ b/Geo/Makefile
@@ -39,7 +39,8 @@ SRC = GEntity.cpp\
         MLine.cpp MTriangle.cpp MQuadrangle.cpp MTetrahedron.cpp\
         MHexahedron.cpp MPrism.cpp MPyramid.cpp\
       MZone.cpp MZoneBoundary.cpp\
-      CellComplex.cpp
+      CellComplex.cpp\
+      ChainComplex.cpp
 
 
 OBJ = ${SRC:.cpp=${OBJEXT}}
@@ -387,3 +388,4 @@ CellComplex${OBJEXT}: CellComplex.cpp CellComplex.h ../Common/GmshConfig.h \
   ../Numeric/FunctionSpace.h ../Numeric/GmshMatrix.h ../Numeric/Gauss.h \
   GModel.h GVertex.h GEntity.h Range.h SBoundingBox3d.h GPoint.h GEdge.h \
   GFace.h GEdgeLoop.h Pair.h GRegion.h
+ChainComplex${OBJEXT}: ChainComplex.cpp ChainComplex.h
diff --git a/Makefile b/Makefile
index 36c7086ca76157afb02554fd1617a1bc8abb62e5..1be159addb4e14ba67d8659fe59fcae9744e55c2 100644
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,7 @@ GMSH_API =\
   Geo/discreteVertex.h Geo/discreteEdge.h Geo/discreteFace.h Geo/discreteRegion.h\
   Geo/SPoint2.h Geo/SPoint3.h Geo/SVector3.h Geo/SBoundingBox3d.h Geo/Pair.h Geo/Range.h\
   Geo/CellComplex.h\
+  Geo/ChainComplex.h\
   contrib/kbipack/gmp_normal_form.h contrib/kbipack/gmp_matrix.h contrib/kbipack/gmp_blas.h\
   Numeric/Gauss.h Numeric/FunctionSpace.h Numeric/GmshMatrix.h\
   Numeric/gmshAssembler.h Numeric/gmshTermOfFormulation.h Numeric/gmshLaplace.h\
diff --git a/contrib/kbipack/gmp_matrix.c b/contrib/kbipack/gmp_matrix.c
index c6dcb11fac8ecec18015404d31fddd310f20c005..47bf32dd827b313831a69612947b097d5479e49b 100755
--- a/contrib/kbipack/gmp_matrix.c
+++ b/contrib/kbipack/gmp_matrix.c
@@ -22,7 +22,7 @@
    P.O.Box 692, FIN-33101 Tampere, Finland
    saku.suuriniemi@tut.fi
 
-   $Id: gmp_matrix.c,v 1.3 2009-04-14 10:02:22 matti Exp $
+   $Id: gmp_matrix.c,v 1.4 2009-04-21 07:06:22 matti Exp $
 */
 
 
@@ -182,6 +182,8 @@ copy_gmp_matrix(const gmp_matrix * matrix,
 
   r = end_row-start_row+1;
   c = end_col-start_col+1;
+  if(r < 1 || c < 1) return NULL;
+  
   new_matrix -> storage = (mpz_t *) calloc(r*c, sizeof(mpz_t));
   if(new_matrix -> storage == NULL)
     {