public static CategClusterSet ProcessAsync(OrdModelView model, int nClusters, CancellationToken cancellationToken)
 {
     if ((model == null) || (nClusters < 2))
     {
         return null;
     }
     if (cancellationToken.IsCancellationRequested)
     {
         return null;
     }
     var rd = new Random();
     CategClusterSet oRet = new CategClusterSet(model);
     Cluster[] cc = new Cluster[nClusters];
     IndivData[] inds = oRet.Indivs.ToArray();
     int nMax = inds.Length;
     if (nMax < nClusters)
     {
         return null;
     }
     HashSet<int> oCur = new HashSet<int>();
     for (int i = 0; i < nClusters; ++i)
     {
         int nx = rd.Next(0, nMax);
         while (nx >= nMax)
         {
             nx = rd.Next(nMax);
         }
         IndivData indiv = inds[nx];
         int index = indiv.IndivIndex;
         while (oCur.Contains(index))
         {
             nx = rd.Next(0, nMax);
             while (nx >= nMax)
             {
                 nx = rd.Next(0, nMax);
             }
             indiv = inds[nx];
             index = indiv.IndivIndex;
         }
         oCur.Add(index);
         var xc = new Cluster(indiv, ClassificationType.Utility);
         xc.Name = String.Format("CU{0}", i + 1);
         xc.Index = i;
         cc[i] = xc;
     }// i
     oRet.Clusters = cc.ToList();
     double xx = oRet.compute_global_crit(cancellationToken);
     oRet.GlobalCrit = xx;
     if (cancellationToken.IsCancellationRequested)
     {
         return null;
     }
     foreach (var ind in inds)
     {
         if (cancellationToken.IsCancellationRequested)
         {
             return null;
         }
         if (!oCur.Contains(ind.IndivIndex))
         {
             if (!oRet.processOne(ind, cancellationToken))
             {
                 return null;
             }
         }
     }// inds
     return oRet;
 }