public static Task<CategClusterSet> KMeansAsync(OrdModelView model, int nClusters, int nbIterations, CancellationToken cancellationToken,
     IProgress<Tuple<int, CategClusterSet>> progress)
 {
     return Task.Run<CategClusterSet>(() =>
     {
         CategClusterSet oBest = new CategClusterSet(model);
         oBest.initializeCiusters(nClusters);
         for (int i = 0; i < nbIterations; ++i)
         {
             CategClusterSet pp = new CategClusterSet();
             pp.m_model = oBest.m_model;
             pp.m_nvars = oBest.m_nvars;
             pp.m_indivs = oBest.m_indivs;
             List<Cluster> oList = new List<Cluster>();
             foreach (var c in oBest.Clusters)
             {
                 Cluster cc = new Cluster();
                 cc.Index = c.Index;
                 cc.Name = c.Name;
                 cc.Center = (double[])c.Center.Clone();
                 oList.Add(cc);
             }// c
             pp.Clusters = oList;
             int f = (int)(((double)i / (double)nbIterations) * 100.0 + 0.5);
             if (cancellationToken.IsCancellationRequested)
             {
                 break;
             }
             pp.kmeansstep(cancellationToken);
             if (cancellationToken.IsCancellationRequested)
             {
                 break;
             }
             if (oBest.Equals(pp))
             {
                 break;
             }
             oBest = pp;
             if (progress != null)
             {
                 progress.Report(new Tuple<int, CategClusterSet>(f, pp));
             }
         }// i
         return oBest;
     }, cancellationToken);
 }