'Performs the Synchronous ascending phase.
Returns an array of candidates, their distance ranks and
distances.'

def _get_candidates(self, query, max_depth, bin_queries, n_neighbors):

    index_size = self._fit_X.shape[0]
    n_candidates = 0
    candidate_set = set()
    min_candidates = (self.n_candidates * self.n_estimators)
    while ((max_depth > self.min_hash_match) and ((n_candidates < min_candidates) or (len(candidate_set) < n_neighbors))):
        left_mask = self._left_mask[max_depth]
        right_mask = self._right_mask[max_depth]
        for i in range(self.n_estimators):
            (start, stop) = _find_matching_indices(self.trees_[i], bin_queries[i], left_mask, right_mask)
            n_candidates += (stop - start)
            candidate_set.update(self.original_indices_[i][start:stop].tolist())
        max_depth -= 1
    candidates = np.fromiter(candidate_set, count=len(candidate_set), dtype=np.intp)
    if (candidates.shape[0] < n_neighbors):
        warnings.warn(('Number    of    candidates    is    not    sufficient    to    retrieve    %i    neighbors    with    min_hash_match    =    %i.    Candidates    are    filled    up    uniformly    from    unselected    indices.' % (n_neighbors, self.min_hash_match)))
        remaining = np.setdiff1d(np.arange(0, index_size), candidates)
        to_fill = (n_neighbors - candidates.shape[0])
        candidates = np.concatenate((candidates, remaining[:to_fill]))
    (ranks, distances) = self._compute_distances(query, candidates.astype(int))
    return (candidates[ranks[:n_neighbors]], distances[:n_neighbors])
