def optimize_in_single_dimension(
        pvalues: np.ndarray,
        a_max: float,
        image_to_node: bool,
        score_function: Callable[[np.ndarray, np.ndarray, np.ndarray], np.ndarray],
    ) -> tuple[float, np.ndarray, float]:
        """
        Optimizes over all subsets of nodes for a given subset of images or over all subsets of images for a given
        subset of nodes.

        :param pvalues: pvalue ranges.
        :param a_max: Determines the significance level threshold.
        :param image_to_node: Informs the direction for optimization.
        :param score_function: Scoring function.
        :return: (best_score_so_far, subset, best_alpha).
        """
        alpha_thresholds = np.unique(pvalues[:, :, 1])

        # alpha_thresholds = alpha_thresholds[0::5] #take every 5th for speed purposes
        # where does a_max fall in check
        last_alpha_index = int(np.searchsorted(alpha_thresholds, a_max))
        # resize check for only ones smaller than a_max
        alpha_thresholds = alpha_thresholds[0:last_alpha_index]

        step_for_50 = len(alpha_thresholds) / 50
        alpha_thresholds = alpha_thresholds[0 :: int(step_for_50) + 1]
        # add on the max value to check as well as it may not have been part of unique
        alpha_thresholds = np.append(alpha_thresholds, a_max)

        # alpha_thresholds = np.arange(a_max/50, a_max, a_max/50)

        if image_to_node:
            number_of_elements = pvalues.shape[1]  # searching over j columns
            size_of_given = pvalues.shape[0]  # for fixed this many images
            unsort_priority = np.zeros((pvalues.shape[1], alpha_thresholds.shape[0]))  # number of columns
        else:
            number_of_elements = pvalues.shape[0]  # searching over i rows
            size_of_given = pvalues.shape[1]  # for this many fixed nodes
            unsort_priority = np.zeros((pvalues.shape[0], alpha_thresholds.shape[0]))  # number of rows

        for elem_indx in range(0, number_of_elements):
            # sort all the range maxes
            if image_to_node:
                # collect ranges over images(rows)
                arg_sort_max = np.argsort(pvalues[:, elem_indx, 1])
                # arg_sort_min = np.argsort(pvalues[:,e,0]) #collect ranges over images(rows)
                completely_included = np.searchsorted(
                    pvalues[:, elem_indx, 1][arg_sort_max],
                    alpha_thresholds,
                    side="right",
                )
            else:
                # collect ranges over nodes(columns)
                arg_sort_max = np.argsort(pvalues[elem_indx, :, 1])
                # arg_sort_min = np.argsort(pvalues[elem_indx,:,0])

                completely_included = np.searchsorted(
                    pvalues[elem_indx, :, 1][arg_sort_max],
                    alpha_thresholds,
                    side="right",
                )

            # should be num elements by num thresh
            unsort_priority[elem_indx, :] = completely_included

        # want to sort for a fixed thresh (across?)
        arg_sort_priority = np.argsort(-unsort_priority, axis=0)

        best_score_so_far = -10000
        best_alpha = -2

        alpha_count = 0
        for alpha_threshold in alpha_thresholds:

            # score each threshold by itself, cumulating priority,
            # cumulating count, alpha stays same.
            alpha_v = np.ones(number_of_elements) * alpha_threshold

            n_alpha_v = np.cumsum(unsort_priority[:, alpha_count][arg_sort_priority][:, alpha_count])
            count_increments_this = np.ones(number_of_elements) * size_of_given
            n_v = np.cumsum(count_increments_this)

            vector_of_scores = score_function(n_alpha_v, n_v, alpha_v)

            best_score_for_this_alpha_idx = np.argmax(vector_of_scores)
            best_score_for_this_alpha = vector_of_scores[best_score_for_this_alpha_idx]

            if best_score_for_this_alpha > best_score_so_far:
                best_score_so_far = best_score_for_this_alpha
                best_size = best_score_for_this_alpha_idx + 1  # not sure 1 is needed?
                best_alpha = alpha_threshold
                best_alpha_count = alpha_count
            alpha_count = alpha_count + 1

        # after the alpha for loop we now have the best score, the best alpha, size of the best subset,
        # and alpha counter use these with the priority argsort to reconstruct the best subset
        unsort = arg_sort_priority[:, best_alpha_count]

        subset = np.zeros(best_size).astype(int)
        for loc in range(0, best_size):
            subset[loc] = unsort[loc]

        return best_score_so_far, subset, best_alpha

        Optimizes over all subsets of nodes for a given subset of images or over all subsets of images for a given
        subset of nodes.

        :param pvalues: pvalue ranges.
        :param a_max: Determines the significance level threshold.
        :param image_to_node: Informs the direction for optimization.
        :param score_function: Scoring function.
        :return: (best_score_so_far, subset, best_alpha).
        
art/defences/detector/evasion/subsetscanning/scanningops.py