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.

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