diff --git a/Addons/HMat/Src/Clustering/FConnexClustering.hpp b/Addons/HMat/Src/Clustering/FConnexClustering.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0810206c2a51d2d76c4b87b90984e9df52d1362c
--- /dev/null
+++ b/Addons/HMat/Src/Clustering/FConnexClustering.hpp
@@ -0,0 +1,112 @@
+#ifndef FCONNEXCLUSTERING_HPP
+#define FCONNEXCLUSTERING_HPP
+
+// @SCALFMM_PRIVATE
+
+#include "./Utils/FGlobal.hpp"
+#include "./Utils/FAssert.hpp"
+#include "./Utils/FMath.hpp"
+#include "./Containers/FBoolArray.hpp"
+
+#include "FClusterTree.hpp"
+
+#include <stack>
+#include <vector>
+#include <functional>
+#include <queue>
+#include <limits>
+#include <memory>
+
+
+
+template <class FReal>
+class FConnexClustering {
+protected:
+    const int dim;
+
+    int* permsNewToOrig;
+    int* permsOrigToNew;
+    int* partitions;
+    int* partitionsOffset;
+    int nbPartitions;
+
+public:
+    FConnexClustering(const int inDim, const FReal inDistMat[], const FReal thresh)
+        : dim(inDim),
+          permsNewToOrig(new int[dim]),
+          permsOrigToNew(new int[dim]),
+          partitions(new int[dim]),
+          partitionsOffset(new int[dim+1]),
+          nbPartitions (0){
+
+        std::unique_ptr<int[]> partitionsMappin(new int[dim]);
+
+        for(int idx = 0 ; idx < dim ; ++idx){
+            partitionsMappin[idx] = -1;
+        }
+
+        partitionsOffset[0] = 0;
+        partitions[0] = 0;
+
+        for(int idx = 0 ; idx < dim ; ++idx){
+            if(partitionsMappin[idx] == -1){
+                FAssertLF(nbPartitions < dim);
+
+                partitionsOffset[nbPartitions+1] = partitionsOffset[nbPartitions]+1;
+                partitionsMappin[idx] = nbPartitions;
+
+                int idxPartitionElement = partitionsOffset[nbPartitions];
+                permsNewToOrig[idxPartitionElement] = idx;
+
+                while(idxPartitionElement < partitionsOffset[nbPartitions+1]){
+                    FAssertLF(idxPartitionElement < dim);
+
+                    for(int idxOther = 0 ; idxOther < dim ; ++idxOther){
+                        if(partitionsMappin[idxOther] == -1
+                               && inDistMat[permsNewToOrig[idxPartitionElement]*dim + idxOther] < thresh){
+                            partitionsMappin[idxOther] = nbPartitions;
+                            permsNewToOrig[partitionsOffset[nbPartitions+1]] = idxOther;
+                            permsOrigToNew[idxOther] = partitionsOffset[nbPartitions+1];
+                            partitionsOffset[nbPartitions+1] += 1;
+                            FAssertLF(partitionsOffset[nbPartitions+1] <= dim);
+                        }
+                    }
+
+                    idxPartitionElement += 1;
+                }
+
+                partitions[nbPartitions] = partitionsOffset[nbPartitions+1]-partitionsOffset[nbPartitions];
+                nbPartitions += 1;
+            }
+        }
+
+        FAssertLF(partitionsOffset[nbPartitions] == dim);
+    }
+
+    ~FConnexClustering(){
+        delete[] permsNewToOrig;
+        delete[] permsOrigToNew;
+        delete[] partitionsOffset;
+        delete[] partitions;
+    }
+
+
+    void fillPermutations(int* inPermuts, int* invPermuts = nullptr) const {
+        memcpy(inPermuts, permsOrigToNew, sizeof(int)*dim);
+        if(invPermuts){
+            memcpy(invPermuts, permsNewToOrig, sizeof(int)*dim);
+        }
+    }
+
+    int getNbPartitions() const{
+        return nbPartitions;
+    }
+
+    void getPartitions(const int inNbPartitions, int inNbIdxInPartitions[]) const{
+        FAssertLF(nbPartitions  == inNbPartitions);
+        memcpy(inNbIdxInPartitions, partitions, sizeof(int)*nbPartitions);
+    }
+};
+
+#endif // FCONNEXCLUSTERING_HPP
+
diff --git a/Addons/HMat/Tests/testConnectClustering.cpp b/Addons/HMat/Tests/testConnectClustering.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c888cf3a882534fdaf4022bf6da3ec4fd022d050
--- /dev/null
+++ b/Addons/HMat/Tests/testConnectClustering.cpp
@@ -0,0 +1,158 @@
+
+// @SCALFMM_PRIVATE
+
+#include "../Src/Clustering/FConnexClustering.hpp"
+#include "../Src/Utils/FMatrixIO.hpp"
+
+#include "../Src/Containers/FPartitionsMapping.hpp"
+#include "../Src/Utils/FSvgRect.hpp"
+#include "../Src/Viewers/FDenseBlockWrapper.hpp"
+#include "../Src/Blocks/FDenseBlock.hpp"
+
+#include "Utils/FParameters.hpp"
+#include "Utils/FParameterNames.hpp"
+
+#include "Files/FGenerateDistribution.hpp"
+
+#include <memory>
+
+// FUSE_SCOTCH
+
+template <class FReal, class PartitionerClass>
+void TestPartitions(const PartitionerClass& partitioner,
+                    const char outputdir[], const int idFile,
+                    const int dim,
+                    const FReal coords[], const char config[]){
+
+    std::unique_ptr<int[]> permutations(new int[dim]);
+    std::unique_ptr<int[]> invpermutations(new int[dim]);
+    partitioner.fillPermutations(permutations.get(), invpermutations.get());
+
+    const int nbPartitions = partitioner.getNbPartitions();
+    std::unique_ptr<int[]> partitions(new int[nbPartitions]);
+    partitioner.getPartitions(nbPartitions, partitions.get());
+
+    {
+        char coordfilename[1024];
+        sprintf(coordfilename, "%s/%s-coord-%d.csv", outputdir, config, idFile);
+        std::cout << "\t save coord to " << coordfilename << "\n";
+        FILE* fcoord = fopen(coordfilename, "w");
+
+        int offsetParticles = 0;
+        for(int idxPartition = 0 ; idxPartition < nbPartitions ; ++idxPartition){
+            for(int idxPart = 0 ; idxPart < partitions[idxPartition] ; ++idxPart){
+                const int idxUnk = invpermutations[idxPart+offsetParticles];
+                fprintf(fcoord, "%e,%e,%e,%d\n",
+                        coords[idxUnk*4 + 0],
+                        coords[idxUnk*4 + 1],
+                        coords[idxUnk*4 + 2],
+                        idxPartition);
+            }
+            offsetParticles += partitions[idxPartition];
+        }
+        FAssertLF(offsetParticles == dim);
+
+        fclose(fcoord);
+    }
+    {
+        typedef FDenseBlock<FReal> CellClass;
+        typedef FPartitionsMapping<FReal, CellClass> GridClass;
+
+        GridClass bissection(dim, partitions.get(), nbPartitions);
+
+        char svgfilename[1024];
+        sprintf(svgfilename, "%s/%s-%d.svg", outputdir, config, idFile);
+        std::cout << "\t save svg to " << svgfilename << "\n";
+        FSvgRect output(svgfilename, dim);
+
+        bissection.forAllBlocksDescriptor([&](const FBlockDescriptor& info){
+            output.addRectWithLegend(info.col, info.row, info.nbCols, info.nbRows, info.level);
+        });
+    }
+}
+
+int main(int argc, char** argv){
+    static const FParameterNames SvgOutParam = {
+        {"-fout", "--out", "-out"} ,
+         "Svg output directory."
+    };
+    static const FParameterNames DimParam = {
+        {"-N", "-nb", "-dim"} ,
+         "Dim of the matrix."
+    };
+
+    FHelpDescribeAndExit(argc, argv,"Test the bisection.",SvgOutParam,DimParam,FParameterDefinitions::OctreeHeight,
+                         FParameterDefinitions::NbParticles);
+
+    const int nbParticles = (FParameters::getValue(argc, argv, FParameterDefinitions::NbParticles.options, 3000) & ~1);
+    const char* outputdir = FParameters::getStr(argc, argv, SvgOutParam.options, "/tmp/");
+
+    typedef double FReal;
+
+    /////////////////////////////////////////////////////////////////
+
+    std::cout << "Create spheres\n";
+
+    const FReal radius = 0.5;
+    const FReal distanceBetweenSpheres = 0.4;
+    const int nbPointsSphere = nbParticles/2;
+
+    std::cout << "\tradius = " << radius << "\n";
+    std::cout << "\tdistanceBetweenSpheres = " << distanceBetweenSpheres << "\n";
+    std::cout << "\tnbPointsSphere = " << nbPointsSphere << "\n";
+
+    std::unique_ptr<FReal[]> spherePoints(new FReal[nbParticles*4]);
+    unifRandonPointsOnSphere(nbPointsSphere, radius, spherePoints.get()) ;
+
+    for(int idxPoint = 0 ; idxPoint < nbPointsSphere ; ++idxPoint){
+        spherePoints[(idxPoint+nbPointsSphere)*4 + 0] = spherePoints[idxPoint*4 + 0] + radius*2 + distanceBetweenSpheres;
+        spherePoints[(idxPoint+nbPointsSphere)*4 + 1] = spherePoints[idxPoint*4 + 1] + radius*2 + distanceBetweenSpheres;
+        spherePoints[(idxPoint+nbPointsSphere)*4 + 2] = spherePoints[idxPoint*4 + 2] + radius*2 + distanceBetweenSpheres;
+    }
+
+    /////////////////////////////////////////////////////////////////
+
+    std::cout << "Compute distance\n";
+
+    const int dim = nbParticles;
+    std::unique_ptr<FReal[]> distances(new FReal[nbParticles*nbParticles]);
+    for(int idxCol = 0 ; idxCol < nbParticles ; ++idxCol){
+        for(int idxRow = 0 ; idxRow < nbParticles ; ++idxRow){
+            const FReal diffx = spherePoints[idxCol*4 + 0] - spherePoints[idxRow*4 + 0];
+            const FReal diffy = spherePoints[idxCol*4 + 1] - spherePoints[idxRow*4 + 1];
+            const FReal diffz = spherePoints[idxCol*4 + 2] - spherePoints[idxRow*4 + 2];
+            distances[idxCol*nbParticles+idxRow] = FMath::Sqrt((diffx*diffx) + (diffy*diffy) + (diffz*diffz));
+        }
+    }
+
+    /////////////////////////////////////////////////////////////////
+
+    FReal distMin = std::numeric_limits<FReal>::max();
+    FReal distMax = std::numeric_limits<FReal>::min();
+
+    for(int idxRow = 0 ; idxRow < dim ; ++idxRow){
+        for(int idxCol = idxRow ; idxCol < dim ; ++idxCol){
+            distMin = FMath::Min(distMin, distances[idxCol*nbParticles+idxRow]);
+            distMax = FMath::Max(distMax, distances[idxCol*nbParticles+idxRow]);
+        }
+    }
+    std::cout << "Dist min " << distMin << "\n";
+    std::cout << "Dist max " << distMax << "\n";
+
+    /////////////////////////////////////////////////////////////////
+    const FReal stepThresh = (distMax-distMin)/10;
+    for(FReal thresh = distMin ; thresh <= distMax ; thresh += stepThresh){
+        std::cout << "Test FConnexClustering for thresh " << thresh << "\n";
+        FConnexClustering<FReal> partitioner(dim, distances.get(), thresh);
+        std::cout << "\tGot " << partitioner.getNbPartitions() << " partitions\n";
+        TestPartitions<FReal, FConnexClustering<FReal>>(partitioner,outputdir,
+                                                        int((thresh-distMin)/stepThresh),
+                                                        dim, spherePoints.get(), "FConnexClustering");
+    }
+
+    return 0;
+}
+
+
+
+