docs: def get_cluster_labels(hdf5_file, N_processes):
      """
      Returns
      -------
      cluster_centers_indices : array of shape (n_clusters,)
          Indices of cluster centers
          
      labels : array of shape (n_samples,)
          Specify the label of the cluster to which each point has been assigned.
      """
  
      with Worker.hdf5_lock:
          with tables.open_file(hdf5_file, 'r+') as fileh:
              A = fileh.root.aff_prop_group.availabilities
              R = fileh.root.aff_prop_group.responsibilities
      
              N = A.nrows
              diag_ind = np.diag_indices(N)
              
              a = A[diag_ind]
              r = R[diag_ind]
      
      I = np.where(np.add(a[:], r[:]) > 0)[0]
      K = I.size
      
      if K == 0:
          labels = np.empty((N, 1))
          labels.fill(np.nan)
          cluster_centers_indices = None
          
      else:
          c_array = multiprocessing.Array(c_int, N, lock = True)
      
          chunk_size = get_chunk_size(N, 3 * N_processes)
          numpy_args = c_array, N, np.int32
      
          with closing(multiprocessing.Pool(N_processes, 
                       initializer = cluster_labels_init, 
                       initargs = (hdf5_file, I, c_array.get_lock()) 
                                 + numpy_args)) as pool:
              pool.map_async(multiprocessing_cluster_labels_A, 
                    chunk_generator(N, 3 * N_processes), chunk_size)
          
          pool.close()
          pool.join()
          
          gc.collect()
          
          c = to_numpy_array(*numpy_args)
          c[I] = np.arange(K)
          # determine the exemplars of clusters, applying some 
          # cosmetics to our results before returning them
          for k in range(K):
              ii = np.where(c == k)[0]
              
              iix = np.full(N, False, dtype = bool)
              iix[ii] = True
              
              s_reduced_array = multiprocessing.Array(c_double, ii.size, 
                                                      lock = True)
          
              chunk_size = get_chunk_size(N, 3 * N_processes)
              numpy_args = s_reduced_array, ii.size, np.float64
              
              with closing(multiprocessing.Pool(N_processes,
                           initializer = cluster_labels_init_B,
                           initargs = (hdf5_file, I, ii, iix, 
                           s_reduced_array.get_lock())
                           + numpy_args)) as pool:
                  pool.map_async(multiprocessing_cluster_labels_B,
                        chunk_generator(N, 3 * N_processes), chunk_size)
                                 
              pool.close()
              pool.join()
              
              s_reduced = to_numpy_array(*numpy_args)
              
              j = np.argmax(s_reduced)
              I[k] = ii[j]
              
              gc.collect()
              
          c_array = multiprocessing.Array(c_int, N, lock = True)
          
          chunk_size = get_chunk_size(N, 3 * N_processes)
          numpy_args = c_array, N, np.int32
      
          with closing(multiprocessing.Pool(N_processes, 
                       initializer = cluster_labels_init, 
                       initargs = (hdf5_file, I, c_array.get_lock()) 
                                 + numpy_args)) as pool:
              pool.map_async(multiprocessing_cluster_labels_C, 
                    chunk_generator(N, 3 * N_processes), chunk_size)
          
          pool.close()
          pool.join()
          
          c = to_numpy_array(*numpy_args)
          c[I] = np.arange(K)
          labels = I[c]
          
          gc.collect()
  
          cluster_centers_indices = np.unique(labels)
          labels = np.searchsorted(cluster_centers_indices, labels)
          
      return cluster_centers_indices, labels