diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp
index 0d0a618a6607c52f2abae9da1a663038969bed92..0fd1b5c576fe62d22cecd5b76bcdc7bdddc188b8 100644
--- a/Common/CommandLine.cpp
+++ b/Common/CommandLine.cpp
@@ -1,4 +1,4 @@
-// $Id: CommandLine.cpp,v 1.108 2007-11-26 14:34:09 remacle Exp $
+// $Id: CommandLine.cpp,v 1.109 2007-12-03 15:17:39 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -204,6 +204,10 @@ void Get_Options(int argc, char *argv[])
         CTX.batch = 3;
         i++;
       }
+      else if(!strcmp(argv[i] + 1, "4")) {
+        CTX.batch = 4;
+        i++;
+      }
       else if(!strcmp(argv[i] + 1, "pid")) {
 	fprintf(stdout, "%d\n", GetProcessId());
 	fflush(stdout);
diff --git a/Fltk/Main.cpp b/Fltk/Main.cpp
index d8f3a96e4252ac523d29f34cc7b9fd714f4a4c4d..8d1d2f829f5950c98fc0d0811d5d5a44759529d5 100644
--- a/Fltk/Main.cpp
+++ b/Fltk/Main.cpp
@@ -1,4 +1,4 @@
-// $Id: Main.cpp,v 1.112 2007-09-26 20:51:58 geuzaine Exp $
+// $Id: Main.cpp,v 1.113 2007-12-03 15:17:39 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -125,7 +125,11 @@ int main(int argc, char *argv[])
         else
           Msg(GERROR, "Invalid background mesh (no view)");
       }
-      if(CTX.batch > 0) {
+      if(CTX.batch == 4) {
+        AdaptMesh();
+        CreateOutputFile(CTX.output_filename, CTX.mesh.format);
+      }
+      else if(CTX.batch > 0) {
         GenerateMesh(CTX.batch);
         CreateOutputFile(CTX.output_filename, CTX.mesh.format);
       }
diff --git a/Geo/GModelIO_Mesh.cpp b/Geo/GModelIO_Mesh.cpp
index 3299ace23fe74ac69d480caf724fdb68c9d45579..e7e1e1b983d0b0dfa1b6b82d79c4c10b18dab2e2 100644
--- a/Geo/GModelIO_Mesh.cpp
+++ b/Geo/GModelIO_Mesh.cpp
@@ -1,4 +1,4 @@
-// $Id: GModelIO_Mesh.cpp,v 1.23 2007-10-03 19:40:41 geuzaine Exp $
+// $Id: GModelIO_Mesh.cpp,v 1.24 2007-12-03 15:17:40 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -20,6 +20,7 @@
 // Please report all bugs and problems to <gmsh@geuz.org>.
 
 #include <map>
+#include <cstring>
 #include <string>
 
 #include "Message.h"
diff --git a/Geo/GVertex.cpp b/Geo/GVertex.cpp
index ee938919107c6fed7291ef054d810f9eac019a02..6ce753e97114044141fa2eb8f2ad56ba9f8e4fb6 100644
--- a/Geo/GVertex.cpp
+++ b/Geo/GVertex.cpp
@@ -1,4 +1,4 @@
-// $Id: GVertex.cpp,v 1.13 2007-09-03 12:00:28 geuzaine Exp $
+// $Id: GVertex.cpp,v 1.14 2007-12-03 15:17:40 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -20,6 +20,7 @@
 // Please report all bugs and problems to <gmsh@geuz.org>.
 
 #include <algorithm>
+#include <cstring>
 #include "GVertex.h"
 #include "GFace.h"
 #include "Message.h"
diff --git a/Geo/MElement.h b/Geo/MElement.h
index 1b4630561e5475162585cfc193f9544f488b137f..a514effaf605cd72a02d0c6833239de27925dad6 100644
--- a/Geo/MElement.h
+++ b/Geo/MElement.h
@@ -304,7 +304,7 @@ class MLineN : public MLine {
 class MTriangle : public MElement {
  protected:
   MVertex *_v[3];
-  virtual void jac(int order, MVertex *v[], double u, double v, double jac[2][2]);
+  virtual void jac(int order, MVertex *verts[], double u, double v, double jac[2][2]);
  public :
   MTriangle(MVertex *v0, MVertex *v1, MVertex *v2, int num=0, int part=0) 
     : MElement(num, part)
diff --git a/Geo/MVertex.cpp b/Geo/MVertex.cpp
index 9302a3e2a5b911f0f596cfe91cb4f20b882cc612..0c7532683e6739bae793cdbd913d0ab6fee2440d 100644
--- a/Geo/MVertex.cpp
+++ b/Geo/MVertex.cpp
@@ -1,4 +1,4 @@
-// $Id: MVertex.cpp,v 1.16 2007-09-21 16:22:51 geuzaine Exp $
+// $Id: MVertex.cpp,v 1.17 2007-12-03 15:17:40 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -19,7 +19,8 @@
 // 
 // Please report all bugs and problems to <gmsh@geuz.org>.
 
-#include <math.h>
+#include <cstring>
+#include <cmath>
 #include "MVertex.h"
 #include "GEdge.h"
 #include "GFace.h"
diff --git a/Geo/OCCEdge.cpp b/Geo/OCCEdge.cpp
index 77779abe43ff25424b5d256e37465268498092e3..95223af5c84936f8e29b7a2f392da5d131c68c59 100644
--- a/Geo/OCCEdge.cpp
+++ b/Geo/OCCEdge.cpp
@@ -1,4 +1,4 @@
-// $Id: OCCEdge.cpp,v 1.27 2007-11-26 14:34:09 remacle Exp $
+// $Id: OCCEdge.cpp,v 1.28 2007-12-03 15:17:40 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -190,14 +190,15 @@ GEntity::GeomType OCCEdge::geomType() const
 
 int OCCEdge::minimumMeshSegments() const
 {
-  int np;
-  if(geomType() == Line)
-    np= GEdge::minimumMeshSegments();
-  else 
+   int np;
+   if(geomType() == Line)
+     np= GEdge::minimumMeshSegments();
+   else 
     np=CTX.mesh.min_curv_points - 1;
   
-  // if the edge is closed, ensure that at least 3 points are generated 
-  if (getBeginVertex() == getEndVertex()) np=std::max(2,np);
+  // if the edge is closed, ensure that at least 3 points are generated in the
+  // 1D mesh (4 segments, one of which is degenerated)
+  if (getBeginVertex() == getEndVertex()) np=std::max(4,np);
 
   return np;
 }
diff --git a/Mesh/Generator.cpp b/Mesh/Generator.cpp
index a64bac22a729cfd6a51e24dee73eb9e0402527ef..c6b86fa80ba00e23060e640799b0383a6cdfa92c 100644
--- a/Mesh/Generator.cpp
+++ b/Mesh/Generator.cpp
@@ -1,4 +1,4 @@
-// $Id: Generator.cpp,v 1.127 2007-11-28 14:18:10 remacle Exp $
+// $Id: Generator.cpp,v 1.128 2007-12-03 15:17:40 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -331,6 +331,36 @@ void OptimizeMesh(GModel *m)
   Msg(STATUS1, "Mesh");
 }
 
+void AdaptMesh()
+{
+  Msg(STATUS1, "Adapting the 3D Mesh...");
+  double t1 = Cpu();
+
+  if(CTX.threads_lock) {
+    Msg(INFO, "I'm busy! Ask me that later...");
+    return;
+  }
+
+  CTX.threads_lock = 1;
+
+  GModel *m = GModel::current();
+
+  std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
+  std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
+  std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
+  std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
+  std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
+  std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
+  std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
+  std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
+  std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
+  std::for_each(m->firstRegion(), m->lastRegion(), adaptMeshGRegion());
+
+  double t2 = Cpu();
+  Msg(INFO, "Mesh Adaptation complete (%g s)", t2 - t1);
+  Msg(STATUS1, "Mesh");
+}
+
 void GenerateMesh(int ask)
 {
   if(CTX.threads_lock) {
diff --git a/Mesh/Generator.h b/Mesh/Generator.h
index a39d30ff9ed0ab5a2ab8b69449e3eed31d358061..4bcdc68156146a176417007a88d69362960c91e7 100644
--- a/Mesh/Generator.h
+++ b/Mesh/Generator.h
@@ -23,6 +23,7 @@
 class GModel;
 
 void GetStatistics(double stat[50], double quality[3][100]=0);
+void AdaptMesh();
 void GenerateMesh(int dimension);
 void OptimizeMesh(GModel *m);
 void OptimizeMeshNetgen(GModel *m);
diff --git a/Mesh/meshGFaceDelaunayInsertion.h b/Mesh/meshGFaceDelaunayInsertion.h
index ae3f8a89c6ef54eceb2c669e54a0560a866367c3..5d307bc3f6a41e01144d2d16e60bea4cb6557bb8 100644
--- a/Mesh/meshGFaceDelaunayInsertion.h
+++ b/Mesh/meshGFaceDelaunayInsertion.h
@@ -20,6 +20,7 @@
 // 
 // Please report all bugs and problems to <gmsh@geuz.org>.
 
+class GModel;
 #include "MElement.h"
 #include <list>
 #include <set>
@@ -132,5 +133,4 @@ struct edgeXface
 };
 
 
-
 #endif
diff --git a/Mesh/meshGRegion.h b/Mesh/meshGRegion.h
index 6744f4bf6f4e035da2a509e70193fd37a040a685..6f3fb49c7bfe205f396ea61558d6f492431d1237 100644
--- a/Mesh/meshGRegion.h
+++ b/Mesh/meshGRegion.h
@@ -56,6 +56,13 @@ class deMeshGRegion {
   void operator () (GRegion *);
 };
 
+// adapt the mesh of a region
+class adaptMeshGRegion {
+ public :
+  void operator () (GRegion *);
+};
+
+
 void MeshDelaunayVolume(std::vector<GRegion*> &delaunay);
 int MeshTransfiniteVolume(GRegion *gr);
 int SubdivideExtrudedMesh(GModel *m);
diff --git a/Mesh/meshGRegionDelaunayInsertion.cpp b/Mesh/meshGRegionDelaunayInsertion.cpp
index 16c8064b82a1fa062da6cf7a90cb1ef71431d512..42dffd8cad07a94903a749bd4fe40a6ede67a400 100644
--- a/Mesh/meshGRegionDelaunayInsertion.cpp
+++ b/Mesh/meshGRegionDelaunayInsertion.cpp
@@ -1,4 +1,4 @@
-// $Id: meshGRegionDelaunayInsertion.cpp,v 1.24 2007-11-28 14:18:10 remacle Exp $
+// $Id: meshGRegionDelaunayInsertion.cpp,v 1.25 2007-12-03 15:17:40 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -25,6 +25,7 @@
 #include "meshGRegionDelaunayInsertion.h"
 #include "GModel.h"
 #include "GRegion.h"
+#include "meshGRegion.h"
 #include "Numeric.h"
 #include "Message.h"
 #include <set>
@@ -324,7 +325,6 @@ static void setLcs ( MTetrahedron *t, std::map<MVertex*,double> &vSizes)
 
  }
 
-typedef std::multimap<MVertex*,std::pair<MTriangle*,GFace*> > fs_cont ;
 
 
 GFace* findInFaceSearchStructure ( MVertex *p1,MVertex *p2,MVertex *p3, const fs_cont&search )
@@ -432,9 +432,10 @@ void recur_classify ( MTet4 *t ,
     }
 }
 
-//template <class CONTAINER, class DATA> 
-void gmshOptimizeMesh (GRegion *gr, const gmshQualityMeasure4Tet &qm)
+void adaptMeshGRegion::operator () (GRegion *gr)
 {
+  const gmshQualityMeasure4Tet qm = QMTET_2;
+
   typedef std::list<MTet4 *> CONTAINER ;
   CONTAINER allTets;
   for (int i=0;i<gr->tetrahedra.size();i++){
@@ -444,8 +445,180 @@ void gmshOptimizeMesh (GRegion *gr, const gmshQualityMeasure4Tet &qm)
   
   connectTets(allTets.begin(),allTets.end());
 
+  double t1 = Cpu();
+  std::vector<MTet4*> illegals;
+  const int nbRanges=10;
+  int quality_ranges [nbRanges];
+  {
+    double totalVolumeb = 0.0;
+    double worst = 1.0;
+    double avg = 0;
+    int count=0;
+    for (int i=0;i<nbRanges;i++)quality_ranges[i] = 0;
+    for (CONTAINER::iterator it = allTets.begin();it!=allTets.end();++it)
+      {
+	if (!(*it)->isDeleted()){
+	  double vol = fabs((*it)->tet()->getVolume());
+	  double qual = (*it)->getQuality();
+	  worst = std::min(qual,worst);
+	  avg+=qual;
+	  count++;
+	  totalVolumeb+=vol;
+	  for (int i=0;i<nbRanges;i++){
+	    double low  = (double)i/(nbRanges);
+	    double high = (double)(i+1)/(nbRanges);
+	    if (qual >= low && qual < high)quality_ranges[i]++;
+	  }	      	      
+	}
+      }
+    Msg(INFO,"Opti : START with %12.5E QBAD %12.5E QAVG %12.5E",totalVolumeb,worst,avg/count);
+    for (int i=0;i<nbRanges;i++){
+      double low  = (double)i/(nbRanges);
+      double high = (double)(i+1)/(nbRanges);
+      Msg(INFO,"Opti : %3.2f < QUAL < %3.2f : %9d elements ",low,high,quality_ranges[i]);
+    }	      	      
+  }    
+    
+  double qMin = 0.5;
+  double sliverLimit = 0.2;
 
+  int nbESwap=0, nbFSwap=0, nbReloc=0, nbCollapse = 0;
+    
+  while (1){
+    std::vector<MTet4*> newTets;    
 
+    for (CONTAINER::iterator it = allTets.begin();it!=allTets.end();++it){
+      if (!(*it)->isDeleted()){
+	for (int i=0;i<4;i++){
+	  for (int j=0;j<4;j++){
+	    if (gmshCollapseVertex(newTets,*it,i,j,QMTET_2)){nbCollapse++;i=j=10;}
+	  }
+	}
+      }
+    }
+
+    printf("nbCollapses = %d\n",nbCollapse);
+
+    for (CONTAINER::iterator it = allTets.begin();it!=allTets.end();++it){
+      if (!(*it)->isDeleted()){
+	double vol;
+	double qq = (*it)->getQuality();
+	if (qq < qMin){
+	  for (int i=0;i<4;i++){
+	    if (gmshFaceSwap(newTets,*it,i,qm)){nbFSwap++;break;}
+	  }
+	}
+      }
+    }
+ 
+    illegals.clear();
+    for (int i=0;i<nbRanges;i++)quality_ranges[i] = 0;
+
+    for (CONTAINER::iterator it = allTets.begin();it!=allTets.end();++it)
+      {
+	if (!(*it)->isDeleted()){
+	  double qq = (*it)->getQuality();
+	  if (qq < qMin)
+	    for (int i=0;i<6;i++){
+	      if (gmshEdgeSwap(newTets,*it,i,qm)) {nbESwap++;break;}
+	    }
+	  if (!(*it)->isDeleted()){
+	    if (qq < sliverLimit)illegals.push_back(*it);
+	    for (int i=0;i<nbRanges;i++){
+	      double low  = (double)i/(nbRanges);
+	      double high = (double)(i+1)/(nbRanges);
+	      if (qq >= low && qq < high)quality_ranges[i]++;
+	    }	      	      
+	  }
+	}
+      }
+
+    if (!newTets.size())break;
+    
+    // add all the new tets in the container
+    for (int i=0;i<newTets.size();i++){
+      if (!newTets[i]->isDeleted()){
+	allTets.push_back(newTets[i]);
+      }
+      else{
+	delete newTets[i]->tet();
+	delete newTets[i];
+      }
+    }  
+
+    // relocate vertices
+    for (CONTAINER::iterator it = allTets.begin();it!=allTets.end();++it)
+      {
+	if (!(*it)->isDeleted()){
+	  double qq = (*it)->getQuality();
+	  if (qq < qMin)
+	    for (int i=0;i<4;i++){
+	      if (gmshSmoothVertex(*it,i,qm))nbReloc++;
+	    }
+	}
+      }
+
+    double totalVolumeb = 0.0;
+    double worst = 1.0;
+    double avg = 0;
+    int count=0;
+    for (CONTAINER::iterator it = allTets.begin();it!=allTets.end();++it)
+      {
+	if (!(*it)->isDeleted()){
+	  double vol = fabs((*it)->tet()->getVolume());
+	  double qual = (*it)->getQuality();
+	  worst = std::min(qual,worst);
+	  avg+=qual;
+	  count++;
+	  totalVolumeb+=vol;
+	}
+      }
+    double t2 = Cpu();
+    Msg(INFO,"Opti : (%d,%d,%d) = %12.5E QBAD %12.5E QAVG %12.5E (%8.3f sec)",nbESwap,nbFSwap,nbReloc,totalVolumeb,worst,avg/count,t2-t1);
+    break;
+  }
+  
+  int nbSlivers = 0;
+  for (int i=0;i<illegals.size();i++)
+    if (!(illegals[i]->isDeleted()))nbSlivers ++;
+  
+  if (nbSlivers){
+    Msg(INFO,"Opti : %d illegal tets are still in the mesh, trying to remove them",nbSlivers);
+  }
+  else{
+    Msg(INFO,"Opti : no illegal tets in the mesh ;-)",nbSlivers);
+  }
+
+  for (int i=0;i<nbRanges;i++){
+    double low  = (double)i/(nbRanges);
+    double high = (double)(i+1)/(nbRanges);
+    Msg(INFO,"Opti : %3.2f < QUAL < %3.2f : %9d elements ",low,high,quality_ranges[i]);
+  }	      	      
+
+  for (CONTAINER::iterator it = allTets.begin();it!=allTets.end();++it)
+    {
+      if (!(*it)->isDeleted()){
+	gr->tetrahedra.push_back((*it)->tet());
+	delete *it;
+      }
+      else{
+	delete (*it)->tet();
+	delete *it;	
+      }
+    }
+}
+
+//template <class CONTAINER, class DATA> 
+void gmshOptimizeMesh (GRegion *gr, const gmshQualityMeasure4Tet &qm)
+{
+  typedef std::list<MTet4 *> CONTAINER ;
+  CONTAINER allTets;
+  for (int i=0;i<gr->tetrahedra.size();i++){
+    allTets.push_back(new MTet4(gr->tetrahedra[i],qm));
+  }
+  gr->tetrahedra.clear();
+  
+  connectTets(allTets.begin(),allTets.end());
 
   double t1 = Cpu();
   std::vector<MTet4*> illegals;
diff --git a/Mesh/meshGRegionDelaunayInsertion.h b/Mesh/meshGRegionDelaunayInsertion.h
index d10f0c91dea908b26f800db6e62f5fdf48e5e29e..1c6469b5357e188f9abe373e21115bdaa225fdcf 100644
--- a/Mesh/meshGRegionDelaunayInsertion.h
+++ b/Mesh/meshGRegionDelaunayInsertion.h
@@ -24,10 +24,13 @@
 #include "qualityMeasures.h"
 #include <list>
 #include <set>
+#include <map>
 #include <stack>
 
 //#define _GMSH_PRE_ALLOCATE_STRATEGY_ 1
 class GRegion;
+class GFace;
+class GModel;
 
 class MTet4Factory;
 
@@ -232,5 +235,8 @@ private:
 };
 
 void gmshOptimizeMesh (GRegion *gr, const gmshQualityMeasure4Tet &qm);
+typedef std::multimap<MVertex*,std::pair<MTriangle*,GFace*> > fs_cont ;
+GFace* findInFaceSearchStructure ( MVertex *p1,MVertex *p2,MVertex *p3, const fs_cont&search );
+bool buildFaceSearchStructure ( GModel *model , fs_cont&search );
 
 #endif
diff --git a/Mesh/meshGRegionLocalMeshMod.cpp b/Mesh/meshGRegionLocalMeshMod.cpp
index e0017a26b59799b14cf863b30bf9b99312a4496c..c2066916a715dcfac70098e979be01c92457f982 100644
--- a/Mesh/meshGRegionLocalMeshMod.cpp
+++ b/Mesh/meshGRegionLocalMeshMod.cpp
@@ -1,5 +1,6 @@
 #include "meshGRegionLocalMeshMod.h"
 #include "GEntity.h"
+#include "Message.h"
 
 static int edges[6][2] =    {{0,1},{0,2},{0,3},{1,2},{1,3},{2,3}};
 static int efaces[6][2] =   {{0,2},{0,1},{1,2},{0,3},{2,3},{1,3}};
@@ -13,6 +14,35 @@ static int vFac[4][3] = {{0,1,2},{0,2,3},{0,1,3},{1,2,3}};
 // all tets that share this edge and all vertices that are
 // forming the outer ring of the cavity 
 // we return true if the cavity is closed and false if it is open
+
+void computeNeighboringTetsOfACavity (const std::vector<MTet4*> &cavity,std::vector<MTet4*> &outside)
+{
+  outside.clear();
+  for (int i=0;i<cavity.size();i++){
+    for (int j=0;j<4;j++){
+      MTet4 * neigh = cavity[i]->getNeigh(j);
+      if (neigh)
+	{
+	  bool found = false;
+	  for (int k=0;k<outside.size();k++){
+	    if(outside[k]==neigh){
+	      found=true;
+	      break;
+	    }
+	  }
+	  if (!found){
+	    for (int k=0;k<cavity.size() ;k++){
+	      if(cavity[k] ==neigh){
+		found=true;
+	      }
+	    }
+	  }
+	  if(!found)outside.push_back(neigh);
+	}
+    }
+  }
+}			   
+			   
 bool gmshBuildEdgeCavity ( MTet4 *t, 
 			   int iLocalEdge, 
 			   MVertex **v1,MVertex **v2,
@@ -21,7 +51,6 @@ bool gmshBuildEdgeCavity ( MTet4 *t,
 			   std::vector<MVertex*> &ring ){
   cavity.clear();
   ring.clear();
-  outside.clear();
 
   *v1 = t->tet()->getVertex(edges[iLocalEdge][0]);
   *v2 = t->tet()->getVertex(edges[iLocalEdge][1]);
@@ -68,31 +97,7 @@ bool gmshBuildEdgeCavity ( MTet4 *t,
       throw;
     }
   }
-  
-  for (int i=0;i<cavity.size();i++){
-    for (int j=0;j<4;j++){
-      MTet4 * neigh = cavity[i]->getNeigh(j);
-      if (neigh)
-	{
-	  bool found = false;
-	  for (int k=0;k<outside.size();k++){
-	    if(outside[k]==neigh){
-	      found=true;
-	      break;
-	    }
-	  }
-	  if (!found){
-	    for (int k=0;k<cavity.size() ;k++){
-	      if(cavity[k] ==neigh){
-		found=true;
-	      }
-	    }
-	  }
-	  if(!found)outside.push_back(neigh);
-	}
-    }
-  }
-
+  computeNeighboringTetsOfACavity (cavity,outside);
   return true;
 }
 
@@ -319,6 +324,47 @@ bool gmshEdgeSwap (std::vector<MTet4 *> &newTets,
   return true;
 }
 
+bool gmshEdgeSplit (std::vector<MTet4 *> &newTets,
+		    MTet4 *tet,
+		    MVertex *newVertex,
+		    int iLocalEdge,
+		    const gmshQualityMeasure4Tet &cr){
+  std::vector<MTet4*> cavity;
+  std::vector<MTet4*> outside;
+  std::vector<MVertex*> ring;
+  MVertex *v1,*v2;
+
+  bool closed = gmshBuildEdgeCavity ( tet,iLocalEdge,&v1,&v2,cavity,outside,ring);
+  if (!closed)return false;
+  
+  for (int j=0;j<ring.size();j++){
+    MVertex *pv1 = ring[j];
+    MVertex *pv2 = ring[(j+1)%ring.size()];
+    MTetrahedron *tr1 = new MTetrahedron ( pv1,
+					   pv2,
+					   newVertex,
+					   v1);
+    MTetrahedron *tr2 = new MTetrahedron ( newVertex,
+					   pv2,
+					   pv1,
+					   v2);
+    MTet4 *t41 = new MTet4 ( tr1 , cr) ; 
+    MTet4 *t42 = new MTet4 ( tr2 , cr);
+    t41->setOnWhat(cavity[0]->onWhat());
+    t42->setOnWhat(cavity[0]->onWhat());
+    outside.push_back(t41);
+    outside.push_back(t42);
+    newTets.push_back(t41);
+    newTets.push_back(t42);
+  }
+
+  for (int i=0;i<cavity.size();i++)cavity[i]->setDeleted(true);
+  
+  connectTets ( outside );      
+
+  return true;
+}
+
 // swap a face i.e. remove a face shared by 2 tets
 bool gmshFaceSwap (std::vector<MTet4 *> &newTets,
 		   MTet4 *t1, 
@@ -413,7 +459,7 @@ void gmshBuildVertexCavity_recur ( MTet4 *t,
 				   std::vector<MTet4*> &cavity){
   //  if (recur > 20)printf("oufti %d\n",recur);
   if(t->isDeleted()){
-    printf("a deleted triangle is a neighbor of a non deleted triangle\n");
+    Msg(FATAL,"a deleted triangle is a neighbor of a non deleted triangle");
   }
   int iV=-1;
   for (int i=0; i<4; i++){
@@ -423,7 +469,7 @@ void gmshBuildVertexCavity_recur ( MTet4 *t,
     }
   }
   if (iV==-1){
-    printf("this tet does not contain a given vertex\n");
+    Msg(FATAL,"trying to build a cavity of tets for a vertex that does not belong to this tet");
   }
   for (int i=0; i<3; i++){
     MTet4 *neigh = t->getNeigh(vFac[iV][i]);
@@ -444,6 +490,79 @@ void gmshBuildVertexCavity_recur ( MTet4 *t,
 }
 
 
+bool gmshCollapseVertex ( std::vector<MTet4 *> &newTets,
+			  MTet4 *t, 
+			  int iVertex,
+			  int iTarget,
+			  const gmshQualityMeasure4Tet &cr){
+  
+  if(t->isDeleted())throw;
+
+  MVertex *v = t->tet()->getVertex(iVertex);
+  MVertex *tg = t->tet()->getVertex(iTarget);
+
+  if(v->onWhat()->dim() < 3)return false;
+  if(tg->onWhat()->dim() < 3)return false;
+
+  std::vector<MTet4*> cavity_v;
+  std::vector<MTet4*> outside;
+  cavity_v.push_back(t);
+  gmshBuildVertexCavity_recur (t,v,cavity_v);
+
+  std::vector<MTet4*> toDelete;
+  std::vector<MTet4*> toUpdate;
+  double volume = 0;
+  for (int i=0;i<cavity_v.size();i++){
+    bool found = false;
+    volume+=fabs(cavity_v[i]->tet()->getVolume());
+    for (int j=0;j<4;j++){
+      if (cavity_v[i]->tet()->getVertex(j) == tg)found=true;
+    }
+    if (found) toDelete.push_back(cavity_v[i]);
+    else toUpdate.push_back(cavity_v[i]);
+  }
+
+  double x = v->x();
+  double y = v->y();
+  double z = v->z();
+  v->x() = tg->x();
+  v->y() = tg->y();
+  v->z() = tg->z();
+
+  double volume_update=0;
+  for (int i=0;i<toUpdate.size();i++){
+    volume_update+=fabs(toUpdate[i]->tet()->getVolume());
+  }
+
+  //  printf("%12.5E %12.5E\n",volume,volume_update);
+
+  if (fabs(volume-volume_update) > 1.e-10 * volume)
+    {
+      v->x() = x;
+      v->y() = y;
+      v->z() = z;
+      return false;
+    }
+  // ok we collapse
+  computeNeighboringTetsOfACavity (cavity_v,outside);
+  for (int i=0;i<toUpdate.size();i++){
+    MTetrahedron *tr1 = new MTetrahedron ( toUpdate[i]->tet()->getVertex(0) == v ? tg : toUpdate[i]->tet()->getVertex(0),
+					   toUpdate[i]->tet()->getVertex(1) == v ? tg : toUpdate[i]->tet()->getVertex(1),
+					   toUpdate[i]->tet()->getVertex(2) == v ? tg : toUpdate[i]->tet()->getVertex(2),
+					   toUpdate[i]->tet()->getVertex(3) == v ? tg : toUpdate[i]->tet()->getVertex(3));
+    MTet4 *t41 = new MTet4 ( tr1 , cr) ; 
+    t41->setOnWhat(cavity_v[0]->onWhat());
+    outside.push_back(t41);
+    newTets.push_back(t41);
+  }
+  for (int i=0;i<cavity_v.size();i++)cavity_v[i]->setDeleted(true);
+  
+  connectTets ( outside );      
+  
+  return true;
+}
+
+
 bool gmshSmoothVertex ( MTet4 *t, 
 			int iVertex,
 			const gmshQualityMeasure4Tet &cr){
@@ -517,3 +636,303 @@ bool gmshSmoothVertex ( MTet4 *t,
 }
 
 
+// Edge split sets ...
+// Here, we only build a list of tets that are a subdivision
+// of the given 
+//static int edges[6][2] =    {{0,1},{0,2},{0,3},{1,2},{1,3},{2,3}};
+
+// the resulting triangles are 012 and 230 in vector res
+// void splitPrism (std::vector<MTet4 *> &newTets,
+// 		 std::vector<MVertex *> &steinerPoints,
+// 		 MVertex* v1,
+// 		 MVertex* v2,
+// 		 MVertex* v3,
+// 		 MVertex* v4,
+// 		 MVertex* v5,
+// 		 MVertex* v6, 
+// 		 const gmshQualityMeasure4Tet &cr,
+// 		 MVertex *res[4],
+// 		 fs_cont &search, 
+// 		 GFace *fakeFace){
+// }
+
+// void splitQuadFace (MVertex* newv1, 
+// 		    MVertex* newv2, 
+// 		    MVertex* v21, 
+// 		    MVertex* v11, 
+// 		    const gmshQualityMeasure4Tet &cr,
+// 		    MVertex *res[4],
+// 		    fs_cont &search, 
+// 		    GFace *fakeFace){
+//   GFace* gfound = findInFaceSearchStructure (newv1,newv2,v11,search);
+//   if (gfound){
+//     res[0] = newv2;
+//     res[1] = newv1;
+//     res[2] = v11;
+//     res[3] = v21;
+//   }
+//   else{
+//     GFace* gfound = findInFaceSearchStructure (newv1,newv2,v21,search);
+//     if (gfound){
+//       res[0] = newv1;
+//       res[1] = newv2;
+//       res[2] = v21;
+//       res[3] = v11;
+//     }
+//     // choose the best configuration
+//     else{
+//       double q1 = std::min(qmTri(newv1,newv2,v11,cr),qmTet(newv2,v11,v21,cr));
+//       double q2 = std::min(qmTet(newv1,newv2,v21,cr),qmTet(newv1,v11,v21,cr));
+//       if (q1 > q2){
+// 	res[0] = newv2;
+// 	res[1] = newv1;
+// 	res[2] = v11;
+// 	res[3] = v21;
+// 	MVertex *p1 = std::min(std::min(newv1,newv2),v11);
+// 	MVertex *p2 = std::min(std::min(newv2,v11),v21);
+// 	search.insert ( std::pair<MVertex*,std::pair<MTriangle*,GFace*> > ( p1, std::pair<MTriangle*,GFace*>(new MTriangle(newv1,newv2,v11),fakeFace)));	  
+// 	search.insert ( std::pair<MVertex*,std::pair<MTriangle*,GFace*> > ( p2, std::pair<MTriangle*,GFace*>(new MTriangle(newv2,v11,v21),fakeFace)));	  
+//       }
+//       else{
+// 	res[0] = newv1;
+// 	res[1] = newv2;
+// 	res[2] = v21;
+// 	res[3] = v11;
+// 	MVertex *p1 = std::min(std::min(newv1,newv2),v21);
+// 	MVertex *p2 = std::min(std::min(newv2,v11),v21);
+// 	search.insert ( std::pair<MVertex*,std::pair<MTriangle*,GFace*> > ( p1, std::pair<MTriangle*,GFace*>(new MTriangle(newv1,newv2,v21),fakeFace)));	  
+// 	search.insert ( std::pair<MVertex*,std::pair<MTriangle*,GFace*> > ( p2, std::pair<MTriangle*,GFace*>(new MTriangle(newv1,v11,v21),fakeFace)));	  
+//       }
+//     }
+//   }	  
+// }
+// void splitQuadFace (std::vector<MTet4 *> &newTets,
+// 		    std::vector<MVertex *> &steinerPoints,
+// 		    MVertex* newv1, 
+// 		    MVertex* newv2, 
+// 		    MVertex* v21, 
+// 		    MVertex* v11, 
+// 		    MVertex* other,
+// 		    const gmshQualityMeasure4Tet &cr,
+// 		    fs_cont &search, 
+// 		    GFace *fakeFace){
+//   MTetrahedron *tr3,*tr4;
+//   // now we look if there exists a face with the same vertices
+//   GFace* gfound = findInFaceSearchStructure (newv1,newv2,v11,search);
+//   if (gfound){
+//     tr3 = new MTetrahedron ( newv1,newv2,v11,other);	  
+//     tr4 = new MTetrahedron ( newv2,v11,v21,other);	  
+//   }
+//   else{
+//     GFace* gfound = findInFaceSearchStructure (newv1,newv2,v21,search);
+//     if (gfound){
+//       tr3 = new MTetrahedron ( newv1,newv2,v21,other);	  
+//       tr4 = new MTetrahedron ( newv1,v11,v21,other);	  
+//     }
+//     // choose the best configuration
+//     else{
+//       double vol;
+//       double q1 = std::min(qmTet(newv1,newv2,v11,other,cr,vol),qmTet(newv2,v11,v21,other,cr,vol));
+//       double q2 = std::min(qmTet(newv1,newv2,v21,other,cr,vol),qmTet(newv1,v11,v21,other,cr,vol));
+//       if (q1 > q2){
+// 	tr3 = new MTetrahedron ( newv1,newv2,v11,other);	  
+// 	tr4 = new MTetrahedron ( newv2,v11,v21,other);
+// 	MVertex *p1 = std::min(std::min(newv1,newv2),v11);
+// 	MVertex *p2 = std::min(std::min(newv2,v11),v21);
+// 	search.insert ( std::pair<MVertex*,std::pair<MTriangle*,GFace*> > ( p1, std::pair<MTriangle*,GFace*>(new MTriangle(newv1,newv2,v11),fakeFace)));	  
+// 	search.insert ( std::pair<MVertex*,std::pair<MTriangle*,GFace*> > ( p2, std::pair<MTriangle*,GFace*>(new MTriangle(newv2,v11,v21),fakeFace)));	  
+//       }
+//       else{
+// 	tr3 = new MTetrahedron ( newv1,newv2,v21,other);	  
+// 	tr4 = new MTetrahedron ( newv1,v11,v21,other);	  
+// 	MVertex *p1 = std::min(std::min(newv1,newv2),v21);
+// 	MVertex *p2 = std::min(std::min(newv2,v11),v21);
+// 	search.insert ( std::pair<MVertex*,std::pair<MTriangle*,GFace*> > ( p1, std::pair<MTriangle*,GFace*>(new MTriangle(newv1,newv2,v21),fakeFace)));	  
+// 	search.insert ( std::pair<MVertex*,std::pair<MTriangle*,GFace*> > ( p2, std::pair<MTriangle*,GFace*>(new MTriangle(newv1,v11,v21),fakeFace)));	  
+//       }
+//     }
+//   }	  
+// }
+
+// bool splitEdgesOfTet (std::vector<MTet4 *> &newTets,
+// 		      std::vector<MVertex *> &steinerPoints,
+// 		      MTet4 *t1,
+// 		      int nbEdges,
+// 		      int e[6], 
+// 		      MVertex* pts[6],
+// 		      const gmshQualityMeasure4Tet &cr,
+// 		      fs_cont &search, 
+// 		      GFace *fakeFace){
+//   switch(nbEdges){
+//   case 1 :
+//     {
+//       int iE = edges[0];
+//       MVertex *newv = pts[0];
+//       MVertex *v1 = t1->tet()->getVertex(e[iE][0]);
+//       MVertex *v2 = t1->tet()->getVertex(e[iE][1]);
+//       int oE = 5-iE;
+//       MVertex *o1 = t1->tet()->getVertex(e[oE][0]);
+//       MVertex *o2 = t1->tet()->getVertex(e[oE][1]);
+//       MTetrahedron *tr1 = new MTetrahedron ( newv,v1,o1,o2);
+//       MTetrahedron *tr2 = new MTetrahedron ( newv,v2,o2,o1);
+//       MTet4 *t41 = new MTet4 (tr1,cr); 
+//       MTet4 *t42 = new MTet4 (tr2,cr);
+//       newTets.push_back(t41);
+//       newTets.push_back(t42);
+//       }
+//     break;
+//   case 2 :
+//     {
+//       int iE1 = edges[0];
+//       int iE2 = edges[1];
+
+//       MVertex *newv1 = pts[0];
+//       MVertex *newv2 = pts[1];
+
+//       MVertex *v11 = t1->tet()->getVertex(e[iE1][0]);
+//       MVertex *v12 = t1->tet()->getVertex(e[iE1][1]);
+//       MVertex *v21 = t1->tet()->getVertex(e[iE2][0]);
+//       MVertex *v22 = t1->tet()->getVertex(e[iE2][1]);
+
+//       if (iE1 == 5-iE2){ // two opposite edges
+// 	MTetrahedron *tr1 = new MTetrahedron ( newv1,newv2,v11,v21);
+// 	MTetrahedron *tr2 = new MTetrahedron ( newv1,newv2,v21,v12);
+// 	MTetrahedron *tr3 = new MTetrahedron ( newv1,newv2,v12,v22);
+// 	MTetrahedron *tr4 = new MTetrahedron ( newv1,newv2,v22,v11);
+// 	MTet4 *t41 = new MTet4 (tr1,cr); 
+// 	MTet4 *t42 = new MTet4 (tr2,cr);
+// 	MTet4 *t43 = new MTet4 (tr3,cr); 
+// 	MTet4 *t44 = new MTet4 (tr4,cr);
+// 	newTets.push_back(t41);
+// 	newTets.push_back(t42);
+// 	newTets.push_back(t43);
+// 	newTets.push_back(t44);
+//       }
+//       else{ //two adjacend edges
+// 	MVertex *vsame,*other=0;
+// 	if      (v11 == v21){vsame=v11; v11=v12; v21=v22;}
+// 	else if (v11 == v22){vsame=v11; v11=v12}
+// 	else if (v12 == v21){vsame=v12; v21=v22}
+// 	else if (v12 == v22){vsame=v12;}
+// 	else throw;
+// 	for (int i=0;i<4;i++){
+// 	  if (vsame != t1->tet()->getVertex(i) && 
+// 	      v11 != t1->tet()->getVertex(i) && 
+// 	      v21 != t1->tet()->getVertex(i)){
+// 	    other = t1->tet->getVertex(i);
+// 	    break;
+// 	  }
+// 	}
+// 	if (!other)throw;
+// 	MTetrahedron *tr1 = new MTetrahedron ( newv1,newv2,vsame,other);
+// 	splitQuadFace (newTets,steinerPoints,newv1,newv2,v21,v11,other,cr,search,fakeFace);
+//       }
+//     }
+//     break;    
+//   case 3 :
+//     {
+//       int iE1 = edges[0];
+//       int iE2 = edges[1];
+//       int iE3 = edges[2];
+//       MVertex *newv1;
+//       MVertex *newv2;
+//       MVertex *newv3;
+//       std::sort(edges,edges+3);
+//       if (iE1 == edges[0])newv1=pts[0];
+//       else if (iE2 == edges[0])newv1=pts[1];
+//       else if (iE3 == edges[0])newv1=pts[2];
+//       if (iE1 == edges[1])newv2=pts[0];
+//       else if (iE2 == edges[1])newv2=pts[1];
+//       else if (iE3 == edges[1])newv2=pts[2];
+//       if (iE1 == edges[2])newv3=pts[0];
+//       else if (iE2 == edges[2])newv3=pts[1];
+//       else if (iE3 == edges[2])newv3=pts[2];
+//       iE1 = edges[0];
+//       iE2 = edges[1];
+//       iE3 = edges[2];
+//       // edges are sorted and points correspond
+
+//       mVertex *v[4];
+//       // the 3 edges coincide at one vertex
+//       int config;
+//       if (iE1 == 0 && iE2 == 1 && iE3 == 2){
+// 	config = 1; 
+// 	v[0] = t1->tet()->getVertex(0);
+// 	v[1] = t1->tet()->getVertex(1);
+// 	v[2] = t1->tet()->getVertex(2);
+// 	v[3] = t1->tet()->getVertex(3);
+//       }      
+//       else if (iE1 == 0 && iE2 == 3 && iE3 == 4){
+// 	config = 1; 
+// 	v[0] = t1->tet()->getVertex(1);
+// 	v[1] = t1->tet()->getVertex(0);
+// 	v[2] = t1->tet()->getVertex(2);
+// 	v[3] = t1->tet()->getVertex(3);
+//       }      
+//       else if (iE1 == 1 && iE2 == 3 && iE3 == 5){
+// 	config = 1; 
+// 	v[0] = t1->tet()->getVertex(2);
+// 	v[1] = t1->tet()->getVertex(0);
+// 	v[2] = t1->tet()->getVertex(1);
+// 	v[3] = t1->tet()->getVertex(3);
+//       }      
+//       else if (iE1 == 2 && iE2 == 4 && iE3 == 5){
+// 	config = 1; 
+// 	v[0] = t1->tet()->getVertex(3);
+// 	v[1] = t1->tet()->getVertex(0);
+// 	v[2] = t1->tet()->getVertex(1);
+// 	v[3] = t1->tet()->getVertex(2);
+//       }
+//       // three edges of the same face are cut
+//       else if (iE1 == 0 && iE2 == 1 && iE3 == 3){
+// 	config = 2;
+// 	v[0] = t1->tet()->getVertex(3);
+// 	v[1] = t1->tet()->getVertex(1);
+// 	v[2] = t1->tet()->getVertex(0);
+// 	v[3] = t1->tet()->getVertex(2);      
+//       }
+//       else if (iE1 == 0 && iE2 == 2 && iE3 == 4){
+// 	config = 2;
+// 	v[0] = t1->tet()->getVertex(2);
+// 	v[1] = t1->tet()->getVertex(1);
+// 	v[2] = t1->tet()->getVertex(0);
+// 	v[3] = t1->tet()->getVertex(3);      
+//       }
+//       else if (iE1 == 1 && iE2 == 2 && iE3 == 5){
+// 	config = 2;
+// 	v[0] = t1->tet()->getVertex(1);
+// 	v[1] = t1->tet()->getVertex(2);
+// 	v[2] = t1->tet()->getVertex(0);
+// 	v[3] = t1->tet()->getVertex(3);      
+//       }
+//       else if (iE1 == 3 && iE2 == 4 && iE3 == 5){
+// 	config = 2;
+// 	v[0] = t1->tet()->getVertex(0);
+// 	v[1] = t1->tet()->getVertex(2);
+// 	v[2] = t1->tet()->getVertex(1);
+// 	v[3] = t1->tet()->getVertex(3);      
+//       }
+//       // the three edges for a kind of Z
+//       else if (iE1 == 0 && iE2 == 3 && iE3 == 5){
+// 	config = 3;
+//       }
+      
+//       if (config == 1){
+//       }
+//       else if (config == 2){
+//       }
+//       else{
+// 	throw; // for the moment.
+//       }
+//     }
+//   default :
+//     throw;
+//   }
+//   t1->setDeleted(true);
+    
+
+// }
+  
+
+
+// collapse
diff --git a/Mesh/meshGRegionLocalMeshMod.h b/Mesh/meshGRegionLocalMeshMod.h
index 7a4ef0dfc1369341c92a0083879928ca2f8085b9..1342d6038132bc052b0b920a7accd6e0e962c02f 100644
--- a/Mesh/meshGRegionLocalMeshMod.h
+++ b/Mesh/meshGRegionLocalMeshMod.h
@@ -22,19 +22,36 @@
 
 #include "meshGRegionDelaunayInsertion.h"
 #include "qualityMeasures.h"
-
+// local mesh modification operators. Those
+// operators only apply to the "bulk" of the
+// mesh and cannot be applied to boudnaries.
+// I'm working on it
 bool gmshEdgeSwap (std::vector<MTet4 *> &newTets,
 		   MTet4 *tet, 
 		   int iLocalEdge,
 		   const gmshQualityMeasure4Tet &cr);
+
 bool gmshFaceSwap (std::vector<MTet4 *> &newTets,
 		   MTet4 *tet, 
 		   int iLocalFace,
 		   const gmshQualityMeasure4Tet &cr);
+
 bool gmshSmoothVertex ( MTet4 *t, 
 			int iLocalVertex,
 			const gmshQualityMeasure4Tet &cr);
-  
+
+bool gmshCollapseVertex ( std::vector<MTet4 *> &newTets,
+			  MTet4 *t, 
+			  int iVertex,
+			  int iTarget,
+			  const gmshQualityMeasure4Tet &cr);
+
+bool gmshEdgeSplit (std::vector<MTet4 *> &newTets,
+		    MTet4 *tet,
+		    MVertex *newVertex,
+		    int iLocalEdge,
+		    const gmshQualityMeasure4Tet &cr);
+
 #endif